跳到主要内容
版本:Next

分层架构图解

本文档引用的文件

目录

  1. 引言
  2. 分层架构概览
  3. 各层职责划分
  4. 数据调用流程图解
  5. 依赖关系与接口设计
  6. 依赖注入与松耦合实现
  7. 典型调用栈示例
  8. 常见架构误用及危害
  9. 结论

引言

本文档旨在全面解析项目的分层架构设计,涵盖表现层(Controller)、应用层(Application Service)、领域层(Domain Model)和基础设施层(EntityFrameworkCore Repository)。通过代码分析和图解方式,展示从HTTP请求进入系统到数据持久化的完整链路,阐明各层职责、依赖方向和设计原则。

分层架构概览

本项目采用典型的领域驱动设计(DDD)分层架构,分为四层:

  • 表现层:位于Controller目录,处理HTTP请求和响应
  • 应用层:位于ApplicationApplication.Contracts目录,编排业务用例
  • 领域层:位于Domain目录,承载核心业务逻辑和规则
  • 基础设施层:位于EntityFrameworkCore目录,实现数据访问和持久化

这种分层架构遵循依赖倒置原则,高层模块不依赖于低层模块,两者都依赖于抽象。

图解来源

各层职责划分

表现层(Controller)

表现层的职责严格限定为:

  • 接收HTTP请求并进行路由
  • 验证请求参数的基本格式
  • 调用应用服务接口
  • 封装并返回HTTP响应

MyEntityNameController中,所有方法都只是简单地委托给IMyEntityNameAppService接口,不包含任何业务逻辑。

本节来源

应用层(Application Service)

应用层负责:

  • 编排业务用例的执行流程
  • 协调多个领域对象的交互
  • 处理事务边界
  • 进行应用级别的验证
  • 实现DTO与领域模型之间的转换

MyEntityNameAppService实现了IMyEntityNameAppService接口,协调领域模型和仓储之间的交互。

本节来源

领域层(Domain Model)

领域层是业务核心,职责包括:

  • 定义实体、值对象和聚合根
  • 封装核心业务规则和不变性
  • 实现领域行为而非仅是数据容器
  • 确保业务状态的一致性

MyEntityName类不仅包含属性,还定义了UpdateAdjustSortClone等业务行为方法。

本节来源

基础设施层(Repository)

基础设施层的职责是:

  • 抽象数据访问细节
  • 提供领域层所需的查询和持久化能力
  • 实现仓储接口的具体技术方案
  • 处理数据库连接、事务等技术细节

EfCoreMyEntityNameRepository实现了IMyEntityNameRepository接口,使用Entity Framework Core进行数据访问。

本节来源

数据调用流程图解

以下序列图展示了从HTTP请求到数据库持久化的完整调用链路:

图解来源

依赖关系与接口设计

本架构严格遵循依赖倒置原则(DIP),各层之间的依赖关系如下:

图解来源

依赖倒置原则的实际体现

  • 表现层依赖应用层接口MyEntityNameController依赖IMyEntityNameAppService接口而非具体实现
  • 应用层依赖领域层接口MyEntityNameAppService依赖IMyEntityNameRepository接口而非EfCoreMyEntityNameRepository具体类
  • 基础设施层实现领域层接口EfCoreMyEntityNameRepository实现IMyEntityNameRepository接口

这种设计使得各层可以独立开发、测试和替换,例如可以轻松更换数据访问技术而不影响业务逻辑。

本节来源

依赖注入与松耦合实现

项目通过依赖注入容器实现松耦合,各层的依赖关系在运行时由容器解析:

图解来源

为何Application层依赖Domain而非反之

  1. 业务逻辑稳定性:领域层包含核心业务规则,相对稳定;应用层处理用例编排,可能频繁变化
  2. 抽象层次:领域层代表业务概念,是更高层次的抽象;应用层是业务概念的具体应用
  3. 重用性:领域模型可以在多个应用服务中重用,而应用服务通常是特定用例的
  4. 测试隔离:领域层可以独立于应用层进行单元测试,确保核心业务逻辑的正确性

本节来源

典型调用栈示例

以下是一个创建MyEntityName实体的典型调用栈:

1. MyEntityNameController.CreateAsync(input)
↓ 调用应用服务接口
2. MyEntityNameAppService.CreateAsync(input)
↓ 检查名称是否存在
3. IMyEntityNameRepository.NameExistAsync(input.Name)
↓ EF Core实现
4. EfCoreMyEntityNameRepository.NameExistAsync(name)
↓ 数据库查询
5. Database: SELECT COUNT(*) FROM myentitynames WHERE name = @name
↓ 返回结果
6. EfCoreMyEntityNameRepository 返回 bool
↓ 返回结果
7. MyEntityNameAppService 接收结果
↓ 检查业务规则
8. MyEntityNameAppService.GetMaxSortAsync()
↓ 获取最大排序值
9. EfCoreMyEntityNameRepository.GetMaxSortAsync()
↓ 数据库查询
10. Database: SELECT MAX(sort) FROM myentitynames
↓ 创建领域对象
11. new MyEntityName(id, code, name, sort, remark)
↓ 持久化实体
12. IMyEntityNameRepository.InsertAsync(myEntityName)
↓ EF Core插入
13. EfCoreMyEntityNameRepository.InsertAsync(entity)
↓ 数据库操作
14. Database: INSERT INTO myentitynames VALUES (...)
↓ 返回结果
15. EfCoreMyEntityNameRepository 返回 MyEntityName
↓ 转换为DTO
16. ObjectMapper.Map<MyEntityName, MyEntityNameDto>
↓ 返回结果
17. MyEntityNameAppService 返回 MyEntityNameDto
↓ 返回响应
18. MyEntityNameController 返回 201 Created

本节来源

常见架构误用及危害

在Controller中直接访问Repository

// 错误做法
[HttpPost]
public async Task<MyEntityNameDto> CreateAsync(MyEntityNameCreateDto input)
{
// 直接访问Repository,违反分层原则
var exist = await _myEntityNameRepository.NameExistAsync(input.Name);
if (exist)
{
throw new UserFriendlyException("名称已存在");
}

var myEntityName = new MyEntityName(Guid.NewGuid(), input.Code, input.Name);
await _myEntityNameRepository.InsertAsync(myEntityName);

return ObjectMapper.Map<MyEntityName, MyEntityNameDto>(myEntityName);
}

危害

  1. 破坏分层:表现层直接依赖基础设施层,破坏了架构的清晰边界
  2. 重复代码:业务规则(如名称唯一性检查)在多个Controller中重复
  3. 难以测试:Controller变得臃肿,难以进行单元测试
  4. 耦合度高:Controller与数据访问技术耦合,难以更换数据库技术
  5. 事务管理困难:复杂的业务操作难以保证事务一致性

正确做法:所有业务逻辑都应在应用层处理,Controller只负责请求路由和响应封装。

本节来源

结论

本项目的分层架构设计遵循领域驱动设计原则,通过清晰的职责划分和依赖倒置实现了高内聚、低耦合的系统结构。表现层专注于请求响应处理,应用层编排业务用例,领域层承载核心业务规则,基础设施层抽象数据访问。各层通过接口进行通信,依赖注入容器负责实例化和依赖解析,确保了系统的可维护性、可测试性和可扩展性。遵循此架构模式,可以有效避免常见的架构误用,构建健壮的企业级应用系统。