0% прочитано

Що я змінила б, якби починала DDD-проєкт знову

Якби я починала DDD-проєкт знову, я б менше намагалася спроєктувати все наперед. Раніше фіксувала б інваріанти, обережніше вводила abstraction, чіткіше відділяла читання і менше дозволяла таблицям диктувати форму доменної моделі.

18 жовтня 2026 р. 12 хв читанняЛаравель

Коли я починала працювати з DDD серйозніше, найбільше хотілося зробити "правильно" з першого разу.

Знайти всі bounded contexts, одразу вибрати правильні abstraction, розкласти код по шарах, визначити repository, entities, value objects і services.

Тепер я дивлюся на це інакше. Найкорисніші рішення з’являлися не тоді, коли я намагалася передбачити всю систему, а тоді, коли конкретне правило ставало достатньо зрозумілим.

Якби я починала DDD-проєкт знову, я б не намагалася зробити його "максимально DDD" від першого дня. Я б швидше шукала те, що справді потребує моделювання.

Мій головний висновок: DDD краще росте разом із розумінням предметної області, ніж із наперед намальованою архітектурою.

Я б не намагалася визначити всі bounded contexts наперед

На початку система здається зрозумілішою, ніж вона є насправді. Назви схожі, таблиці пов’язані, сценарії ще невеликі, тому легко провести межу занадто рано.

У матеріалі про межі домену в системі, що росте я писала, що межа стає видимою через різні значення слів, життєві цикли, причини змін та інваріанти.

Тепер я б сприймала початкові bounded contexts як робочі гіпотези, а не як остаточну карту системи.

Межі уточнюються разом із розумінням
Раннє припущення: Product  + registration  + access  + billing Пізніше розуміння: ProductRegistrationAccessBilling Деякі концепції використовують спільні дані.Вони не обов'язково використовують одну модель.

Я б раніше фіксувала інваріанти

Раніше я часто починала з use case: що натискає користувач, який endpoint викликається, які таблиці треба змінити.

Тепер я б набагато раніше ставила просте питання: що в системі не має права стати неправдою після завершення операції?

Саме відповідь на це питання часто підказує aggregate, межу транзакції, власника правила і потрібні перевірки.

Інваріант дає більше інформації, ніж список таблиць
Use case: Activate product  Питання, які я ставлю зараз: Що має бути істинним до активації? Що має залишатися істинним після активації? Який стан має змінитися разом? Яка помилка має відкотити все назад?

Я б раніше визначала власника бізнес-правила

У матеріалі про те, де має жити бізнес-правило я розбирала різницю між controller, service і domain model.

Якби я починала знову, я б швидше переносила правило до поняття, якому воно природно належить, замість того щоб дозволяти йому довго жити в загальному Service.

Це особливо важливо для правил, які викликаються з HTTP, queue, command або cron. Якщо правило залежить від контролера, воно майже напевно стоїть не там.

Я б не створювала abstraction без реальної проблеми

DDD легко провокує на створення великої кількості "правильних" класів: interfaces, repositories, factories, mappers, services.

Частина з них справді потрібна. Але якщо abstraction не відділяє конкретну змінну деталь, не виражає доменне поняття і не спрощує залежність, вона стає церемонією.

Тепер я б вводила abstraction тільки після того, як можу чітко сказати, яку проблему вона вирішує.

Саме тому я більше не створюю repository для кожної моделі. У матеріалі про те, коли Repository справді потрібен у Laravel DDD я описувала Repository як межу стійкості для aggregate, а не як обов’язкову обгортку над Eloquent.

Я б чіткіше відділяла read side значно раніше

Одна з речей, яку я довше тримала разом, ніж варто було, це модель запису і модель читання.

Коли для адміністративної таблиці починаєш тягнути aggregate, додавати relation лише заради колонок і будувати складні eager loading, це вже хороший сигнал.

У матеріалі про Read Model у DDD я писала, що read side може мати власну форму даних без порушення DDD.

Якби я починала знову, я б дозволила собі окремий query object або Read Model раніше.

Різні задачі можуть мати різні моделі
Write side: Domain model- invariants- behavior- state transitions  Read side: Query model- filters- sorting- counts- projections

Я б менше дозволяла таблицям диктувати доменну модель

