我正在使用 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 之外没有任何意义,并且不需要任何存储库;我觉得很奇怪。
欢迎任何评论!
最佳答案
尽管它只是用于“报告”,但仍然具有业务/域的含义。我认为你的设计很好。尽管我不会通过存储 OrderLine -> Product
引用来处理新要求。我会做一些类似于您已经对产品名称和价格所做的事情。您只需要在订单行中存储某种产品标识符(SKU?)。此标识符/SKU 稍后可以在查询中使用。 SKU 可以是 Store 和 Product 自然键的组合:
class Sku {
private String _storeNumber;
private String _someProductIdUniqueWithinStore;
}
class OrderLine {
private Money _price;
private int _quantity;
private String _productName;
private Sku _productSku;
}
这样您就不会违反任何聚合规则,并且可以安全地删除产品和商店,而不会影响现有或已存档的订单。您仍然可以拥有“来自 StoreY 的 ProductX 订单”。
更新:关于您对外键的担忧。在我看来,外键只是一种在数据库级别强制执行长期存在的域关系的机制。由于您没有域关系,因此也不需要强制机制。
关于domain-driven-design - DDD : keep a link to an entity inside an aggregate root, 仅用于报告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7429515/