Дизайн, керований доменом, та взаємодія між доменами


10

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

Я натрапив на це питання DDD, і одна з відповідей мене заінтригувала.

Контексти та домени, обмежені DDD?

В одній з відповідей плакат наводить приклад системи електронної комерції з продуктами, принаймні двома доменами:

1) Каталог товарів 2) Управління запасами

Гаразд, це все має сенс, тобто у передній частині електронної комерції ви зацікавлені у відображенні інформації про товар, а не зацікавлені в управлінні запасами.

АЛЕ Ви можете відобразити рівень запасів на веб-сторінці, або ви можете відобразити номер видання інвентарю на складі (уявіть, що ваш інвентар - це книги, журнали тощо). Ця інформація надходить із домену Інвентар.

Отже, як би ти впорався з цим? Міг би ти

a) Завантажте як домен Product, так і агрегати домену Inventory? b) Чи хочете ви зберегти деякі властивості у вашому об'єкті домену продукту для кількості на складі та видання на складі, а потім використати події домену, щоб оновити їх під час оновлення об’єкта "Інвентар"?

Одне заключне питання. Я знаю, що ми покликані забути / ігнорувати наполегливість домену і просто думати про домен. Але для того, щоб подумати про це, у наведеному вище прикладі ми б закінчилися потенційно 2 таблицями БД для каталогу продуктів та інвентаризації продукції. Тепер ми використовуємо той самий ідентифікатор у них, оскільки це той самий продукт. Або ми можемо використати для таблиці 1 рядок та 1 рядок таблиці та просто зіставити відповідні дані на сукупні властивості?

Відповіді:


8

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

Головне, що слід помітити в цей момент, це те, що ви говорите про вигляд, а це означає, що використання несвіжих даних є прийнятним.

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

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

Завантажити як домен Product, так і агрегати домену Inventory?

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

Чи хочете ви зберегти деякі властивості об’єкта домену продукту для кількості на складі та видання на складі, а потім використати події домену, щоб оновити їх, коли оновлення об’єкта "Інвентар" буде оновлено?

"Не перетинайте потоки. Це було б погано".

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

Ви хочете зберегти домени чистими. У додатках, які взаємодіють з доменами, це не так важливо. Так, наприклад, для програми "Інвентар" доцільно викликати службу в додатку для продукту, щоб запитувати деякі конкретні поняття для продукту, які слід додати до представлення. Або навпаки.

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

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

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

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

Або ми можемо використати для таблиці 1 рядок та 1 рядок таблиці та просто зіставити відповідні дані на сукупні властивості?

ЙІК! Ні, не робіть цього. Ви додаєте суперечку щодо транзакцій без будь-якої ділової причини для цього.


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

Я щойно прочитав цю статтю msdn.microsoft.com/en-us/magazine/dn802601.aspx, яка детально використовує події домену для дублювання деяких даних між контекстами. Приклад спільного використання списку клієнтів між службою обслуговування клієнтів та системою обробки замовлень. Це дублювання даних клієнтів у системі замовлень та використання подій для синхронізації даних. Це летить перед тим, що ми тут відповіли. Безумовно, що у зв'язаному зразку контекст замовлення просто потребує ID клієнта, і його можна заповнити з програми, яка має доступ до контексту обслуговування клієнтів.
PendorPaul

Тут ви хочете бути дуже точними у своєму мисленні. Копіювання даних між системами НЕ те саме, що копіювати дані між моделями домену. Щоразу, коли ви бачите "лише для читання [mumble]", це великий натяк на те, що дані не належать до цього домену (навіть якщо програма все ще може про це піклуватися).
VoiceOfUnreason

Так, я теж думав. Зміна міркувального процесу досить складна без "експертів" публікації статей, які каламутять води. Я провів сьогодні, аналізуючи свій домен, щоб спробувати зрозуміти, де лежить мій BC та Aggregate Roots. Я майже там є, але також знаю, що можу вдосконалити та змінити модель, коли їду. Я вже кілька днів застряг у пеклі над аналізами, переживаю, щоб почати писати код, побоюючись, що у мене немає DDD прямо в голові. Я думаю, що найкраще зламати і продовжувати сумніватися в моєму коді та чи правильна модель. Я туди потраплю. Спасибі
PendorPaul

Можливо, я неправильно прочитав, але практика, на яку я вважаю, що ви говорите, називається перенесенням стану, що переноситься подією (Event-Carried State Transfer), і це може бути дуже потужною моделлю, особливо для переглядів інформаційної панелі / таблиці, де вам потрібно відфільтрувати дані та сторінки. через послуги. Ви можете створювати "проекції" (в основному перегляди) з подій домену для керування цими інформаційними панелями. Я використовував його раніше досить успішно. Це може бути дуже потужним інструментом за правильним сценарієм. ІМХО, що тут важливіше, це те, що ви розумієте, що ці прогнози не представляють доменних моделей і не повинні містити ділової логіки.
Йорданія

3

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

  • Ви завантажуєте два об’єкти і представляєте їх дані разом або ви завантажуєте 1 об’єкт, який містить усе, що ви хочете?

  • Чи використовуєте агрегати для відображення речей чи чогось іншого?

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

Рішення a) з вашого Q, здається, підлягає безлічі цих підводних каменів. Варіант b) може бути дійсним, але я використовував би його лише в тому випадку, якщо дані з InventoryManagementБК потрібні для забезпечення інваріантів при мутації Productсукупності. Краще, якщо агрегат містить усі дані, необхідні для перевірки його правил ведення бізнесу після внесення змін, але на стороні читання вони можуть сидіти в будь-якому місці.

