Велика спокуса - зробити агрегат «як в реальному світі»: клієнт з усіма замовленнями, замовлення з товарами, товари з категоріями. Вон Вернон у серії «Effective Aggregate Design» сформулював правила, чому так робити не варто.
Правило 1. Агрегат - для інваріантів, а не для зручності навігації. До агрегату входить лише те, що потрібно для перевірки правил, які мають виконуватися миттєво й разом. Якщо правило «замовлення має позиції з сумою, що дорівнює total» - позиції входять. Якщо «клієнт має не більше 3 неоплачених замовлень» - це не обов'язково причина робити всі замовлення частиною агрегату клієнта.
Правило 2. Маленькі агрегати. Великий агрегат:
- завантажується цілком при кожній зміні - повільно;
- блокує конкурентні зміни: два менеджери редагують різні замовлення клієнта, а оптимістичне блокування агрегату «клієнт» відхиляє одну зі змін;
- зростає з часом - клієнт з 10 000 замовлень.
Правило 3. Посилання на інші агрегати - за ідентифікатором:
// погано: агрегат тримає інший агрегат
class Order { private Customer $customer; }
// добре
class Order { private int $customerId; }
Так межі агрегатів чіткі: змінюючи замовлення, неможливо «випадково» змінити й зберегти клієнта. Кожен агрегат завантажується й зберігається окремо.
Правило 4. Між агрегатами - кінцева узгодженість. Правило, що охоплює кілька агрегатів, виконується не в тій самій транзакції, а через доменну подію:
OrderPlaced → слухач (окрема транзакція) → оновити лічильник у Customer
Якщо бізнес погоджується, що лічильник оновиться за секунду, - це правильна межа.
Як знайти правильну межу - питання до бізнесу: «що станеться, якщо ці дві речі будуть неузгоджені кілька секунд?». Якщо «нічого страшного» - різні агрегати. Якщо «гроші списано двічі» - один агрегат чи явне блокування.
Практика в Laravel: зв'язки Eloquent ($order->customer) зручні для читання, але змінювати інші агрегати через них у методах кореня не варто. Читання - через зв'язки й запити; зміни - через корінь відповідного агрегату.
Докладніше в документації: Vaughn Vernon: Effective Aggregate Design