领域驱动设计(DDD)——聚合 详解和示例

        在领域驱动设计(DDD,Domain-Driven Design)中,**聚合(Aggregate)**是一个核心概念,用于定义领域模型中一组紧密关联的对象,以及它们之间的边界和关系。

        简单来说,聚合是一组具有强一致性约束的领域对象的集合,这些对象是作为一个整体进行操作的

DDD架构
DDD架构

聚合的定义

聚合是由一个聚合根(Aggregate Root)及其相关对象组成的边界内的集合。
聚合根是聚合的入口点,对聚合内的所有对象负责。

聚合帮助我们定义哪些对象需要一起变化、被一起操作,同时帮助我们控制领域模型的复杂性。


聚合的设计原则

  1. 聚合定义边界:
    聚合将领域对象划分成多个边界,每个边界内包含一个聚合根和其管理的相关对象。聚合外部的代码不能直接操作聚合内的其他对象。

  2. 聚合根是唯一的入口:
    只有通过聚合根,才能访问或操作聚合内的对象。聚合内的其他对象(称为“子实体”或“值对象”)只能通过聚合根来操作。

  3. 聚合保证一致性:
    聚合在某个时间点内应该保持一致性。当修改聚合时,一次操作应确保所有规则和约束都满足。

  4. 聚合避免过大:
    聚合的边界不宜过大,否则会导致性能和复杂性问题。建议一个聚合尽量保持小而清晰。

  5. 聚合根有唯一标识:
    聚合根通常有一个全局唯一的标识符,用于标识整个聚合。


为什么需要聚合

  1. 复杂性管理:
    聚合为领域模型分割出清晰的边界,避免模型变得混乱和难以维护。

  2. 一致性控制:
    聚合内部的对象必须通过聚合根来操作,从而保证聚合内部的状态一致性。

  3. 简化持久化:
    聚合让我们可以将一组相关对象作为整体进行存储、查询和更新。

  4. 保护领域模型:
    聚合外部的代码无法直接访问聚合内的非聚合根对象,从而保护领域模型的完整性。


聚合的组成

  1. 聚合根(Aggregate Root):

    • 聚合的核心对象。
    • 负责管理聚合内部的对象。
    • 对外提供聚合的唯一入口,外界只能通过聚合根访问聚合内部的对象。
    • 有一个全局唯一标识符(ID)。
  2. 实体(Entity):

    • 聚合内的独立对象,具有唯一标识符。
    • 聚合内的实体只能通过聚合根访问,不能直接被外部操作。
  3. 值对象(Value Object):

    • 聚合内的无唯一标识的对象,用于描述实体的属性或行为。
    • 值对象是不可变的。

聚合的例子

示例场景:订单和订单项

        我们以一个电子商务系统为例,“订单(Order)订单项(OrderItem)”的关系很适合作为聚合的示例。

  • 订单(Order)是聚合根,负责管理整个订单。
  • 订单项(OrderItem)是订单的组成部分,不能脱离订单单独存在。
  • 值对象可以是“商品价格”和“折扣信息”。
示例代码
import java.util.ArrayList;
import java.util.List;

/**
 * 值对象:价格。
 * 无唯一标识,仅描述商品的属性。
 */
class Price {
    private final double amount; // 金额
    private final String currency; // 货币单位

    public Price(double amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public double getAmount() {
        return amount;
    }

    public String getCurrency() {
        return currency;
    }
}

/**
 * 实体:订单项(OrderItem)。
 * 订单项是聚合内的子实体,描述订单中的单个商品信息。
 */
class OrderItem {
    private final String productId; // 商品 ID
    private final int quantity;     // 商品数量
    private final Price price;      // 商品价格

    public OrderItem(String productId, int quantity, Price price) {
        this.productId = productId;
        this.quantity = quantity;
        this.price = price;
    }

    public String getProductId() {
        return productId;
    }

    public int getQuantity() {
        return quantity;
    }

    public Price getPrice() {
        return price;
    }
}

/**
 * 聚合根:订单(Order)。
 * 订单是聚合的根,负责管理订单项。
 */
class Order {
    private final String orderId;       // 订单唯一标识
    private final String userId;        // 用户 ID
    private final List<OrderItem> items; // 订单中的商品列表

    public Order(String orderId, String userId) {
        this.orderId = orderId;
        this.userId = userId;
        this.items = new ArrayList<>();
    }

    public String getOrderId() {
        return orderId;
    }

    public String getUserId() {
        return userId;
    }

    /**
     * 添加订单项。
     *
     * @param item 要添加的订单项
     */
    public void addItem(OrderItem item) {
        items.add(item);
    }

    /**
     * 获取所有订单项。
     *
     * @return 订单项列表
     */
    public List<OrderItem> getItems() {
        return items;
    }

    /**
     * 计算订单总金额。
     *
     * @return 订单总金额
     */
    public double calculateTotal() {
        return items.stream()
                .mapToDouble(item -> item.getPrice().getAmount() * item.getQuantity())
                .sum();
    }
}

聚合的特点

  1. 外部只能操作聚合根:
    在上述代码中,订单项(OrderItem)不能单独存在,所有操作必须通过订单(Order)来进行,比如添加订单项、获取订单项列表等。

  2. 聚合根保证一致性:
    聚合根负责维护聚合内的业务规则。例如,订单的总金额是由所有订单项的金额计算得出的,外部无法直接修改订单项,只能通过聚合根进行。

  3. 聚合的边界清晰:
    订单是聚合的边界,订单项和价格等对象只能通过订单访问。


聚合在生产环境中的使用

        在实际开发中,聚合通常会与仓储一起使用。仓储负责保存和加载聚合根,同时通过聚合根间接管理聚合内的其他对象。

示例:基于 MyBatis 的聚合操作
public interface OrderMapper {
    // 保存订单
    void insertOrder(Order order);

    // 保存订单项
    void insertOrderItem(OrderItem orderItem);

    // 根据订单 ID 查询订单
    Order selectOrderById(String orderId);

    // 根据订单 ID 查询订单项
    List<OrderItem> selectOrderItemsByOrderId(String orderId);
}

总结

  1. 聚合定义了一组具有强一致性约束的对象集合,并通过聚合根管理其内部状态。
  2. 聚合根是外界与聚合交互的唯一入口。
  3. 聚合控制了领域模型的边界,避免了直接操作子对象导致的不一致性。
  4. 生产环境中,聚合与仓储结合使用,既保证模型的清晰性,又便于持久化和扩展。

        通过聚合设计,可以更好地管理领域模型的复杂性,保持模型的内聚性和一致性,从而实现健壮的业务逻辑实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值