Ответ
Разрастание агрегата нарушает его основную цель — быть консистентной границей транзакций. Вот стратегии борьбы с этим:
1. Пересмотр границ агрегата: Часто агрегат становится большим из-за неправильно определенных инвариантов. Нужно задать вопрос: «Действительно ли эти сущности и value-объекты должны изменяться вместе в одной транзакции?» Если нет — это кандидаты на выделение в отдельные агрегаты.
2. Выделение новых агрегатов:
Например, вместо Order со всеми OrderLineItem, Payment, Shipment внутри, можно выделить:
Order(сOrderLineItemкак частью).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. Ленивая загрузка — это антипаттерн внутри агрегата: Если вам кажется, что нужна ленивая загрузка частей агрегата — это верный признак, что границы определены неверно. Агрегат должен загружаться целиком для обеспечения своей консистентности.