分层架构与四种模型:失血、贫血、充血、胀血
战术与分层 · 第 8/15 篇
上一篇:《通用语言与事件风暴落地》 · 下一篇:《Domain Primitive:定义、三原则与重构步骤》
开头:三层 MVC 够用,为什么还要 DDD 四层?
很多项目从「表现层 → 业务层 → DAO」起步,表结构一确定就开始 CRUD。业务复杂后,Service 膨胀、产品与开发「各说各话」、改需求牵一发动全身——DDD 的分层与领域模型,正是为了把业务逻辑收拢到领域层,让技术细节沉到基础设施,而不是让 Service 变成万能事务脚本。
本篇对比传统三层与 DDD 四层,梳理失血 / 贫血 / 充血 / 胀血四种对象建模风格,并说明分层利弊与适用场景。
一、DDD 核心概念与分层动机
领域驱动设计围绕领域模型,通过分层架构把领域独立出来。模型对象包括实体、值对象、领域服务;聚合封装一致性边界;工厂与仓储管理对象生命周期。
传统 MVC/三层(表现 → 业务逻辑 → 数据访问)在小系统里清晰,但从领域视角有两个问题:
- 基础结构层职责含混:既做纯技术又夹带业务,与业务层纠缠。
- 数据访问地位过高:「先建表再写代码」让持久化细节反客为主;无表场景或复杂领域时,团队不知业务该放哪。
业务复杂后,Service 层代码暴增、测试困难、新人对着模型两眼茫然——根因常是缺少与产品一致的领域语言,以及业务逻辑未归属到领域对象。

二、四种领域模型风格
实践中常把领域对象按「行为归谁」分成四种说法(含戏谑的「胀血」):
2.1 失血模型
POJO 连 getter/setter 都没有,所有行为在外部 Service 里。对象只是数据载体,毫无封装。
@Data
public class User {
private Long id;
private String username;
private Integer status;
// 无行为
}
// 业务全在 UserService2.2 贫血模型
对象有 getter/setter,甚至少量内存态行为,但持久化与核心业务仍在 Service(事务脚本)。这是许多 Spring 项目的默认形态:Domain 类当 DTO 用,Logic 层 singleton Bean 扛一切。
@Data
public class User {
private Integer status;
public boolean isActive() {
return status.equals(StatusEnum.ACTIVE.getCode());
}
}
// 复杂流程仍在 UserService优点:层次清楚、上手快。缺点:状态与行为分离,不像面向对象,复杂规则堆在 Service。
2.3 充血模型
大部分业务逻辑在领域对象内;应用服务只做事务、权限、编排。对象可依赖 Repository 接口完成持久化。
public class User {
private UserRepository userRepository;
public void rename(String username) {
this.username = username.trim();
userRepository.update(this);
}
}优点:符合 OO、职责清晰。缺点:与仓储耦合紧,团队需领域建模能力;大团队协作时「完整用例」难再切分。
2.4 胀血模型
Service 都不要,逻辑与存储全塞进一个类——可维护性最差。DDD 语境下,失血缺聚合、胀血缺边界,都不合适;实践多在贫血与充血之间权衡。

2.5 贫血 vs 充血(对照)
| 维度 | 充血(DDD 倾向) | 贫血(传统 OOP/CRUD) |
|---|---|---|
| 编码 | 以领域对象状态转换为主 | 事务脚本式 Service |
| Domain | 实体、值对象、聚合 | 多为 VO/DTO |
| Service | 跨领域组合、应用编排 | 承载主要业务逻辑 |
| 聚合 | 显式边界与聚合根 | 往往缺失 |

三、DDD 四层架构
Evans 在《领域驱动设计》中提出四层结构,与三层相比:DAO 沉入基础设施层,领域层成为核心。


3.1 基础设施层(Infrastructure)
为其他层提供与技术相关、与业务无关的能力:持久化实现、消息、序列化等。领域层通过 Repository 接口声明需求,基础设施层用 JDBC/JPA/NoSQL 等实现。
数据的 CRUD 本身不是业务逻辑,因此读写实现放在此层。
3.2 领域层(Domain)
包含实体、值对象、领域服务及其关系,即领域模型。提倡富领域模型:规则尽量进对象;放不下的用领域服务。
领域层业务逻辑示例:
- 业务实体(账户、订单)
- 业务规则(余额不足不可取款)
- 完整性约束、业务流程片段
与 CRUD 模式的差别:领域对象有行为,且保证不变式(不可能构造非法对象)。
3.3 应用层(Application)
不含领域规则,负责编排:按用例顺序调用领域对象,类似网关或总线的「转发与拼装」。可维护应用状态、控制事务边界。
3.4 用户接口层(User Interface)
对外交互:Web、App、REST API 等。通过 DTO 与应用层交换数据——避免把领域对象的行为与内部结构直接暴露给外部。
可选的门面层(Facade)夹在外层与应用层之间:独立前后端开发、DTO 装配、分布式部署时避免「Open Session In View」与长事务。
四、分层的优缺点
4.1 优点
- 单一职责:每层一类关切
- 高内聚:业务逻辑集中在领域层
- 低耦合:上层依赖下层,无环
- 可复用、易维护:外部接口变更集中在接口层
4.2 缺点
- 开发成本:功能跨层改动
- 性能:多一层多一次转换与调用
- 扩展:层间耦合使部分需求需多层修改
原则:该分则分;简单 CRUD 不必硬上四层。
五、何时值得上 DDD?
DDD 把设计驱动从数据表转向领域模型,用封装、多态等降低业务复杂度。适合:
- 业务复杂或预期会持续变复杂
- 需要产品与开发统一语言
- 希望领域知识沉淀、边界清晰
若系统简单、扩展预期低,数据模型驱动可能更划算——DDD 的封装与隔离有学习与协作成本,还有聚合加载、跨上下文事务等工程挑战。
| DDD 收益 | 说明 |
|---|---|
| 统一语言 | 限界上下文内术语一致 |
| 知识沉淀 | 模型与技术实现分离 |
| 架构清晰 | 业务复杂度 vs 技术复杂度分离 |
| 可扩展 | 新需求可判断归属子域 |
| DDD 难点 | 说明 |
|---|---|
| 人才与流程 | 需要领域专家 + 熟练实践者 |
| 前期投入 | 建模与对齐耗时 |
| 协作 | 模型整体性强,难完全并行 |
| 性能 | 大聚合、跨上下文一致性需额外设计 |
本篇小结
战术设计从分层 + 模型风格落地战略里划定的边界:四层把领域托举到中心,充血模型让规则回到对象。下一篇起将进入 Domain Primitive、六边形架构、Repository 等更具体的战术模式——在选对分层与模型风格之后,再把类型安全与边界隔离做细。