Domain-Driven Design: тактичне проектування. Частина 2



Здрастуйте, шановні хабрапользователи! попередній статті ми розглянули стратегічне моделювання з допомогою підходу DDD. У ній було показано, як виділяти концептуальні межі, в рамках яких вирішуються окремі завдання предметної області –
обмежені контексти
.

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

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

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

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

Є кілька стратегій створення ідентифікаторів:

Введення користувачем унікального значення

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

Ідентифікатор генерується додатком

Існують швидкі і високонадійні генератори, які можна використовувати в додатках для автоматичного створення унікального ідентифікатора. Наприклад, в середовищі Java існує клас
java.util.UUID
, який дозволяє генерувати так званий універсально унікальний ідентифікатор (universally unique identifier) чотирма різними способами (time-based, DCE security, name-based, randomly generated UUIDs).

Прикладом такого UUID є:
046b6c7f-0b8a-43b9-b35d-6489e6daee91
. Тобто 36 байтові рядок.

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

Для прикладу можна розглянути такий ідентифікатор:
APM-P-08-14-2016-046B6C7F
.

Тут:
АРМ
– окремий контекст управління проектуванням;
P
– проект;
08-14-2016
– дата створення;
046B6C7F-
– перший сегмент UUID. Коли такі ідентифікатори трапляються в різних
обмежених контекстах
, розробники відразу бачать, звідки вони з'явилися.

Ідентифікатор генерується механізмом постійного зберігання

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

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

Ідентифікатори, які присвоюються іншими обмеженими контекстами

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

Щоб знайти певний ідентифікатор в іншому
обмеженому контексті
, можна для прикладу вказати деяку кількість атрибутів (е-мейл, номер рахунку), які дадуть можливість визначити унікальний ідентифікатор зовнішньої
сутності
, який можна використовувати в якості локального ідентифікатора. З зовнішньої
сутності
в локальну
сутність
також можна скопіювати якийсь додатковий стан (властивість).

Для сутності, зазвичай, крім основного ідентифікатора предметної області, використовується сурогатний ідентифікатор. Перший підпорядковується вимогам предметної області, а другий призначений вже для самого інструменту ORM (Hibernate). Для створення сурогатного ключа зазвичай створюється атрибут
сутності
типу
long
або
int
, в базі даних створюється унікальний ідентифікатор, і він використовується як первинний ключ. Потім включається відображення цього ключа в атрибут з допомогою інструментів ORM. Такий сурогатний ідентифікатор зазвичай приховують від зовнішнього світу, так як він не є частиною моделі предметної області.

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

Для початку необхідно виділити
сутності
в предметної області. Цілком очевидно, що існує
сутність
BankingAccount, і для її ідентифікації напрошується використання номера рахунку accountNumber. Тим не менше цей номер унікальний лише в окремому банку і може повторюватися в інших банках. (Можна використовувати номер IBAN, але цей номер в основному використовується тільки в банках Європейського союзу.) Тобто крім номера рахунку ще можна використовувати сегмент UUID. Тоді наш ідентифікатор буде складатися з
PFM-A-424214343245-046b6c7f
, де:

  • PFM
    – ім'я контексту
  • A
    – Account
  • 424214343245
    – номер рахунку accountnumber
  • 046b6c7f
    – частина з UUID

Ідентифікатор можна використовувати
об'єкт-значення
. Необхідно детально розглянути цей дуже важливий шаблон DDD.

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

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

Дуже рідко
об'єкти-значення
робляться змінними. Щоб заборонити доступ до полів, зазвичай методи установники (setters) роблять приватними, а публічним роблять конструктор об'єкта, в який передаються всі об'єкти, які є атрибутами
значення
. Створення
об'єкта-значення
має бути атомарної операцією.

Для
об'єкта-значення
дуже важливо визначати операцію перевірки рівності. Щоб два
об'єкта-значення
були рівні, необхідно, щоб всі типи і значення атрибутів були рівні.

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

