Как бороться с разрастанием агрегата (большой агрегат) в DDD?

«Как бороться с разрастанием агрегата (большой агрегат) в DDD?» — вопрос из категории Архитектура, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Разрастание агрегата нарушает его основную цель — быть консистентной границей транзакций. Вот стратегии борьбы с этим:

1. Пересмотр границ агрегата: Часто агрегат становится большим из-за неправильно определенных инвариантов. Нужно задать вопрос: «Действительно ли эти сущности и value-объекты должны изменяться вместе в одной транзакции?» Если нет — это кандидаты на выделение в отдельные агрегаты.

2. Выделение новых агрегатов: Например, вместо Order со всеми OrderLineItem, Payment, Shipment внутри, можно выделить:

  • OrderOrderLineItem как частью).
  • Payment (отдельный агрегат, ссылающийся на OrderId).
  • Shipment (отдельный агрегат, ссылающийся на OrderId). Связь через идентификаторы (ID), а не через прямые объектные ссылки.

3. Использование паттерна «Гость» (Guest) или «Ссылка» (Reference): Вместо включения всей сущности внутрь агрегата, храните только ее ID и минимальный набор атрибутов, необходимых для инвариантов агрегата (как snapshot).

// Вместо этого (сущность Product внутри агрегата OrderLine):
class OrderLine {
    private Product $product; // Product — большая сущность
    private int $quantity;
}

// Используйте это (храните только необходимые данные):
class OrderLine {
    private ProductId $productId;
    private string $productName; // Снимок названия на момент заказа
    private Money $priceAtOrder; // Снимок цены на момент заказа
    private int $quantity;
    // Инварианты агрегата Order проверяются с использованием этих данных, без загрузки полного Product.
}

4. Применение CQRS: Разделите модель на командную (для изменений, с маленькими агрегатами, оптимизированными под запись) и запросную (для чтения, с denormalized данными, оптимизированными под конкретные view). Это снимает нагрузку с агрегата по удовлетворению всех потребностей в чтении.

5. Ленивая загрузка — это антипаттерн внутри агрегата: Если вам кажется, что нужна ленивая загрузка частей агрегата — это верный признак, что границы определены неверно. Агрегат должен загружаться целиком для обеспечения своей консистентности.