Я понимаю, что агрегаты должны быть маленькими и защищать инварианты. Я также знаю, что хранение больших коллекций в Aggregates влияет на производительность.
У меня есть вариант использования, который должен защищать свои инварианты, но также приведет к большой коллекции.
Агрегат — это Поставщик, и он может иметь несколько активных Рекламных акций. У каждой Promotion есть PromotionType, StartDate и EndDate. Инварианты:
- в любой момент времени может быть максимум одно продвижение каждого PromotionType
- в любой момент времени может быть максимум 2 акции
public Vendor : Aggregate {
public Guid Id;
public List<Promotion> Promotions;
// some other Vendor props here
public void AddPromotion(Promotion promo) {
// protect invariants (business rules) here:
// rule_1: if 2 promotions are already active during any time between promo.Start and promo.End then throw ex
// rule_2: if during any time between promo.Start and promo.End there is promo with same Type then throw ex
// if all is ok (invariants protected) then:
Promotions.Add(promo);
}
}
public Promotion : ValueObject {
public PromotionType Type; // enum CheapestItemForFree, FreeDelivery, Off10PercentOfTotalBill
public DateTime Start;
public DateTime End;
}
Как мы видим, коллекция Promotions
будет увеличиваться по мере добавления новых акций с течением времени, а срок действия старых акций истечет.
решение 1) Одна из возможностей состоит в том, чтобы сделать Promotion
агрегатом, содержащим VendorId, но в этом случае будет сложно защитить упомянутые инварианты.
решение 2) Другая возможность состоит в том, чтобы иметь задание обслуживания, которое переместит срок действия (EndDate передан) в некоторую таблицу истории, но это вонючее решение IMO.
решение 3) Еще одна возможность состоит в том, чтобы также сделать Promotion
агрегатом, но защитить инварианты в доменной службе, например:
public class PromotionsDomainService {
public Promotion CreateNewVendorPromotion(Guid vendorId, DateTime start, DateTime end, PromotionType type) {
// protect invariants here:
// invariants broken -> throw ex
// invariants valid -> return new Promotion aggregate object
}
}
... но защищая его в PromotionsDomainService (и возвращая агрегаты), мы рискуем состоянием гонки и несогласованностью (если только мы не применим пессимистическую блокировку).
Какой рекомендуемый подход DDD в таком случае?