贫血模型是一种软件设计模式,其中业务逻辑被放置在服务层或管理类中,而领域模型仅包含数据和访问这些数据的方法。这种模式有时被认为是反模式,因为它可能导致领域模型过于简单,缺乏业务逻辑。
肖哥弹架构 跟大家“弹弹” 代码设计技巧,需要代码关注
欢迎 点赞,点赞,点赞。
关注公号Solomon肖哥弹架构获取更多精彩内容
历史热点文章
- 依赖倒置原则:支付网关设计应用案例
- Holder模式(Holder Pattern):公司员工权限管理系统实战案例分析
- 一个项目代码讲清楚DO/PO/BO/AO/E/DTO/DAO/ POJO/VO
- 写代码总被Dis:5个项目案例带你掌握SOLID技巧,代码有架构风格
- 里氏替换原则在金融交易系统中的实践,再不懂你咬我
2. 贫血模型设计图:
贫血模型的设计哲学是将业务逻辑从领域模型中移除,使得领域模型只包含属性和基本的getter/setter方法,而业务逻辑则在应用服务层中实现。
- DomainModel
是一个领域模型,它包含数据和基本的getter/setter方法。
Service
是一个服务类,它处理业务逻辑。在这个例子中,我们展示了OrderService
处理Order
对象的逻辑。Order
是一个具体的领域模型,包含订单数据和基本的getter/setter方法。User
是另一个领域模型,包含用户数据和基本的getter/setter方法。OrderService
是一个服务类,它依赖于Order
模型,并处理订单相关的业务逻辑。
3.贫血模型解决什么:
贫血模型试图通过简化领域模型来降低系统的复杂性,使得领域模型更容易理解和维护。
4. 贫血模型特点:
- 简单性:领域模型简单,只包含数据和基本的数据访问方法。
- 业务逻辑集中:所有的业务逻辑都集中在服务层,易于管理和测试。
5. 贫血模型缺点:
- 领域模型贫血:领域模型失去了表达业务逻辑的能力,变得过于简单。
- 服务层臃肿:随着业务逻辑的增加,服务层可能会变得过于复杂和难以维护。
6. 模型使用场景:
当面临需要快速开发和部署的简单应用程序时,或者当领域逻辑非常简单时,可以考虑使用贫血模型。
7. 贫血模型案例
7.1 电商平台的订单处理系统案例
考虑一个电商平台的订单处理系统,该系统需要处理订单和用户信息。
重构前:
public class Order {
private String orderId;
private double totalAmount;
private User user;
// 仅包含基本的getter和setter方法
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public double getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(double totalAmount) {
this.totalAmount = totalAmount;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
public class UserService {
public void processOrder(Order order) {
// 订单处理逻辑
}
}
分析问题:
- 业务逻辑分布不均:
- 所有业务逻辑集中在服务层,领域模型只包含数据和访问器,没有业务逻辑。
- 领域模型功能不足:
- 领域模型变得简单但无用,不能表达业务规则或行为。
- 服务层臃肿:
- 随着业务逻辑的增加,服务层变得越来越复杂,难以维护。
- 测试困难:
- 由于业务逻辑分散在服务层,单元测试变得更加困难,需要大量模拟。
- 缺乏封装:
- 领域模型没有封装业务逻辑,导致数据和行为分离。
- 违反DRY原则:
- 服务层可能重复实现相同的业务逻辑,违反了“不要重复自己”(DRY)原则。
重构后:
public class Order {
private String orderId;
private double totalAmount;
private User user;
// 仅包含基本的getter和setter方法
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public double getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(double totalAmount) {
this.totalAmount = totalAmount;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void applyDiscounts() {
// 应用折扣逻辑
}
}
public class OrderService {
public void processOrder(Order order) {
order.applyDiscounts();
// 其他订单处理逻辑
}
}
解决的问题:
- 业务逻辑合理分布:
- 将业务逻辑移至领域模型,使得领域模型能够表达业务规则。
- 增强领域模型的实用性:
- 领域模型不再只是数据容器,而是具有实际业务逻辑的实体。
- 服务层简化:
- 服务层不再负责所有业务逻辑,而是协调领域模型之间的交互。
- 易于测试:
- 领域模型具有明确的业务逻辑,便于编写单元测试。
- 封装性增强:
- 业务逻辑封装在领域模型中,提高了数据和行为的封装性。
- 遵循DRY原则:
- 通过将业务逻辑集中到领域模型,避免了在服务层的重复代码。
- 提高代码可读性和可维护性:
- 领域模型的业务逻辑清晰,提高了代码的可读性和可维护性。
- 更好的扩展性:
- 当业务需求变化时,可以更容易地在领域模型中添加或修改逻辑。
8.贫血模型与充血模型区别
- 业务逻辑的位置:
- 贫血模型将业务逻辑放在服务层,而充血模型将业务逻辑封装在领域对象内部。
- 领域对象的复杂性:
- 贫血模型的领域对象通常更简单,而充血模型的领域对象可能更复杂,因为它们包含业务逻辑。
- 测试的难易程度:
- 贫血模型可能使单元测试更容易编写,因为测试可以集中在服务层。
- 充血模型可能使单元测试更复杂,因为需要模拟领域对象的内部行为。
- 系统的可维护性:
- 贫血模型可能导致服务层随着业务逻辑的增加而变得难以维护。
- 充血模型通过分散业务逻辑,可能提高系统的可维护性,但也可能增加单个对象的复杂性。
- 设计哲学:
- 贫血模型倾向于简化领域对象,专注于数据。
- 充血模型强调领域对象的完整性,认为对象应该完全表达其业务意图。
9. 参考开源框架:
许多现代Web框架,如Spring Boot,鼓励使用服务层来处理业务逻辑,这有时会导致贫血模型的使用。
10. 总结:
贫血模型通过将业务逻辑从领域模型中移除,试图简化设计。然而,这种方法可能会导致领域模型过于简单,而服务层变得过于复杂。在设计系统时,需要权衡领域模型的复杂性和服务层的职责,以确保系统的可维护性和可扩展性。虽然贫血模型在某些情况下可能有用,但过度简化可能会导致其他问题。