DDD:保留指向聚合根目录内实体的链接,仅用于报告

时间:2011-09-15 10:38:19

标签: domain-driven-design entity-relationship aggregate ddd-repositories aggregateroot

我正在使用DDD重构一个项目,但我担心的是没有让太多的实体成为他们自己的聚合根。

我有一个Store,其中包含ProductOption个列表和Product个列表。多个ProductOption可以使用Product。这些实体似乎很适合Store聚合。

然后我有Order,它会暂时使用Product来构建其OrderLine

class Order {
    // ...
    public function addOrderLine(Product $product, $quantity) {
        $orderLine = new OrderLine($product, $quantity);
        $this->orderLines->add($orderLine);
    }
}

class OrderLine {
    // ...
    public function __construct(Product $product, $quantity) {
        $this->productName = $product->getName();
        $this->basePrice = $product->getPrice();
        $this->quantity = $quantity;
    }
}

现在看起来,DDD规则受到尊重。但是我想添加一个可能违反聚合规则的要求:商店所有者有时需要检查有关包含特定产品的订单的统计信息。

这意味着基本上,我们需要在OrderLine中保留对Product的引用,但实体内的任何方法都不会永远使用它。在查询数据库时,我们只会将此信息用于报告目的;因此,由于这个内部引用,不可能“破坏”Store聚合内的任何内容:

class OrderLine {
    // ...
    public function __construct(Product $product, $quantity) {
        $this->productName = $product->getName();
        $this->basePrice = $product->getPrice();
        $this->quantity = $quantity;

        // store this information, but don't use it in any method
        $this->product = $product;
    }
}

这个简单的要求是否要求Product成为聚合根?这也将级联到ProductOption成为聚合根,因为Product有对它的引用,因此导致两个聚合在Store之外没有任何意义,并且不需要任何Repository;对我来说很奇怪。

欢迎任何评论!

2 个答案:

答案 0 :(得分:3)

即使是“仅报告”,仍然存在业务/域名含义。我认为你的设计很好。虽然我不会通过存储OrderLine -> Product引用来处理新要求。我会做一些类似于你已经在做的产品名称和价格。您只需要在订单行中存储某种产品标识符(SKU?)。此标识符/ SKU稍后可用于查询。 SKU可以是商店和产品自然键的组合:

class Sku {
    private String _storeNumber;
    private String _someProductIdUniqueWithinStore;
}

class OrderLine {
    private Money _price;
    private int _quantity;
    private String _productName;
    private Sku _productSku;
}

这样,您就不会违反任何聚合规则,并且可以安全地删除产品和商店,而不会影响现有或已存档的订单。您仍然可以通过StoreY获得“ProductX订单”。

更新:关于您对外键的关注。在我看来,外键只是一个机制,它在数据库级别强制执行长期存在的域关系。由于您没有域关系,因此您也不需要强制机制。

答案 1 :(得分:0)

在这种情况下,您需要报告的信息与聚合根无关。

因此,最适合它的地方是服务(如果它与业务相关,则可以是域服务,或者更好地应用于查询所需数据的查询服务等应用服务,并将其作为可自定义的DTO返回给演示或消费者。

我建议您创建一个统计服务,使用只读存储库(或更好的Finders)查询所需数据,这些存储库返回DTO而不是使用查询模型破坏域。

检查this