image.png

贫血模型是一种软件设计模式,其中业务逻辑被放置在服务层或管理类中,而领域模型仅包含数据和访问这些数据的方法。这种模式有时被认为是反模式,因为它可能导致领域模型过于简单,缺乏业务逻辑。

肖哥弹架构 跟大家“弹弹” 代码设计技巧,需要代码关注

欢迎 点赞,点赞,点赞。

关注公号Solomon肖哥弹架构获取更多精彩内容

历史热点文章

2. 贫血模型设计图:

贫血模型的设计哲学是将业务逻辑从领域模型中移除,使得领域模型只包含属性和基本的getter/setter方法,而业务逻辑则在应用服务层中实现。

image.png- 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) {
            // 订单处理逻辑
        }
    }

分析问题:

  1. 业务逻辑分布不均
    • 所有业务逻辑集中在服务层,领域模型只包含数据和访问器,没有业务逻辑。
  2. 领域模型功能不足
    • 领域模型变得简单但无用,不能表达业务规则或行为。
  3. 服务层臃肿
    • 随着业务逻辑的增加,服务层变得越来越复杂,难以维护。
  4. 测试困难
    • 由于业务逻辑分散在服务层,单元测试变得更加困难,需要大量模拟。
  5. 缺乏封装
    • 领域模型没有封装业务逻辑,导致数据和行为分离。
  6. 违反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();
            // 其他订单处理逻辑
        }
    }

解决的问题:

  1. 业务逻辑合理分布
    • 将业务逻辑移至领域模型,使得领域模型能够表达业务规则。
  2. 增强领域模型的实用性
    • 领域模型不再只是数据容器,而是具有实际业务逻辑的实体。
  3. 服务层简化
    • 服务层不再负责所有业务逻辑,而是协调领域模型之间的交互。
  4. 易于测试
    • 领域模型具有明确的业务逻辑,便于编写单元测试。
  5. 封装性增强
    • 业务逻辑封装在领域模型中,提高了数据和行为的封装性。
  6. 遵循DRY原则
    • 通过将业务逻辑集中到领域模型,避免了在服务层的重复代码。
  7. 提高代码可读性和可维护性
    • 领域模型的业务逻辑清晰,提高了代码的可读性和可维护性。
  8. 更好的扩展性
    • 当业务需求变化时,可以更容易地在领域模型中添加或修改逻辑。

8.贫血模型与充血模型区别

  • 业务逻辑的位置
    • 贫血模型将业务逻辑放在服务层,而充血模型将业务逻辑封装在领域对象内部。
  • 领域对象的复杂性
    • 贫血模型的领域对象通常更简单,而充血模型的领域对象可能更复杂,因为它们包含业务逻辑。
  • 测试的难易程度
    • 贫血模型可能使单元测试更容易编写,因为测试可以集中在服务层。
    • 充血模型可能使单元测试更复杂,因为需要模拟领域对象的内部行为。
  • 系统的可维护性
    • 贫血模型可能导致服务层随着业务逻辑的增加而变得难以维护。
    • 充血模型通过分散业务逻辑,可能提高系统的可维护性,但也可能增加单个对象的复杂性。
  • 设计哲学
    • 贫血模型倾向于简化领域对象,专注于数据。
    • 充血模型强调领域对象的完整性,认为对象应该完全表达其业务意图。

9. 参考开源框架:

许多现代Web框架,如Spring Boot,鼓励使用服务层来处理业务逻辑,这有时会导致贫血模型的使用。

10. 总结:

贫血模型通过将业务逻辑从领域模型中移除,试图简化设计。然而,这种方法可能会导致领域模型过于简单,而服务层变得过于复杂。在设计系统时,需要权衡领域模型的复杂性和服务层的职责,以确保系统的可维护性和可扩展性。虽然贫血模型在某些情况下可能有用,但过度简化可能会导致其他问题。