Розглянемо класичний приклад об'єкта значення грошова сума (у багатьох прикладах в інтернеті зустрічається цей клас):

public class Money implements Serializable {
private BigDecimal amount;
private String currency;
public Money (BigDecimal anAmount, String aCurrency) {
this.setAmount(anAmount);
this.setCurrency(aCurrency);
}
...
}

Методи установники тут робляться невидимими, створення об'єкта значення – атомарна операція. Прикладом конкретного значення
{50 000 доларів}
. Окремо ці атрибути або описують щось інше, або нічого конкретного не означають. Особливо це відноситься до числа 50000 і в деякій мірі до доларам. Тобто ці атрибути утворює концептуально
ціле значення
, яке описує грошову суму. Така цілісність концепції предметної області відіграє дуже велику роль. Важливо розуміти, що іменуються типи
значення
і самі
значення
у відповідності з
єдиною мовою
у своєму
обмеженому контексті
.

Давайте перейдемо до наступного важливого шаблоном тактичного моделювання.

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

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

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


Подія (Domain Entity)
Вивчаючи предметну область, зустрічаються факти, які мають особливу важливість для експертів у предметній області. Наприклад від експертів можна почути такі ключові фрази:

  • «Коли… »
  • «Якщо це станеться… »
  • «Повідомте мені, якщо… » або «повідомте мене, якщо… »
  • «У разі… »
Тобто, якщо щось має статися як наслідок іншого окремого дії, скоріше за все необхідно моделювання певного
події предметної області
.
При моделюванні необхідно враховувати, що
подія
– це те, що трапилося в минулому часі. Тому ім'я
події
відображає минуле час, при цьому саме ім'я має присвоюватися відповідно до
єдиною мовою
на
обмеженому контексті
.

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

Для прикладу можна розглянути подія FundsDeposited:


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

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

Для того, щоб
події
можна було публікувати, і, щоб їх можна було обробляти, можна використовувати шаблон
спостерігач
(Observer), або
видавець-читач
.

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

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

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

Існує багато компонентів для передачі повідомлень, які належать до класу проміжного програмного забезпечення (наприклад, RabbitMQ, NServiceBus). Можна також зробити обмін повідомленнями за допомогою ресурсів REST, де автономні системи звертаються до видавничій системі, вимагаючи повідомлення про події, які ще не були оброблені.

Підхід RESTful до публікації повідомлень про
подію
є протилежністю публікації з допомогою типової інфраструктури обміну повідомленнями. «Видавець» не підтримує ряд зареєстрованих передплатників, тому що зацікавленим сторонам нічого не розсилається. Замість цього підхід вимагає, щоб клієнти REST самі запитували повідомлення, використовуючи ресурс URI.

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

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

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

Давайте розглянемо наступний шаблон DDD.

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

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

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

com.bankingsystems

Наступний сегмент імені модуля ідентифікує
обмежений контекст
. Бажано, щоб ім'я цього сегмента ґрунтувалося на імені
обмеженого контексту
:

com.bankingsystes.pfm

Далі слід квалификатор, який ідентифікує модуль саме предметної області:

com.bankingsystems.pfm.domain

Всі
модулі
моделі можна помістити саме в цьому розділі domain. Ось так:

com.bankingsystems.pfm.domain.account
<<Entity>>BankingAccount
<<ValueObject>>AccountId

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

сом.bankingsystems.resources

сом.bankingsystems.resources.view
рівень інтерфейсу
(зберігання уявлень)

сом.bankingsystems.application

сом.bankingsystems.application.account
прикладний рівень
(підмодуль
прикладних сервісів
)

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

Агрегат (Aggregate)
Агрегат
є найскладнішим з усіх тактичних інструментів DDD.
Агрегатом
називається кластер з об'єктів
сутностей
або
значення
. Тобто ці об'єкти розглядаються як єдине ціле з точки зору зміни даних.

У кожного
агрегату
є корінь (Aggregate Root) і межа, всередині якої завжди повинні бути задоволені інваріанти.

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

