معماری Clean (Clean Architecture)
معماری Clean یک روش ساختاردهی نرمافزار است که توسط رابرت سی. مارتین (Robert C. Martin) معروف به “عمو باب”) ارائه شد. این معماری بر اساس اصول SOLID و با هدف ایجاد سیستمهای نرمافزاری قابل نگهداری، قابل تست و مستقل از جزئیات پیادهسازی طراحی شده است.
اصول کلیدی معماری Clean
- استقلال از فریمورکها: سیستم به فریمورکهای خاص وابسته نیست
- قابل تست بودن: کسبوکار منطق باید بدون UI، دیتابیس یا هر عنصر خارجی دیگر قابل تست باشد
- استقلال از UI: رابط کاربری میتواند تغییر کند بدون تأثیر بر لایههای دیگر
- استقلال از دیتابیس: میتوان دیتابیس را تغییر داد بدون تأثیر بر لایههای کسبوکار
- استقلال از عوامل خارجی: منطق کسبوکار از دنیای خارج (APIها، سرویسهای خارجی) بیخبر است
لایههای معماری Clean
1. لایه Entities (Enterprise Business Rules)
- شامل مدلهای اصلی کسبوکار و قوانین سازمان
- مستقل از هر چیز دیگر در سیستم
- مثال: کلاس Customer با خصوصیات و رفتارهای اصلی
2. لایه Use Cases (Application Business Rules)
- شامل منطق خاص برنامه و گردش کارها
- واسطه بین Entities و لایههای خارجی
- مثال: RegisterCustomerUseCase که عملیات ثبت مشتری را مدیریت میکند
3. لایه Interface Adapters
- تبدیل داده بین لایه Use Cases و دنیای خارج
- شامل:
- Controllers: مدیریت درخواستهای HTTP
- Presenters/ViewModels: آمادهسازی داده برای نمایش
- Gateways/Repositories: واسطه دسترسی به دادههای خارجی
4. لایه Frameworks & Drivers (External Agencies)
- شامل جزئیات پیادهسازی:
- UI (وب، موبایل، دسکتاپ)
- دیتابیس (SQL، NoSQL)
- فریمورکها (Spring, .NET, etc.)
- کتابخانههای خارجی
قانون وابستگی (Dependency Rule)
وابستگی فقط باید به سمت داخل باشد:
- لایههای بیرونی میتوانند به لایههای درونی وابسته باشند
- لایههای درونی هرگز نباید به لایههای بیرونی وابسته باشند
- این قانون با استفاده از وارونگی وابستگی (Dependency Inversion) از SOLID اعمال میشود
مزایای معماری Clean
- قابلیت نگهداری بالا: تغییرات آسانتر میشوند
- قابلیت تست پذیری: تست واحد آسانتر میشود
- استقلال از فناوری: امکان تغییر تکنولوژیها بدون تأثیر بر هسته سیستم
- قابلیت توسعه: افزودن ویژگیهای جدید سادهتر میشود
- جداسازی واضح مسئولیتها: هر لایه مسئولیت مشخصی دارد
معایب معماری Clean
- پیچیدگی اولیه: نیاز به طراحی دقیق دارد
- منحنی یادگیری: برای تیمهای تازه کار ممکن است چالشبرانگیز باشد
- بار اضافی: برای پروژههای کوچک ممکن است بیش از حد باشد
مثال:
// لایه Entities
public class Customer {
private String id;
private String name;
// رفتارهای کسبوکار اصلی
}
// لایه Use Cases
public interface RegisterCustomerUseCase {
Customer registerCustomer(String name);
}
// لایه Interface Adapters
@RestController
public class CustomerController {
private final RegisterCustomerUseCase useCase;
@PostMapping("/customers")
public ResponseEntity<Customer> register(@RequestBody String name) {
Customer customer = useCase.registerCustomer(name);
return ResponseEntity.ok(customer);
}
}
// لایه Frameworks & Drivers
@Repository
public class CustomerRepositoryImpl implements CustomerRepository {
// پیادهسازی دسترسی به دیتابیس
}
تفاوت با سایر معماریها
- MVC: معماری Clean جداسازی بهتری ارائه میدهد و لایه Model را به چند لایه تقسیم میکند
- Layered Architecture: معماری Clean وابستگیهای بهتری با قانون وابستگی مدیریت میکند
- Hexagonal: هر دو اهداف مشابهی دارند اما معماری Clean لایهبندی مشخصتری دارد
معماری Clean یک روش ساختاردهی نرمافزار است که با جداسازی قوی لایهها و مدیریت وابستگیها، سیستمهای انعطافپذیر و قابل نگهداری ایجاد میکند. این معماری به ویژه برای سیستمهای پیچیده و بلندمدت مناسب است.
لایههای معماری تمیز
در معماری Clean (معماری تمیز)، سیستم به چندین لایه مجزا تقسیم میشود که هر کدام مسئولیت مشخصی دارند. این لایهها به صورت دایرهای (مثل پیاز) قرار میگیرند و وابستگی فقط از لایههای بیرونی به سمت لایههای درونی مجاز است.
لایههای اصلی معماری Clean (از درونیترین به بیرونیترین):
1. لایه Entities (هسته مرکزی – Enterprise Business Rules)
- مسئولیت: مدلسازی دادهها و قوانین اصلی کسبوکار
- ویژگیها:
- مستقل از هر چیز خارجی (UI، دیتابیس، فریمورکها)
- شامل مدلهای پایه و قوانین اساسی سازمان
مثال:
public class User {
private String id;
private String name;
// قوانین کسبوکار (مثلاً اعتبارسنجی نام)
public boolean isValidName() {
return name != null && name.length() > 2;
}
}
2. لایه Use Cases (Application Business Rules)
- مسئولیت: پیادهسازی منطق خاص برنامه و سناریوهای کاربردی
- ویژگیها:
- گردش کارهای برنامه را مدیریت میکند (مثلاً ثبت کاربر، پرداخت)
- از Entities استفاده میکند اما از جزئیات بیرونی (مثل دیتابیس) بیخبر است
مثال
public class RegisterUserUseCase {
private final UserRepository repository;
public User execute(String name) {
User user = new User(name);
if (user.isValidName()) {
return repository.save(user);
}
throw new InvalidUserException();
}
}
3. لایه Interface Adapters (پل ارتباطی)
- مسئولیت: تبدیل داده بین لایه Use Cases و دنیای خارج (مثل UI، دیتابیس، APIها)
- زیرلایهها:
- Controllers: مدیریت درخواستهای HTTP (در برنامههای وب)
- Presenters/ViewModels: آمادهسازی داده برای نمایش در UI
- Gateways/Repositories: واسط برای دسترسی به دادههای خارجی (مثل دیتابیس)
مثال
@RestController
public class UserController {
private final RegisterUserUseCase useCase;
@PostMapping("/users")
public ResponseEntity<User> register(@RequestBody String name) {
User user = useCase.execute(name);
return ResponseEntity.ok(user);
}
}
4. لایه Frameworks & Drivers (بیرونیترین لایه)
- مسئولیت: پیادهسازی جزئیات فنی و ابزارهای خارجی
- اجزاء:
- UI: فرانتاند (React, Angular, Android, etc.)
- دیتابیس: MySQL, MongoDB, etc.
- فریمورکها: Spring, Hibernate, etc.
- کتابخانههای خارجی: مثلاً برای ارسال ایمیل
مثال
@Repository
public class UserRepositoryImpl implements UserRepository {
private final JpaRepository jpaRepo;
@Override
public User save(User user) {
// تبدیل User به Entity دیتابیس و ذخیره آن
return jpaRepo.save(user);
}
}
نمودار وابستگی لایهها:
Frameworks & Drivers (بیرون)
↑
Interface Adapters
↑
Use Cases
↑
Entities (داخل)
قانون طلایی وابستگی:
- وابستگی فقط به سمت داخل است:
- لایههای بیرونی میتوانند به لایههای درونی وابسته باشند.
- لایههای درونی هرگز نباید به لایههای بیرونی وابسته باشند.
- این قانون با Dependency Injection و اینترفیسها پیادهسازی میشود.
مثال ساده وابستگی:
// Use Case به Repository وابسته است، اما از طریق اینترفیس:
public class RegisterUserUseCase {
private final UserRepository repository; // نه UserRepositoryImpl!
}
این لایهبندی باعث میشود تغییر در UI یا دیتابیس، هسته اصلی برنامه (Entities و Use Cases) را تحت تأثیر قرار ندهد.
اگر لایهبندی پروژه را به شکل چهارلایه اصلی (Domain, Infrastructure, Application, Presentation) طراحی کنیم، ساختار و مسئولیتهای هر لایه به شرح زیر خواهد بود:
1. لایه Domain (هسته مرکزی)
مسئولیتها:
- تعریف موجودیتها (Entities)، Value Objects و قوانین کسبوکار خالص.
- شامل اینترفیسهای ریپازیتوری (مثلاً
IAccountRepository) برای جلوگیری از وابستگی به پیادهسازی. - مستقل کامل از فناوریهای بیرونی (UI، دیتابیس، فریمورکها).
اجزاء کلیدی:
- موجودیتهای اصلی مانند
Account,JournalEntry,Transaction. - Enumها مانند
AccountType(دارایی، بدهی، …). - استثناهای سفارشی (
DomainException).
مثال موجودیت Account:
public class Account : Entity<Guid> {
public string Code { get; private set; } // مثال: 101.01
public string Name { get; private set; }
public AccountType Type { get; private set; }
public AccountLevel Level { get; private set; } // کل، معین، تفصیلی
// قوانین کسبوکار:
public void UpdateName(string newName) {
if (string.IsNullOrEmpty(newName))
throw new DomainException("نام حساب نمیتواند خالی باشد.");
Name = newName;
}
}
2. لایه Infrastructure (پیادهسازی فنی)
مسئولیتها:
- پیادهسازی واسطهای تعریفشده در لایه
Domain(مثلIAccountRepository). - ارتباط با دیتابیس (EF Core, Dapper), فایلسیستم, سرویسهای خارجی (API, SMS, Email).
- مدیریت کش (Cache), لاگگیری, و احراز هویت.
اجزاء کلیدی:
- ریپازیتوریها (مثلاً
AccountRepository). - DbContext و Migrationها.
- سرویسهای инфраструктуры مانند
EmailService.
مثال ریپازیتوری:
public class AccountRepository : IAccountRepository {
private readonly AccountingDbContext _context;
public AccountRepository(AccountingDbContext context) {
_context = context;
}
public async Task AddAsync(Account account) {
await _context.Accounts.AddAsync(account);
await _context.SaveChangesAsync();
}
}
3. لایه Application (منطق کاربرد)
مسئولیتها:
- پیادهسازی Use Cases (مثل ثبت سند حسابداری، محاسبه ترازنامه).
- تعریف DTOها برای انتقال داده بین لایهها.
- اعتبارسنجی ورودیها (با FluentValidation یا DataAnnotations).
اجزاء کلیدی:
- Handlers (پردازش
CommandsوQueries). - Services (منطق چندوجهی مانند
TaxCalculator). - Mappers (تبدیل Entity به DTO).
مثال Use Case:
public class CreateAccountCommandHandler : IRequestHandler<CreateAccountCommand, Guid> {
private readonly IAccountRepository _repository;
public CreateAccountCommandHandler(IAccountRepository repository) {
_repository = repository;
}
public async Task<Guid> Handle(CreateAccountCommand command) {
var account = new Account(command.Code, command.Name, command.Type);
await _repository.AddAsync(account);
return account.Id;
}
}
4. لایه Presentation (رابط کاربری)
مسئولیتها:
- نمایش دادهها و دریافت ورودی از کاربر.
- وابسته به پلتفرم (مثلاً WinForms, ASP.NET Core, WPF).
اجزاء کلیدی:
- Controllers (در وب) یا ViewModels (در WPF).
- Razor Pages یا Blazor Components.
- API Endpoints (در حالت Web API).
مثال Controller در ASP.NET Core:
[ApiController]
[Route("api/accounts")]
public class AccountController : ControllerBase {
private readonly IMediator _mediator;
public AccountController(IMediator mediator) {
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateAccountCommand command) {
var accountId = await _mediator.Send(command);
return Ok(accountId);
}
}
نمودار وابستگیها:
Presentation (بیرونیترین لایه)
↑
Application
↑
Domain ← Infrastructure
قانون وابستگی:
- لایههای بالایی فقط به لایههای پایینی وابستهاند:
Presentation→Application→Domain.Infrastructure→Domain(پیادهسازی اینترفیسهای دامنه).
- هرگز برعکس (مثلاً
Domainنباید بهInfrastructureوابسته باشد).
مقایسه با معماری Clean کلاسیک:
| معماری چهارلایه | معماری Clean |
|---|---|
Presentation = Frameworks & Drivers | Presentation = UI |
Application = Use Cases | Application = Use Cases + Interface Adapters |
Infrastructure = Interface Adapters + Frameworks | Infrastructure = جدا شده به لایههای دقیقتر |
Domain = Entities | Domain = Entities + Enterprise Rules |
مزایای این ساختار:
- سادگی: تقسیمبندی واضح برای پروژههای متوسط.
- انعطافپذیری: امکان جایگزینی آسان
Infrastructure(مثلاً تغییر از EF Core به Dapper). - تستپذیری: تست
Applicationبدون نیاز بهPresentationیاInfrastructure.
معایب:
- عدم تفکیک دقیق بین
Interface AdaptersوFrameworks(مشکل در پروژههای بسیار بزرگ). Applicationممکن است بیش از حد سنگین شود (در صورت عدم تقسیم به ماژولهای کوچکتر).
جمعبندی:
این معماری برای اکثر پروژههای تجاری (خصوصاً با چارچوبهایی مانند ASP.NET Core یا WPF) مناسب است. اگر پروژه بسیار پیچیده باشد (مثل سیستمهای بانکی)، استفاده از معماری Clean با لایههای دقیقتر (Entities, Use Cases, Interface Adapters, Frameworks) توصیه میشود.
(Domain, Infrastructure, Application, Presentation) ارائه میشود. این نمودار شامل پوشههای داخلی، زیرلایهها و الگوهای معماری مرتبط است:
کلیدواژههای معماری و الگوها:
| لایه | الگوها/اصول | توضیح |
|---|---|---|
| Domain | DDD (Domain-Driven Design) | طراحی بر اساس مدلهای کسبوکار |
| Entity Pattern | موجودیتها با شناسه یکتا | |
| Value Object Pattern | اشیاء بدون شناسه (مثل Money) | |
| Infrastructure | Repository Pattern | جداسازی دسترسی به داده |
| Unit of Work | مدیریت تراکنشهای دیتابیس | |
| Dependency Injection | تزریق وابستگیها | |
| Application | CQRS (Command-Query Separation) | تفکیک عملیات نوشتن و خواندن |
| Mediator Pattern | کاهش وابستگی با واسطه (MediatR) | |
| Fluent Validation | اعتبارسنجی declarative | |
| Presentation | MVC/MVVM | جداسازی منطق نمایش |
| REST API | برای سرویسهای وب | |
| Clean UI | حداقل منطق در لایه نمایش |

- پیکانها: جهت وابستگی (مثلاً
Presentationفقط بهApplicationوابسته است). - رنگها:
- بنفش: لایه نمایش
- آبی: منطق کاربرد
- نارنجی: دامنه
- سبز: زیرساخت
نکات کلیدی:
- Domain نباید به هیچ لایه دیگری وابسته باشد.
- Infrastructure میتواند به Domain وابسته باشد (برای پیادهسازی اینترفیسها).
- الگوی CQRS در لایه
Applicationباعث تمایز واضح بین عملیات نوشتن (Commands) و خواندن (Queries) میشود. - Repository Pattern در
Infrastructureاز دامنه در برابر تغییرات دیتابیس محافظت میکند.
دیدگاه شما