معماری Clean Architecture به زبان ساده
منطق اصلی برنامه از دیتابیس، فریمورک و ابزارهای جانبی جدا میماند.
Clean Architecture چیست؟
Clean Architecture یک روش برای ساخت نرمافزار است که هدف اصلی آن این است که منطق اصلی برنامه از تکنولوژیها و جزئیات فنی جدا بماند.
یعنی برنامه طوری طراحی شود که اگر در آینده دیتابیس، فریمورک یا ابزارهای جانبی تغییر کردند، مجبور نباشیم کل پروژه را تغییر دهیم.
این ایده را Robert C. Martin (Uncle Bob) مطرح کرد. تصویر کلاسیک آن دایرههای تو در تو است؛ در عمل، مخصوصاً در پروژههای .NET، همان ایده را معمولاً با چهار پروژه پیاده میکنیم:
چهار بخش
در سادهترین حالت، Clean Architecture را میتوان به چهار بخش تقسیم کرد:
Domain
قوانین و مفاهیم اصلی کسبوکار. از دیتابیس و فریمورک خبر ندارد.
Application
کارهایی که سیستم میتواند انجام دهد: ساخت، ویرایش، جستجو، تأیید، ورود.
Infrastructure
جزئیات فنی: EF Core، Redis، RabbitMQ، ایمیل، APIهای بیرونی.
API / Presentation
نقطه ورود و خروج: Controller، Endpoint، درخواست و پاسخ.
۱. لایه Domain
Domain قلب برنامه است.
چیزهایی که مستقیماً مربوط به قوانین و مفاهیم اصلی کسبوکار هستند در این لایه قرار میگیرند. مثلاً:
- Entities
- Value Objects
- Enums
- Domain Events
- Business Rules
- Domain Exceptions
نکته مهم این است که Domain نباید بداند برنامه از چه دیتابیسی، فریمورکی یا تکنولوژیای استفاده میکند.
یعنی Domain نباید وابسته به مواردی مثل اینها باشد:
EF Core
SQL Server
Redis
RabbitMQ
ASP.NET Core
HTTP
۲. لایه Application
لایه Application مشخص میکند سیستم چه کارهایی میتواند انجام دهد. مثلاً:
Create
Update
Delete
Search
Approve
Reject
Login
Register
معمولاً Use Caseها، Handlerها، DTOها و Interfaceها در این لایه قرار میگیرند. مثلاً:
CreateUserHandlerUpdateProfileHandlerLoginHandlerIUserRepositoryIEmailService
Application مشخص میکند:
اما معمولاً وارد جزئیات فنی نمیشود. مثلاً Application میگوید:
await _repository.AddAsync(entity);
ولی نمیداند این اطلاعات قرار است در SQL Server ذخیره شوند یا PostgreSQL.
۳. لایه Infrastructure
Infrastructure محل قرار گرفتن جزئیات فنی سیستم است. مثلاً:
- EF Core و
DbContext - SQL Server و PostgreSQL
- Redis
- RabbitMQ و Kafka
- MinIO
- Email Service و SMS Service
- External APIs
Application ممکن است فقط یک Interface داشته باشد:
public interface IUserRepository
{
Task AddAsync(User user);
}
اما پیادهسازی واقعی آن داخل Infrastructure قرار میگیرد:
public class UserRepository : IUserRepository
{
private readonly ApplicationDbContext _context;
public async Task AddAsync(User user)
{
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
}
}
یعنی Application میگوید:
Infrastructure میگوید:
۴. لایه API / Presentation
این لایه نقطه ورود و خروج برنامه است.
در یک پروژه ASP.NET Core معمولاً شامل مواردی مثل این است:
- Controllers
- Endpoints
- Middlewares
- Request و Response
- Authentication
مثلاً Controller درخواست را میگیرد:
[HttpPost]
public async Task<IActionResult> Create(CreateUserCommand command)
{
var result = await _handler.Handle(command);
return Ok(result);
}
Controller نباید منطق اصلی برنامه را داخل خودش پیادهسازی کند. وظیفهاش بیشتر این است:
ارتباط لایهها
یکی از مهمترین قوانین Clean Architecture این است که وابستگیها باید به سمت داخل باشند.
به صورت ساده:
Infrastructure هم معمولاً Interfaceهایی را که Application تعریف کرده پیادهسازی میکند:
در نتیجه معمولاً این وابستگیها را داریم:
API ----> Application
Infrastructure --> Application
Application ----> Domain
Domain ----> Infrastructure
Domain ----> API
Application ----> Infrastructure
Application ----> API
پس Application چطور از Infrastructure استفاده میکند؟
این قسمت ممکن است در ابتدا کمی گیجکننده باشد.
Application ممکن است چنین چیزی داشته باشد:
public interface IUserRepository
{
Task AddAsync(User user);
}
Application فقط همین Interface را میشناسد.
بعد Infrastructure آن را پیادهسازی میکند:
public class UserRepository : IUserRepository
{
// EF Core
}
و در Dependency Injection مشخص میکنیم:
builder.Services.AddScoped<IUserRepository, UserRepository>();
IUserRepository نیاز داشت، UserRepository موجود در Infrastructure را به آن بده.
مسیر اجرای یک Request
مثلاً یک Request وارد سیستم میشود:
اگر نیاز به دیتابیس یا سرویس خارجی باشد:
چرا این معماری مفید است؟
بزرگترین مزیت Clean Architecture جدا کردن Business Logic از تکنولوژی است.
مثلاً اگر امروز از SQL Server استفاده میکنیم و فردا بخواهیم به PostgreSQL مهاجرت کنیم، بخش اصلی منطق برنامه نباید تغییر زیادی کند.
همین موضوع درباره ابزارهای دیگر هم صدق میکند:
| امروز | فردا |
|---|---|
| SQL Server | PostgreSQL |
| RabbitMQ | Kafka |
| Redis | ابزار Cache دیگر |
| MinIO | Storage دیگر |
مزایای مهم Clean Architecture:
- کاهش وابستگی بین قسمتهای پروژه
- تستپذیری بهتر
- تغییر راحتتر تکنولوژیها
- خوانایی بیشتر پروژه
- نگهداری سادهتر پروژههای بزرگ
- جدا شدن Business Logic از جزئیات فنی
- رعایت بهتر اصل Dependency Inversion
ساختار معمول در .NET
یک پروژه ساده با Clean Architecture معمولاً میتواند این ساختار را داشته باشد:
MyProject.Domain
MyProject.Application
MyProject.Infrastructure
MyProject.Api
| پروژه | مسئولیت |
|---|---|
| Domain | قوانین و مفاهیم اصلی |
| Application | کارهایی که سیستم انجام میدهد |
| Infrastructure | نحوه انجام کارهای فنی |
| API | ورودی و خروجی سیستم |
جمعبندی
اگر بخواهیم کل Clean Architecture را در یک جمله خلاصه کنیم:
منطق اصلی برنامه نباید به دیتابیس، فریمورک یا ابزارهای فنی وابسته باشد؛ این ابزارها باید به منطق برنامه وابسته باشند.
| لایه | به خاطر بسپار |
|---|---|
| Domain | منطق |
| Application | سناریو |
| Infrastructure | تکنولوژی |
| API | ورودی و خروجی |
اگر میخواهید همین لایهها را داخل یک سرویس واقعی Order ببینید، آموزش DDD و CQRS با پروژه Order قدم بعدی است.