Що стосується даних, загальною рекомендацією є надання Bounded Contexts власної бази даних (з причин розгортання та SoC). Ймовірно, вам доведеться використовувати однакові ідентифікатори, якщо ви хочете співставити продукти між двома BC.

Про взаємодію між БК, ви також можете ознайомитись з /programming/16713041/communicating-bet between-two-bounded-contexts-in- ddd


1
Гаразд, це допомагає. По суті, ми використовуємо агрегатні корені та BC, щоб забезпечити послідовність наших інваріантів, і наші дані є дійсними, а операції, які ми хочемо виконати, завершуються в межах нашого агрегату або BC. Що стосується читання та відображення цих даних, ми можемо впоратися з цим окремо і легко, без того, щоб усі агрегати / BC були гідратовані. Зрештою, навіщо нам завантажувати агрегат, коли все, що ми робимо, це відображення даних або у звіті, або на екрані. Це має ідеальний сенс. Дякую.
PendorPaul

Ось де справді світить CQRS. Ви можете просто зробити SQL-запит, приєднатись до таблиць та повернути індивідуальний запит для певного перегляду. Це також очищає ваше репо від безладу методу запитів. Крім того, ви можете навіть копіювати дані на такій службі, як ElasticSearch і запитувати проти неї.
didnotmatter

1

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

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


Добре, тож візьміть щось на зразок Amazon, це складна система зі складною бізнес-логікою. Вони мають управління каталогами та управління запасами. Для управління каталогом їм не потрібні загальні запаси, маючи на увазі найменування продукту, опис, тип, стан тощо. Однак вони показують кількість товарів на складі на першій сторінці магазину. У такому сценарії, коли ви хочете відокремити домени управління каталогами продуктів та товарно-матеріальних цінностей товарів, але вам потрібно відобразити певну інформацію про товарний запас на своїй сторінці продукту, як це зробити?
PendorPaul

Я думаю, що в домені Каталог товарів є вся інформація, необхідна для управління каталогом товарів. Домен "Інвентар" має всю інформацію, необхідну для управління запасами. Однак, коли я хочу показати деяку інформацію користувачеві, і ці дані надходять із 2 доменів, де це робити. Чи просто я завантажую обидва домени у мій інтерфейс і зв’язую властивості, які я хочу показати? Або у мене є якийсь механізм звітування / зчитування, коли я повертаю анонімний тип або DTO, що містить дані, необхідні для мого інтерфейсу?
PendorPaul

Це комічно, озирнувшись на ці старі мої коментарі 11 місяців тому, але, здається, все життя. Ви були абсолютно праві Ейфоричні щодо аспекту читання і запису. Додаток, над яким я працюю, розвивався зовсім небагато, оскільки розвивалося моє розуміння. Зараз я використовую методи CQRS, через Jimmy Bogards Mediatr. Велика гнучкість наявності команд, які взаємодіють з агрегатами, але потім використання запитів та обробників запитів, щоб повернути все, що мені потрібно для відображення, є неймовірною. Зверніть це до переглядів, які викликають ці QueryHandlers, і роз'єднання добре з цим. Спасибі
PendorPaul

@PendorPaul, я думаю, я там, де ти був 11 (зараз 13) місяців тому. Що тебе привело туди, де ти зараз?
Грег Белл

1
@GregBell Вам потрібно змусити змінити налаштування мислення. Я застряг у підході "спроектувати базу даних, побудувати рівень даних, побудувати деяку логіку бізнесу ... тощо". І я був зосереджений на створенні всіх охоплюючих сутностей. тобто "Товар" на веб-сайті електронної комерції, який обробляв все, починаючи від цін, опису, товарних запасів, розташування запасів .... але це стає надзвичайно складним. Підхід з обмеженим контекстом означає в контексті Інвентаризації "Продукт" містить лише інформацію та поведінку для управління запасами. Опис та зображення керуються у змістовному контексті, ціноутворення в контексті ціноутворення.
PendorPaul

1

З моєї точки зору, існують різні визначення поняття "продукт" - кожен обмежувальний контекст має власне визначення поняття "продукт" -домен:

  • У контексті Content-Management-Bounding-Context продукт містить зображення та текст опису.
  • У контексті, що обмежує запаси, продукт має запаси, продавець продукту передає, коли продукт буде доступний
  • У контексті ціни, що обмежує цінність, є правила, скільки може коштувати продукт за кількість.

На додаток до цього я додав би додатковий контекст Shop-Bounding-Context із власним визначенням продукту (відповідна комбінація доменів продукту інших Контекстів обмеження).

Магазин-товар матиме "зображення та текст опису" від вмісту та наявності в "Інвентар", але не "продавець товару" з інвентарю.

Цей додатковий контекст, що обмежує магазин, залежить від вмісту, інвентаризації, ціни, обмежувального контексту.


То як ви створюєте цей магазин-продукт BC? У цьому контексті у вас є посилання на продукт та інвентар до н.е., і ви зволожуєте їх з магазину своєї наполегливості, коли ви завантажуєте магазин-продукт BC, а потім пропонуєте потрібні вам властивості для цих БЦ через свої властивості StoreProduct? Я знайшов цю статтю насправді, яка відповідає принципам того, про що я думав, перехрещуючи події до н.е., але @ guillaume13 вище вказав, що для цілей відображення я можу уникнути BC та просто повернути назад потрібні мені дані. msdn.microsoft.com/en-us/magazine/dn802601.aspx
PendorPaul
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.