Laravel дуже природно підштовхує мислити таблицями: model, migration, relation, foreign key.

Для CRUD це часто чудово. Але в DDD таблиця не завжди дорівнює entity, а foreign key не визначає межу агрегату.

Якби я починала знову, я б спочатку описувала поведінку словами предметної області, а вже потім вирішувала, як це зберігати.

Я б раніше відокремила domain language від назв у базі

Назва колонки може бути історичною, технічною або просто незручною. Це не означає, що та сама назва повинна проникати в усю доменну модель.

Коли код починає говорити мовою предметної області, стає легше помічати неправильні межі й приховані правила.

Тепер я дозволила програму відображення або репозиторій перекласти термінологію стійкості в термінологію домену раніше, якщо ця різниця вже має значення.

Я б не робила кожен зв’язок двостороннім

Коли модель будується від ORM, дуже легко додати relation в обидві сторони просто тому, що це можливо.

Але доменній моделі не завжди потрібно знати всі сусідні сутності.

Менша кількість зв’язків часто робить агрегат простішим, а залежності явнішими.

Я б раніше писала доменні тести без HTTP і бази

Такий тест перевіряє правило, а не транспорт
public function test_product_cannot_activate_when_incomplete(): void{    $product = Product::draft(        code: null,        name: 'Example',    );     $this->expectException(        DomainException::class,    );     $product->activate();}

Коли правило легко тестується без framework layers, це хороший знак, що воно справді знаходиться в моделі.

Такі тести також швидше показують, чи ми розуміємо invariant, чи лише повторюємо поведінку контролера.

Я б менше боялася змінювати модель після нового розуміння

Раніше зміна обмеженого контексту, агрегату або назви важливого поняття могла здаватися ознакою того, що початкова модель була "неправильна".

Тепер я сприймаю це інакше. Якщо команда краще зрозуміла предметну область, модель повинна мати право стати точнішою.

Найгірший варіант для мене тепер не зміна моделі, а збереження старої моделі лише тому, що навколо неї вже побудовано багато структури.

У Center я б починала з правил, а не з архітектурної карти

У роботі над Center я поступово прийшла саме до такого підходу.

Найбільше користі давали не наперед створені абстракції, а моменти, коли ми могли чітко сформулювати правило: що є допустимим станом, хто ним володіє, що повинно змінитися разом, а що є лише реакцією після факту.

Якби я починала цей шлях знову, я б зробила ці питання центральними раніше.

Я навмисно не переношу сюди внутрішню карту Center. Для цієї статті важливі висновки, а не конкретні модулі.

Мій новий порядок роботи був би простішим

Менше архітектури наперед, більше перевірки правил
1. Опишіть бізнес-дію. 2. Запишіть інваріант. 3. Знайдіть власника правила. 4. Визначте найменшу межу узгодженості. 5. Зберігайте першу модель простою. 6. Розділяйте читання, коли потреби читання розходяться. 7. Додавайте абстракції лише для реальної межі. 8. Удосконалюйте обмежені контексти, коли мова стає зрозумілішою.

Що я точно не робила б знову

Я б не робила кілька речей автоматично:

  • не намагалася б намалювати всі bounded contexts до появи достатньої кількості сценаріїв;
  • не створювала б repository, interface або service тільки тому, що вони "мають бути в DDD";
  • не дозволяла б контролерам довго володіти предметними правилами;
  • не тягнула б aggregate в кожен список і кожну таблицю;
  • не вважала б foreign key доказом спільного aggregate;
  • не боялася б змінити ранню модель після нового розуміння предметної області.

DDD для мене стало способом ставити кращі питання

Якби я починала DDD-проєкт знову, я б витрачала менше часу на питання "який pattern тут використати?" і більше на питання "яке правило ми зараз захищаємо?".

Менше дивилася б на таблиці як на готову модель. Раніше відділяла б читання. Не вводила б abstraction до появи реальної межі. І дозволяла б bounded contexts змінюватися разом із нашим розумінням.

Для мене зрілий DDD-підхід тепер не означає більше класів. Він означає точніше розуміння того, де живе правило, що повинно бути узгодженим і яку складність справді варто моделювати.

Якби я починала DDD знову, я б проєктувала менше наперед і раніше фіксувала правила. Архітектура мала б вирости з розуміння предметної області, а не навпаки.