Ответ
Большой агрегат (Aggregate Root с множеством сущностей и value objects) часто возникает из-за нарушения принципов проектирования границ контекста и инвариантов.
Основные причины:
- Неверно определенные инварианты консистентности. Агрегат должен защищать инварианты только в своих границах. Если вы пытаетесь обеспечить консистентность между сущностями, которые могут изменяться независимо, это признак того, что они должны быть разными агрегатами.
- Страх ссылок между агрегатами. Разработчики иногда включают всё в один агрегат, чтобы избежать использования
IDдля ссылок и обеспечения eventual consistency. - Агрегат как отражение таблицы БД. Это антипаттерн. Агрегат — это граница транзакции, а не схема данных.
- Нарушение принципа единственной ответственности (SRP). Агрегат начинает отвечать за слишком много бизнес-процессов.
Пример проблемы и решения:
Проблема: Агрегат Order управляет и заказом, и инвентарём.
// Слишком большой агрегат
class Order {
private List<OrderLine> lines;
private List<InventoryItem> inventoryItems; // Это должен быть отдельный агрегат!
// ... методы для резервирования товара, изменения его количества и т.д.
}
Решение: Выделить InventoryItem в отдельный агрегат. Согласованность между Order и Inventory поддерживается через доменные события (Domain Events) или saga.
class Order {
private UUID id;
private List<OrderLine> lines;
public OrderPlacedEvent place() {
// ... логика размещения заказа
return new OrderPlacedEvent(this.id, this.lines); // Событие для резервирования товара
}
}
class InventoryItem {
private UUID productId;
private int quantity;
public void reserveForOrder(UUID orderId, int amount) { ... }
}
Правило: Агрегат должен быть как можно меньше, но достаточным для поддержания инвариантов в рамках одной транзакции.