8.2 分层架构与依赖方向
分层架构的目标不是让项目看起来复杂,而是让业务规则、接口入口和基础设施依赖各自待在合适位置。小项目也可以保持简单分层,避免后期难以测试和修改。
学习目标
- 能解释 API、Application、Domain、Infrastructure 的职责。
- 能设计正确的项目依赖方向。
- 能把业务规则从 Controller 或 Minimal API 中抽离。
- 能判断什么时候不需要过度抽象。
推荐分层
| 层 | 职责 | 不应该做什么 |
|---|---|---|
| API | HTTP、认证、模型绑定、响应 | 写复杂业务规则 |
| Application | 用例编排、事务边界、DTO | 依赖 Web 框架细节 |
| Domain | 业务实体、值对象、领域规则 | 依赖数据库或 HTTP |
| Infrastructure | EF Core、外部服务、文件、消息 | 反向依赖 API |
依赖方向
API 和 Infrastructure 都是外层,Domain 是最内层。内层不应该知道外层框架细节。
案例:完成任务用例
public sealed class CompleteTodoHandler(ITodoRepository repository)
{
public async Task<CompleteTodoResult> HandleAsync(
int todoId,
CancellationToken cancellationToken)
{
var todo = await repository.FindByIdAsync(todoId, cancellationToken);
if (todo is null)
{
return CompleteTodoResult.NotFound;
}
todo.Complete();
await repository.SaveChangesAsync(cancellationToken);
return CompleteTodoResult.Completed;
}
}
API 层只负责把 HTTP 请求转换成用例调用,真正的业务行为放在 Application 或 Domain。
重点难点
- 分层不是按文件夹摆放,而是控制依赖方向和职责边界。
- 不要为了模式而模式;简单 CRUD 可以保持简单,但规则复杂时要抽离。
- Domain 不应该依赖 EF Core 特性、HTTP Context 或配置系统。
- 接口要服务测试和替换需求,不要给每个类机械加接口。
常见误区
| 误区 | 推荐做法 |
|---|---|
| Controller 里写所有业务 | 把用例放到 Application 层 |
| Domain 直接调用数据库 | 通过仓储或应用服务协调持久化 |
| 层数越多越专业 | 按复杂度选择最小够用结构 |
练习
- 把创建任务逻辑从 Minimal API 抽到
CreateTodoHandler。 - 为
TodoItem.Complete()增加“已完成不能重复完成”的业务规则。 - 画出当前项目的依赖方向图。