Перейти до основного вмісту

Domain-driven design

· 8 хв. читання
Petro Ostapuk

Domain-driven design (Предметно-орієнтоване проєктування, рідше проблемно-орієнтоване) - це набір принципів і схем, спрямованих на створення оптимальних систем об'єктів. Зводиться до створення програмних абстракцій, які називаються моделями предметних областей. У ці моделі входить бізнес-логіка, що встановлює зв'язок між реальними умовами області застосування продукту і кодом.

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

Стратегічне проєктування

Проєктування на високому рівні абстракції, без технічних нюансів, що здійснюється всією командою - як менеджерами/замовниками, так і технічними фахівцями.

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

Ubiquitous Language (Єдина мова)

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

Ця колективна мова термінів називається - єдина мова. (Ubiquitous Language). Це один з основних і найважливіших шаблонів предметно-орієнтованого проєктування. Це не бізнес-жаргон, нав'язаний розробникам, а справжня мова, створена цілісною командою - експертами в предметній області, розробниками, бізнес-аналітиками й всіма, хто залучений до створення системи. Роль у команді не настільки істотна, оскільки кожен член команди використовує для опису проєкту єдину мову.

Bounded Context (Обмежений контекст)

Дуже важливо розуміти, що в межах предметної області сенс певного терміна або фрази може сильно відрізнятися. Існує певна межа, у межах якої поняття єдиної мови мають цілком конкретне контекстне значення - обмежений контекст (Bounded context). Це друга за значущістю властивість DDD після єдиної мови. Обидва ці поняття взаємопов'язані й не можуть існувати одне без одного.

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

  • У кожному обмеженому контексті існує лише одна єдина мова.
  • Обмежені контексти є відносно невеликими, меншими, ніж може здатися, на перший погляд. Обмежений контекст досить великий тільки для єдиної мови ізольованої предметної області, але не більше.
  • Єдина означає "всюдисуща" або "повсюдна", тобто мова, якою розмовляють члени команди і якою виражається окрема модель предметної області, що її розробляє команда.
  • Мова є єдиною тільки в межах команди, що працює над проєктом у єдиному обмеженому контексті.
  • Спроба застосувати єдину мову в рамках усього підприємства або, що гірше, серед кількох підприємств, закінчиться провалом.

Предметна область (Domain)

Це те, що робить організація, і середовище, у якому вона це робить. Розробник програмного забезпечення для організації обов'язково працює в її предметній області. Слід розуміти, що під час розроблення моделі предметної області необхідно зосередитися в певній підобласті, оскільки практично неможливо створити єдину, всеосяжну модель бізнесу навіть помірно складної організації. Дуже важливо розділяти моделі на логічні розділені предметні підобласті (Subdomain) всієї предметної області, відповідно до їхньої фактичної функціональності. Підобласті дають змогу швидше визначити різні частини предметної області, необхідні для вирішення конкретного завдання

Смислове ядро (Core domain)

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

Простір завдань і простір рішень

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

  • Простір завдань - частини предметної області, які необхідні, щоб створити смислове ядро. Це комбінація смислового ядра і підобластей, які це ядро має використовувати.
  • Простір рішень - один або кілька обмежених контекстів, набір конкретних моделей програмного забезпечення. Розроблений обмежений контекст - це конкретне рішення, подання реалізації.

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

Тактичне проєктування

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

Entity

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

Value Object

Невеликий простий об'єкт, як-от гроші або діапазон дат, чия рівність не базується на ідентичності

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

  • Воно вимірює, оцінює або описує об'єкт предметної області;
  • Повинно бути незмінним (immutable): під час спроби поновлення значення властивостей ми повинні створити й повернути новий екземпляр VO
  • Воно моделює щось концептуально цілісне, об'єднуючи пов'язані атрибути в одне ціле;
  • При зміні способу вимірювання або опису його можна повністю замінити;
  • Його можна порівнювати з іншими об'єктами за допомогою відношення рівності значень. Два VO вважаються однаковими тоді й тільки тоді, коли всі поля VO рівні
  • Воно надає пов'язаним із ним об'єктам функцію без побічних ефектів.
  • Складається тільки з інших VO і примітивів (не може містити сутність або сервіс)
  • Повинен містити в собі логіку само валідації: не можна не валідний VO, виняток має викидатися прямо з методу VO(конструктор або клон).