Інваріант – це бізнес-правило, яке завжди зберігає свою несуперечність. Це явище називається транзакційний узгодженістю, яка є атомарною. Є ще й підсумкова узгодженість. У разі інваріанти, мова йде про транзакційні узгодженості.
Агрегатом
також можна називати кордон транзакційний узгодженості, всередині якої виконуються інваріантні правила, незалежно від того, які саме операції при цьому виконуються.

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

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

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

У рамках однієї транзакції в
обмеженому контексті
має відбуватися зміна тільки одного
агрегату
.

Посилання краще робити з глобальних ідентифікаторів
кореня агрегату
, а не зберігаючи прямі посилання як об'єкти (або покажчики). Таким чином: зменшується пам'ять об'єктів; вони швидше завантажуються; їх легше масштабувати.

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

В якості прикладу агрегату можна привести кредитний звіт:


Кожен кредитний звіт повинен містити дані про ідентифікаційних даних позичальника. Для цього ми зберігаємо зовнішню зв'язок з ідентифікатором
сustomerId
. Сustomer – зовсім інший
агрегат
, який містить інформацію про позичальника (ім'я, адреса, контакти). Інваріантом, припустимо, буде правило перерахунку кредитного рейтингу – в залежності від даних кредитної історії.

Абсолютно неважливо, як саме він перераховується, головне, – щоб виконувалася транзакційна узгодженість всередині
агрегату
. Наприклад, після додавання або заміни запису кредитної історії, показник кредитного рейтингу повинен відразу ж пересчитаться. Це повинна бути атомарна операція. Якщо база даних повинна бути окрема транзакція. Відразу після внесення даних в об'єкти всередині
агрегату
, повинні виконуватися всі інваріанти.

Inquiry
– певний запит на кредитний звіт з боку інших організацій.
Корінь агрегату
є
сутністю
. Він має глобальну ідентичність. Якщо необхідно буде посилатися на цей
агрегат
, можна буде використовувати тільки ідентифікатор
кореня
.

При видаленні
агрегату
звіту, повинні віддалятися
значення
– записи про історію і запитах.

Фабрика (Factory)
Шаблон
фабрика
є більш відомим, ніж інші.

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

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

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

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

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

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

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

Є два типи проектів
сховищ
:

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

Уявити таке
сховище
HashSet<ObjectId,Object>
. У цій колекції виключається повторна вставка одного і того ж елемента. При цьому, отримуючи і змінюючи об'єкт, зміни фіксуються відразу.

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

  1. Неявне копіювання при читанні (implicity copy-on-read) (механізм постійного зберігання копіює щоразу об'єкт зберігання при читанні з бази даних і порівнює закриту копію з клієнтської при фіксації транзакції);
  2. Неявне копіювання при записі (механізм управляє завантаженими об'єктами з допомогою проксі-об'єкта).

Такий механізм, як Hibernate, дозволяє створювати
сховище
, орієнтований на імітацію колекції.

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

У випадку з
сховищем
, орієнтованим на механізм постійного зберігання, не потрібно відслідковувати зміни в об'єктах, а необхідно кожен раз після змін у
агрегаті
фіксувати зміни методом
save()
або аналогічним. Наприклад:


Реалізація
сховища
використовує методи і об'єкти механізму. При це реалізація знаходиться на інфраструктурному рівні, а інтерфейс оголошується в домені.

Тут використовується перший тип
сховища
, орієнтований на імітацію колекції. В ньому обов'язково реалізувати метод
save()
або
put()
. Тільки методи колекції.

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

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

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

Сподіваємося, що стаття допомогла вам просунутися в розумінні шаблонів DDD! Якщо є питання, пишіть у коментарях. З радістю відповімо. І дякую за увагу.

Статтю підготували: greebn9k (Сергій Грибняк), wa1one (Володимир Ковальчук), silmarilion (Андрій Хахарев).
Джерело: Хабрахабр

0 коментарів

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