淺談 Clean Architecture with ASP.NET Core 8
Clean Architecture 是一種軟體架構設計模式,旨在將應用程式的關注點明確地分離,使程式碼更具彈性、可測試性和可維護性。在本文中,我們將探討如何在 ASP.NET Core 8 中實作 Clean Architecture,並討論其優點和實踐方式。
Youtube影片來源
什麼是 Clean Architecture?
Clean Architecture 也被稱為 Onion Architecture、Hexagonal Architecture 或 Ports and Adapters。它的核心思想是將應用程式的核心業務邏輯與外部界面(如 UI、資料庫等)分離,使業務邏輯不受外部界面的影響。這種分離可以透過依賴反轉原則(Dependency Inversion Principle)來實現,將外部界面作為實作(Implementation),而業務邏輯作為抽象(Abstraction)。
在 Clean Architecture 中,應用程式被分為以下幾個層次:
層次 | 描述 |
---|---|
Domain Model | 包含應用程式的核心業務邏輯、實體(Entities)、值物件(Value Objects)和領域服務(Domain Services)。這是最內層,不依賴任何外部介面。 |
Application Layer | 定義應用程式的使用案例(Use Cases),包括命令(Commands)、查詢(Queries)和 DTO(Data Transfer Objects)。這層可以依賴 Domain Model,但不依賴外部介面。 |
Infrastructure Layer | 實作外部介面,如資料存取(Repository)、API 用戶端、檔案系統存取等。這層可以依賴 Application Layer 和 Domain Model。 |
Presentation Layer | 處理使用者介面(UI),如 Web 應用程式、CLI 等。這是最外層,可以依賴所有內層。 |
透過這種分層架構,Clean Architecture 使得應用程式的核心業務邏輯與外部界面完全解耦,提高了可測試性和可維護性。此外,它也符合開放/封閉原則(Open/Closed Principle),因為新功能的添加只需要在相應的層次中實作,而不需要修改其他層次的程式碼。
在 ASP.NET Core 8 中實作 Clean Architecture
ASP.NET Core 8 提供了一個方便的範本,可以快速建立符合 Clean Architecture 的解決方案。你只需要在終端機執行以下命令:
dotnet new install Microsoft.DotNet.Web.ProjectTemplates.8.0::8.0.0-preview.2.23128.3
dotnet new console -n cleanarch
這將建立一個包含以下專案的解決方案:
- Domain Model:包含實體、值物件、領域服務等核心業務邏輯。
- Application:定義使用案例,包括命令、查詢和 DTO。
- Infrastructure:實作資料存取、外部服務等。
- WebUI:ASP.NET Core Web 應用程式。
- Tests:單元測試和整合測試專案。
在 Domain Model 專案中,你可以定義實體、值物件和領域服務。例如,一個簡單的產品實體可能如下所示:
public class Product : Entity
{
public string Name { get; private set; }
public decimal Price { get; private set; }
public Product(string name, decimal price)
{
Name = name;
Price = price;
}
}
在 Application 專案中,你可以定義使用案例,例如創建新產品的命令和查詢:
// Command
public record CreateProductCommand(string Name, decimal Price) : IRequest<Guid>;
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Guid>
{
private readonly IRepository<Product> _productRepository;
public CreateProductCommandHandler(IRepository<Product> productRepository)
{
_productRepository = productRepository;
}
public async Task<Guid> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
var product = new Product(request.Name, request.Price);
_productRepository.Add(product);
await _productRepository.SaveChangesAsync(cancellationToken);
return product.Id;
}
}
// Query
public record GetProductQuery(Guid Id) : IRequest<ProductDto>;
public class GetProductQueryHandler : IRequestHandler<GetProductQuery, ProductDto>
{
private readonly IRepository<Product> _productRepository;
public GetProductQueryHandler(IRepository<Product> productRepository)
{
_productRepository = productRepository;
}
public async Task<ProductDto> Handle(GetProductQuery request, CancellationToken cancellationToken)
{
var product = await _productRepository.GetByIdAsync(request.Id, cancellationToken);
return new ProductDto(product.Id, product.Name, product.Price);
}
}
在 Infrastructure 專案中,你可以實作資料存取層,例如使用 Entity Framework Core:
public class ProductRepository : IRepository<Product>
{
private readonly ApplicationDbContext _dbContext;
public ProductRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public void Add(Product product)
{
_dbContext.Products.Add(product);
}
public async Task<Product> GetByIdAsync(Guid id, CancellationToken cancellationToken)
{
return await _dbContext.Products.FindAsync(id, cancellationToken);
}
// 其他存放庫方法...
}
最後,在 WebUI 專案中,你可以實作 ASP.NET Core Web 應用程式,包括控制器、視圖等。例如,一個產品控制器可能如下所示:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<ActionResult<Guid>> Create(CreateProductCommand command)
{
return await _mediator.Send(command);
}
[HttpGet("{id}")]
public async Task<ActionResult<ProductDto>> Get(Guid id)
{
return await _mediator.Send(new GetProductQuery(id));
}
}
透過這種架構,你可以清晰地分離應用程式的關注點,提高可測試性和可維護性。核心業務邏輯被封裝在 Domain Model 中,與外部介面完全解耦。而當需要新增功能時,只需要在相應的層次中實作,而不需要修改其他層次的程式碼,符合開放/封閉原則。
優點與實踐
實作 Clean Architecture 帶來了以下幾個主要優點:
可測試性提高:由於核心業務邏輯與外部介面分離,可以更輕鬆地對業務邏輯進行單元測試,而無需啟動整個應用程式或模擬外部依賴項。
更好的可維護性:通過將關注點明確分離,程式碼變得更加模組化和可理解。當需要修改或添加新功能時,只需要在相應的層次中進行更改,而不會影響其他層次的代碼。
更高的靈活性:由於外部介面是通過抽象介面與核心業務邏輯交互,因此可以輕鬆地更換或添加新的外部介面實現,而不需要修改核心業務邏輯。
更好的可伸縮性:由於關注點的明確分離,可以更輕鬆地在不同的團隊或個人之間分配工作,從而提高開發效率。
在實踐 Clean Architecture 時,需要注意以下幾點:
嚴格遵循依賴規則:外層不能直接依賴內層,只能通過接口進行交互。違反此原則將破壞整個架構的可測試性和可維護性。
適當使用設計模式:如 CQRS(Command Query Responsibility Segregation)、Domain Events、Specification 等設計模式,可以進一步提高架構的靈活性和可維護性。
保持業務邏輯的純淨性:Domain Model 中的業務邏輯應該與任何外部依賴項完全隔離,以保持其純淨性和可測試性。
適當抽象和封裝:在每個層次中,適當地抽象和封裝代碼,以減少重複代碼和提高可維護性。
充分利用依賴注入:在整個應用程式中廣泛使用依賴注入,以降低耦合度並提高可測試性。
總的來說,Clean Architecture 為構建可靠、可維護和可擴展的應用程式提供了一種有力的架構模式。通過適當的實踐,可以顯著提高應用程式的質量和開發效率。