Domain Service

Використовуючи єдину мову, іменники цієї мови відображаються в об'єкти, а дієслова відображаються в поведінку цих об'єктів. Дуже часто існують дієслова або якісь дії, які не можна віднести до якоїсь сутності або до якогось об'єкта-значення. Якщо існує такого роду операція в предметній області, її оголошують як Domain Service (вона відрізняється від прикладної служби, яка є клієнтом). Є три характеристики служб:

  • Операція, що виконується службою, належить до концепції предметної області, яка не належить жодній з існуючих сутностей;
  • Операція виконується над різними об'єктами моделі предметної області;
  • Операція не має стану.

Domain Event

Подія - це те, що сталося в минулому. Логічно, подія предметної області - це те, що сталося в конкретній предметній області, і те, про що мають бути в курсі і на що мають реагувати інші частини тієї ж предметної області. Мають бути імутабельними (оскільки не можна змінювати минуле). Назва має описувати подію, що трапилася в минулому.

Module(Модуль)

Модулі всередині моделі є іменованими контейнерами для деякої групи об'єктів предметної області, тісно пов'язаних один з одним. Їхня мета - послаблення зв'язків між класами, що знаходяться в різних модулях. Оскільки модулі в підході DDD - це неформальні або узагальнені розділи, їх слід правильно називати. Вибір їхніх імен є функцією єдиної мови.

Factory

Деякі агрегати або сутності можуть бути досить складними. Складний об'єкт не може створювати сам себе за допомогою конструктора. (У книзі Еріка Еванса було наведено приклад: двигун автомобіля, який збирається або механіком, або роботом, але він ніяк не повинен збиратися сам по собі.) Ще гірше, коли передають створення складного об'єкта на клієнта. Так, клієнт має знати про внутрішню структуру і взаємозв'язки всередині об'єкта. Це порушує інкапсуляцію і прив'язує клієнта до певної реалізації (отже, у разі зміни об'єкта доведеться змінювати і реалізацію клієнта).

Краще виконувати створення складних агрегатів або інших об'єктів окремо. Для цього використовуються фабрики. Фабрики - елементи програми, обов'язки якого створювати інші об'єкти.

Aggregate

Агрегатом називається кластер з об'єктів сутностей або значень. Тобто ці об'єкти розглядаються як єдине ціле з точки зору зміни даних. У кожного агрегату є корінь Aggregate Root і межа, всередині якої завжди мають бути задоволені інваріанти.

Усі звернення до агрегату мають здійснюватися через його корінь, який являє собою сутність із глобально унікальним ідентифікатором. Усі внутрішні об'єкти агрегату мають тільки локальну ідентичність, вони можуть посилатися один на одного як завгодно. Зовнішні об'єкти можуть зберігати тільки посилання на корінь, а не на внутрішні об'єкти.

Repository(Репозиторій)

Репозиторій - область пам'яті, яка призначена для безпечного зберігання поміщених у неї елементів. Саме цим є Репозиторій у DDD. Репозиторій використовується для агрегатів. Поміщаючи агрегат у відповідний Репозиторій, а потім витягуючи його звідти, ви отримуєте цілісний об'єкт. Якщо агрегат буде змінено, то зміни буде збережено. Якщо агрегат буде видалено, то його вже не можна буде витягти.

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

Є два типи Repository:

  1. Орієнтовані на імітацію колекцій;
  2. Орієнтовані на механізм постійного зберігання.

Application Layer