DDD: сохранить ссылку на сущность внутри совокупного корня, только для отчетов - PullRequest
2 голосов
/ 15 сентября 2011

Я занимаюсь рефакторингом проекта с использованием DDD, но меня беспокоит то, что слишком много сущностей не имеют собственного Агрегированного корня.

У меня есть Store, в котором есть список ProductOption s исписок Product с.A ProductOption может использоваться несколькими Product s.Эти сущности, похоже, очень хорошо вписываются в совокупность Store.

Тогда у меня есть Order, который временно использует Product для построения OrderLine s:

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 соблюдает правила.Но я хотел бы добавить требование, которое может нарушить правила совокупности: владельцу магазина иногда нужно будет проверять статистику о заказах, которые включали конкретный продукт.

Это означает, что в основном мы будемнеобходимо сохранить ссылку на Product в OrderLine, но это никогда не будет использоваться каким-либо методом внутри сущности.Мы будем использовать эту информацию только для целей отчетности при запросах к базе данных;таким образом, было бы невозможно «сломать» что-либо внутри совокупности Магазина из-за этой внутренней ссылки:

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

Указывает ли это простое требование, что Продукт становится корнем совокупности?Это также может привести к тому, что ProductOption станет объединенным корнем, так как Product имеет на него ссылку, что приведет к двум агрегатам, которые не имеют никакого значения вне Магазина и не нуждаются в каком-либо Репозитории;выглядит странно для меня.

Любой комментарий приветствуется!

Ответы [ 2 ]

3 голосов
/ 16 сентября 2011

Несмотря на то, что он предназначен только для «отчетности», в нем по-прежнему есть значение для бизнеса / домена. Я думаю, что ваш дизайн хорош. Хотя я бы не справился с новым требованием, сохранив ссылку 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;
}

Таким образом, вы не нарушаете никаких сводных правил, и продукт и магазины можно безопасно удалить, не затрагивая существующие или архивированные заказы. И у вас все еще могут быть ваши «Заказы с ProductX из StoreY».

Обновление: относительно вашей озабоченности по поводу внешнего ключа. На мой взгляд, внешний ключ - это просто механизм , который обеспечивает долговременные доменные отношения на уровне базы данных. Поскольку у вас нет доменных отношений, вам также не нужен механизм принудительного исполнения.

0 голосов
/ 15 сентября 2011

В этом случае вам нужна информация для отчетов, которая не имеет ничего общего с совокупным корнем.

Таким образом, наиболее подходящим местом для этого может быть служба (может быть служба домена), если она связана с бизнесом или, что лучше, со службой приложений, такой как служба запросов, которая запрашивает требуемые данные и возвращает их в виде DTO, настраиваемых для представления или для потребителя.

Я предлагаю вам создать службы статистики, которые запрашивают необходимые данные, используя репозитории только для чтения (или предпочтительные Finders), которые возвращают DTO вместо того, чтобы портить домен моделями запросов.

Проверить это

...