← All tutorials

معماری Clean Architecture به زبان ساده

منطق اصلی برنامه از دیتابیس، فریم‌ورک و ابزارهای جانبی جدا می‌ماند.

نمودار معماری کلین: درخواست از کاربر وارد API می‌شود، از Infrastructure و Application می‌گذرد و به Domain در مرکز می‌رسد. کارت‌های کناری محتوای هر لایه را نشان می‌دهند.
Domain در مرکز است. لایه‌های بیرونی درخواست، ابزارهای فنی و سناریوها را بر عهده دارند.

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
Domain = منطق و قوانین اصلی سیستم

۲. لایه Application

لایه Application مشخص می‌کند سیستم چه کارهایی می‌تواند انجام دهد. مثلاً:

Create
Update
Delete
Search
Approve
Reject
Login
Register

معمولاً Use Caseها، Handlerها، DTOها و Interfaceها در این لایه قرار می‌گیرند. مثلاً:

  • CreateUserHandler
  • UpdateProfileHandler
  • LoginHandler
  • IUserRepository
  • IEmailService

Application مشخص می‌کند:

چه کاری باید انجام شود؟

اما معمولاً وارد جزئیات فنی نمی‌شود. مثلاً Application می‌گوید:

await _repository.AddAsync(entity);

ولی نمی‌داند این اطلاعات قرار است در SQL Server ذخیره شوند یا PostgreSQL.

Application = سناریوها و عملیات سیستم

۳. لایه 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 می‌گوید:

من با EF Core و SQL Server این کار را انجام می‌دهم.
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 نباید منطق اصلی برنامه را داخل خودش پیاده‌سازی کند. وظیفه‌اش بیشتر این است:

Request را بگیرد
↓
Application را صدا بزند
↓
Response برگرداند
API = ورودی و خروجی سیستم

ارتباط لایه‌ها

یکی از مهم‌ترین قوانین Clean Architecture این است که وابستگی‌ها باید به سمت داخل باشند.

به صورت ساده:

API
↓
Application
↓
Domain

Infrastructure هم معمولاً Interfaceهایی را که Application تعریف کرده پیاده‌سازی می‌کند:

Infrastructure
↓
Application
↓
Domain

در نتیجه معمولاً این وابستگی‌ها را داریم:

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>();
هر وقت Application به IUserRepository نیاز داشت، UserRepository موجود در Infrastructure را به آن بده.
Application از قابلیت Infrastructure استفاده می‌کند، اما مستقیماً به آن وابسته نیست.

مسیر اجرای یک Request

مثلاً یک Request وارد سیستم می‌شود:

Client
↓
API
↓
Application
↓
Domain

اگر نیاز به دیتابیس یا سرویس خارجی باشد:

Client
↓
API
↓
Application
↓
Interface
↓
Infrastructure
↓
Database / Redis / Message Broker / External API
مسیر اجرای برنامه و مسیر وابستگی پروژه‌ها دقیقاً یک چیز نیستند. ممکن است در زمان اجرا Application از Repository استفاده کند، اما Application فقط Interface آن Repository را می‌شناسد.

چرا این معماری مفید است؟

بزرگ‌ترین مزیت Clean Architecture جدا کردن Business Logic از تکنولوژی است.

مثلاً اگر امروز از SQL Server استفاده می‌کنیم و فردا بخواهیم به PostgreSQL مهاجرت کنیم، بخش اصلی منطق برنامه نباید تغییر زیادی کند.

همین موضوع درباره ابزارهای دیگر هم صدق می‌کند:

امروزفردا
SQL ServerPostgreSQL
RabbitMQKafka
Redisابزار Cache دیگر
MinIOStorage دیگر

مزایای مهم 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 قدم بعدی است.