Обʼєкти, які не живуть окремо
Уявіть відгук кандидата на вакансію. У нього є етап (новий, співбесіда, офер), історія змін і підсумок. Ці частини не живуть окремо: після підсумку етап більше не змінюється, а кожна зміна етапу лишає запис в історії. Це і є : правило, яке має бути правдою після кожної зміни.
Агрегат обводить такі обʼєкти однією межею. Усередині неї є : єдиний обʼєкт, через який іде будь-яка зміна. Етап, запис історії й підсумок видно зсередини, але не ззовні.
Межа агрегату збігається з : агрегат зберігають цілком або не зберігають зовсім. Якщо одна дія змінює два агрегати, решту системи повідомляє , а не спільна транзакція.
Вхід лише через корінь
- Агрегат «Відгук» (межа транзакції) містить: Application, Stage, HistoryEntry, Outcome.
- Controller використовує Application (withdraw()).
- Application використовує Stage.
- Application використовує HistoryEntry.
- Application використовує Outcome.
- ReportJob не може змінювати Stage напряму, лише через корінь.
- 1Ззовні тримають посилання лише на корінь. Інші агрегати знають його за ID, а не за обʼєктом.
- 2Будь-яка зміна йде методом кореня з доменною назвою: withdraw(), а не setStatus().
- 3Одна транзакція змінює один агрегат. Решта дізнається про зміну з доменної події.
Агрегат «Відгук» у нашому коді
Тут агрегат «Відгук»: змінює етап, записує подію в історію й дає кандидату відкликати відгук лише доти, доки в нього немає підсумку.
Як агрегати зазвичай псують
Агрегат на пів бази
Компанія тримає всі вакансії, а ті всі відгуки. Будь-яка зміна вантажить тисячі рядків і блокує сусідів.
Натомість: робіть межу такою малою, як дозволяє інваріант. Вакансія й відгук живуть окремо.
Зміна двох агрегатів в одній транзакції
Відкликали відгук і в тій самій транзакції зменшили лічильник відгуків у вакансії.
Натомість: лічильник оновлює слухач події ApplicationWithdrawn, у власній транзакції.
Сетери на все
setStage() і setOutcome() дозволяють будь-кому обійти правило, і інваріант тримається лише на дисципліні.
Натомість: методи з доменними назвами, як withdraw(), перевіряють правило всередині кореня.
Чи потрібен вам агрегат
- Інваріант
- Правило, яке має бути правдою після будь-якої зміни. Наприклад: у відгуку з підсумком етап більше не змінюється.
- Корінь агрегату
- Єдиний обʼєкт агрегату
- Межа транзакції
- Усе, що зберігається атомарно: або цілком, або ніяк. Для агрегату це він сам.
- Доменна подія
- Запис про те, що сталося в домені, у минулому часі: ApplicationWithdrawn. Інші частини системи реагують на неї окремо.
Питання зі співбесід
Так межі не зливаються: не можна випадково змінити чужий агрегат через навігацію по звʼязках, і кожен агрегат вантажиться й зберігається окремо.
Записати доменну подію й обробити її окремо, у власній транзакції. Якщо потрібна гарантія доставки, подія спершу пишеться в outbox у тій самій транзакції, що й агрегат.