DDD:保留到聚合根内实体的链接,仅用于报告

发布于 2024-12-05 03:45:45 字数 1535 浏览 3 评论 0原文

我正在使用 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 之外没有任何意义,并且不需要任何存储库;我觉得很奇怪。

有什么意见欢迎留言!

I'm refactoring a project using DDD, but am concerned about not making too many Entities their own Aggregate Root.

I have a Store, which has a list of ProductOptions and a list of Products. A ProductOption can be used by several Products. These entities seem to fit the Store aggregate pretty well.

Then I have an Order, which transiently uses a Product to build its OrderLines:

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;
    }
}

Looks like for now, DDD rules as respected. But I'd like to add a requirement, that might break the rules of the aggregate: the Store owner will sometimes need to check statistics about the Orders which included a particular Product.

That means that basically, we would need to keep a reference to the Product in the OrderLine, but this would never be used by any method inside the entity. We would only use this information for reporting purposes, when querying the database; thus it would not be possible to "break" anything inside the Store aggregate because of this internal reference:

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;
    }
}

Does this simple requirement dictates that Product becomes an aggregate root? That would also cascade to the ProductOption becoming an aggregate root, as Product has a reference to it, thus resulting in two aggregates which have no meaning outside a Store, and will not need any Repository; looks weird to me.

Any comment is welcome!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

七色彩虹 2024-12-12 03:45:45

尽管它只是用于“报告”,但那里仍然有业务/域的含义。我认为你的设计很好。尽管我不会通过存储 OrderLine -> 来处理新要求产品参考。我会做一些类似于您已经对产品名称和价格所做的事情。您只需要在订单行中存储某种产品标识符(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 订单”。

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

Even though it is for 'reporting only' there is still a business / domain meaning there. I think that your design is good. Although I would not handle the new requirement by storing OrderLine -> Product reference. I would do something similar to what you already doing with product name and price. You just need to store some sort of product identifier (SKU?) in the order line. This identifier/SKU can later be used in a query. SKU can be a combination of Store and Product natural keys:

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

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

This way you don't violate any aggregate rules and the product and stores can be safely deleted without affecting existing or archived orders. And you can still have your 'Orders with ProductX from StoreY'.

Update: Regarding your concern about foreign key. In my opinion foreign key is just a mechanism that enforces long-living Domain relationships at the database level. Since you don't have a domain relationship you don't need the enforcement mechanism as well.

李不 2024-12-12 03:45:45

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

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

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

。 href="https://stackoverflow.com/questions/7365913/how-do-read-only-database-views-fit-into-the-repository-pattern/7378058#7378058">这个

In this case you need the information for reporting which has nothing to do with the aggregate root.

So the most suitable place for it would be a service (could be a domain service if it is related to business or better to application service like querying service which query the required data and return them as DTOs customizable for presentation or consumer.

I suggest you create a statistics services which query the required data using read only repositories (or preferable Finders) which returns DTOs instead of corrupting the domain with query models.

Check this

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文