开发者

DDD: keep a link to an entity inside an aggregate root, for reporting only

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!


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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