贫血模型与充血模型
贫血模型与充血模型
- 显著差异。
- 贫血模型将业务逻辑分散在服务层(Service)。
- 充血模型则将业务逻辑内聚在实体类(Entity)中。
特性对比
特性 | 贫血模型 (Anemic Domain Model) | 充血模型 (Rich Domain Model) |
---|---|---|
业务逻辑位置 | 主要在服务层 (Service Layer) | 内聚在实体类 (Entity Class) |
数据结构 | 仅包含数据属性 (Getters/Setters) | 包含业务方法 (Methods) |
代码可读性 | 较低,业务逻辑分散 | 较高,业务逻辑集中 |
维护性 | 较差,逻辑分散在多个类中 | 较好,逻辑集中在实体类中 |
扩展性 | 较低,需要添加新的实体类 | 较高,添加新的实体类不会影响其他类 |
领域事件 | 不支持或难以实现 | 支持,易于实现领域事件 |
领域模型设计 | 不符合领域驱动设计 (DDD) | 符合领域驱动设计 (DDD) |
测试性 | 较差,业务逻辑分散 | 较好,业务逻辑集中 |
代码重复率 | 较高,服务层可能重复逻辑 | 较低,业务逻辑集中在实体类中 |
学习曲线 | 较低,简单易懂 | 较高,需要理解领域驱动设计概念 |
案例 - 用户转账
贫血模型写法
// 贫血的订单实体 (Anemic Order Entity)
public class Order {private String orderId;private double amount;private String status; // 例如: "PENDING", "PAID", "SHIPPED", "CANCELLED"// 只有 getter 和 setter 方法public String getOrderId() { return orderId; }public void setOrderId(String orderId) { this.orderId = orderId; }public double getAmount() { return amount; }public void setAmount(double amount) { this.amount = amount; }public String getStatus() { return status; }public void setStatus(String status) { this.status = status; }// 没有业务方法,比如 order.pay() 或 order.cancel()
}// 订单服务 (OrderService) - 业务逻辑都在这里
public class OrderService {public void createOrder(Order order) { /* 保存订单到数据库 */ }public void processPayment(String orderId) {Order order = orderRepository.findById(orderId); // 从数据库获取贫血 Order 对象if ("PENDING".equals(order.getStatus())) {// 执行支付逻辑order.setStatus("PAID"); // 修改状态orderRepository.save(order); // 保存回数据库} else {throw new IllegalStateException("订单状态不正确,无法支付");}}public void cancelOrder(String orderId) {Order order = orderRepository.findById(orderId);if ("PENDING".equals(order.getStatus())) {// 执行取消逻辑order.setStatus("CANCELLED"); // 修改状态orderRepository.save(order);} else {throw new IllegalStateException("订单状态不正确,无法取消");}}
}
充血模型写法
// 充血的订单实体 (Rich Order Entity)
public class Order {private String orderId;private double amount;private OrderStatus status; // 使用枚举或更复杂的对象表示状态public Order(String orderId, double amount) {this.orderId = orderId;this.amount = amount;this.status = OrderStatus.PENDING; // 初始状态在构造时设定}// 业务方法内聚在实体中public void pay() {if (this.status != OrderStatus.PENDING) {throw new IllegalStateException("订单状态不正确,无法支付。当前状态: " + this.status);}// 执行支付相关的内部逻辑(例如扣款,如果实体内部能处理)this.status = OrderStatus.PAID; // 状态变更逻辑在实体内部// 也可以触发领域事件}public void cancel() {if (this.status != OrderStatus.PENDING && this.status != OrderStatus.PAID) {throw new IllegalStateException("订单状态不正确,无法取消。当前状态: " + this.status);}// 执行取消相关的内部逻辑this.status = OrderStatus.CANCELLED; // 状态变更逻辑在实体内部}// getter 方法public String getOrderId() { return orderId; }public double getAmount() { return amount; }public OrderStatus getStatus() { return status; }
}// 订单服务 (OrderService) - 变得更薄,只负责事务和协调
public class OrderService {public void processOrderPayment(String orderId) {Order order = orderRepository.findById(orderId); // 从数据库获取充血 Order 对象order.pay(); // 调用实体自身的业务方法orderRepository.save(order); // 保存更新后的实体}public void cancelCustomerOrder(String orderId) {Order order = orderRepository.findById(orderId);order.cancel(); // 调用实体自身的业务方法orderRepository.save(order);}
}public enum OrderStatus {PENDING, PAID, SHIPPED, CANCELLED
}
总结
充血模型更符合领域驱动设计的原则,能够提高代码的可读性、维护性和扩展性。
通过将业务逻辑集中在实体类中,充血模型能够更好地反映领域概念,并支持领域事件的实现。
充血模型虽然学习曲线较陡,但在复杂业务场景中能够提供更好的解决方案。
实际上很多项目都使用的贫血模型,无他,简单、快速。天然适合快速开发。
最后 充血模型其实更像是理想,贫血模型是现实
理想很丰满,现实很骨感
。
如果团队成员对领域驱动设计不熟悉,或者项目需求简单,还是使用贫血模型可以更快地上手和迭代。
如果项目复杂,业务逻辑较多,充血模型能够更好地组织代码和业务逻辑。
充血模型对团队开发者的要求较高,需要理解领域驱动设计的概念和实践,自己个人项目尝试差不多了。