Предметно-орієнтоване проєктування (DDD): у чому користь підходу і хто його використовує

Усім привіт! Мене звуть Олександр Книга, я Solution Architect у компанії WebLab Technology.

У цій статті я поділюсь прикладом того, як Domain Driven Design підхід може змінити розробку і привнести виграшну перевагу у сфері створення програмного забезпечення.

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

Предметно-орієнтоване проєктування

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

Наприклад, аналіз 100 проєктів, проведений Standish Group, засвідчив, що причиною 70% доробок був брак знань у предметній галузі на етапах формування вимог та проєктування, і підтвердив, що DDD сприяє взаєморозумінню між бізнесом та розробниками.

За даними Forrester, групи розробників, які практикують ітеративну модель DDD, працюють на 60% швидше, а не витрачають місяці на попередній аналіз.

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

Тож навіщо компаніям потрібен цей підхід, хто його використовує і в чому його суть?

Основні принципи предметно-орієнтованого проєктування

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

  • Перший принцип — визначення пріоритетів предметної моделі. Вона представляє основні бізнес-суб’єкти, поведінку, відносини та правила. Реалізація коду безпосередньо відображає предметну модель, а не навпаки. Модель розробляється ітеративно, а не визначається заздалегідь.
  • Ще один ключовий принцип — розробка універсальної мови. Спільна лексика розробників і бізнес-експертів стандартизує термінологію та знання предметної галузі, усуваючи двозначність і неузгодженість між групами.
  • DDD також містить стратегічний і тактичний етапи проєктування. Стратегічне проєктування зосереджено на високорівневій організації предметної галузі у вигляді обмежених контекстів і підобластей. Тактичне проєктування охоплює шаблони та компоненти реалізації нижчого рівня — сутності, сервіси та репозиторії.

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

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

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

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

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

У контексті DDD стратегічне проєктування є невіддільною частиною розробки програмного забезпечення. Воно містить основні аспекти:

  • Огляд. Стратегічне проєктування починається з огляду проблемного предмета та цінності бізнесу. На цьому етапі досліджуються ключові концепції та процеси, визначаються основні бізнес-потреби та цілі.
  • Простір проблем і простір рішень. Структура стратегічного проєктування визначає ці два основні концептуальні простори. Простір проблем фокусується на дослідженні та аналізі предмета бізнесу, визначенні сутностей, агрегатів, послуг та взаємозв’язків між ними. Простір рішень стосується створення моделі, яка ефективно розвʼязує проблеми, визначені в проблемному просторі.
  • Обмежені контексти. Обмежені контексти — це обмежені підрозділи предмета, що відповідають зонам відповідальності конкретних груп розробників. Кожен контекст визначає свої сутності, агрегати, сервіси та правила. Керування межами контекстів має важливе значення для ізоляції та розуміння різних частин предмета.
  • Основний предмет. Основний предмет — це ядро бізнесу, його найважливіша і найцінніша частина. У межах стратегічного проєктування основний предмет критично важливий, оскільки є фокусом розробки та містить фундаментальні абстракції та бізнес-правила, що визначають функціональність програмного забезпечення.

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

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

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

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

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

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

Один з прикладів використання інструментарію тактичного проєктування — створення репозиторіїв. Репозиторії відповідають за зберігання та отримання даних зі сховища певної сутності чи сукупності сутностей. Вони забезпечують єдиний інтерфейс для взаємодії зі сховищем даних та інкапсулюють деталі зберігання даних.

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

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

Роль обмеженого контексту та універсальної мови в DDD

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

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

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

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

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

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

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

Ключ до нових рішень: що дає DDD підхід і на кого він розрахований

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

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

Сила малих DDD-груп

Предметно-орієнтоване проєктування добре підходить невеликим, автономним групам. Прикладом є концепція «групи на дві піци». Ідея полягає в тому, що група має бути досить малою, щоб прогодуватися двома піцами. Це забезпечує зосередженість, узгодженість і продуктивність.

Ми бачимо, як підхід «групи на дві піци» переплітається з DDD та успішно застосовується такими лідерами галузі, як Netflix (що дозволило їм швидко масштабувати платформу) та Uber (вони змогли гнучко ізолювати інциденти та керувати коливаннями попиту).

Здається, що DDD — це ексклюзивний клуб, членами якого є Netflix, Uber та наша скромна WebLab Technology. Ми в хорошій компанії, чи не так?

На порталі DEV спільноти хтось створив дискусію з питанням: «Як знайти компанії, що дотримуються предметно-орієнтованого підходу до проєктування?».

Щоб знайти DDD практиків, йдіть слідами чітко структурованих діалогів... або просто шукайте нашу групу!

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

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

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

Зокрема, DDD дає змогу мати:

  • Поліпшення зв’язку: універсальна мова дозволяє розробникам і фахівцям бізнесу співпрацювати ефективніше.
  • Відповідність бізнесу: дизайн програмного забезпечення безпосередньо відображає реальні бізнес-процеси та цілі.
  • Гнучкість: модульна архітектура дозволяє легко модифікувати застосунки залежно від потреб.
  • Акцент на користувачі: акцент на предметі дозволяє створювати рішення, адаптовані до потреб користувачів.
  • Ефективність: тісна співпраця з предметними фахівцями забезпечує продукцію, що вирішує реальні бізнес-завдання.

DDD та малі організації: можливі проблеми

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

Однак будьте готові до перешкод, що можуть виникнути, зокрема:

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

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

Впровадження DDD: поступовий старт

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

1. Починайте помалу.

Починайте роботу з DDD помалу, особливо якщо ви новачок або маєте справу з великою системою. Візьміть невелику, некритичну частину застосунка і почніть використання DDD.

2. Постійне навчання.

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

3. Співпраця.

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

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

Взаємодія: зв’язок DDD з Agile

Отже, як проявляється взаємодія DDD та Agile? DDD та Agile мають схожі принципи, що створює підґрунтя для їхньої успішної інтеграції.

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

Зв’язок між DDD та Agile проявляється як взаємодоповнювані стосунки. Так, використання DDD в середовищі Agile може впорядкувати комунікацію, забезпечити кращу відповідність бізнес-вимогам і надати високоякісне програмне забезпечення.

Висновки

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

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

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

Зрештою, як писав Ерік Еванс у своїй книзі Domain-Driven Design: «Для ефективної комунікації код має базуватися на тій же мові, на якій написані вимоги — на тій же мові, якою розробники спілкуються один з одним та з предметними фахівцями».

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному2
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Прочитав статтю і коменти, дозвольте приєднатись до дискусії.

Хвилює те саме питання, що і пана User-a

В реальному житті є така штука називається performance. Підхід «тянемо все з БД в RAM, там міняємо, пишемо все БД» для проектів для яких performance важливий не дуже підходить. Але бізнес crud це зазвичай не стосується.

Тобто окремо виділяти Domain, а окремо Entities та Repositories — це, звісно, гарна ідея (в теорії), а як бути з практикою, коли для приймання певних бізнес рішень треба завантажувати дані, на основі результата завантажувати (або не завантажувати) ще, і при цьому не скотитсь в процедурну локшину?

Давайте розглянемо такий примітивний Task CRUD.

Таска — це description та статус (NEW, IN_PROGRESS, DONE), але є нюанс — хай вони будуть ієрархічними.

Відповідно придумовую юзкейси:

  • витягту таску з ієрархію або без, або тільки з 1-им рівнем сабтасок
  • перевести таску в DONE можна тільки, коли всі її сабтаски вже в DONE
  • або примусово перевести весь граф в DONE

Я зробив це за допомогою JPA lazy-loading, але вкрай цікаво, що думає про це чиста/гексональна/DDD архітектура.

Чи може шановний автор статті або великовчені архітектори пояснити кодом або словами як робити правильно?

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

Питання не про DDD. Точніше від DDD (і то тактичного) тут лише те, що кожна рут таска включно зі своєю ієрархією виглядає як агрегат. І потрібно знайти технічне рішення для забезпечення кінцевого набору можливих мутацій цього агрегату, таким чином, щоб він у цілому залишався у консістентному стані. Ви навели лише декілька юзкейсів. Наприклад, чи можно додати нову сабтаску до сабтаски до таски яка вже done? Як це вплине на статуси? Чи можно повернути сабтаску із done в in-progres? Що при цьому повинно статись з парент тасками? Окреме питання, — скільки юзерів у системі? Чи можуть два юзера змінювати статуси тасок із однієї ієрархії у паралель? Якщо так, завантаження ієрархії в пам’ять, або навіть тримання її в пам’яті під час активної роботи може виявитись меншим злом порівняно з боротьбою з дідлоками на базі, якщо ви будете намагатись зробити це за рахунок підвищення рівня ізоляції (якщо ми розглядаємо реляційні бази). Якщо сотні юзерів можуть змінювати одну ієрархію, можливо взагалі потрібна черга команд. Якщо ієрархія належить тільки одному юзеру, наврядчи у ній буде більш ніж декілька сотень тасок. Можливо має сенс зберігати та читати ієрархію як один документ. Але ж, це все технічна реалізація.

P.S. якщо є зміни у паралель lazy load левела за левелом це погана ідея. Бо у вас різні частини графу можуть належати вже до різних версій ієрархії в цілому. Взагалі lazy load у більшості випадків створює більше проблем ніж вирішує.

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

Якщо я правильно (може неправильно?) почув про ДДТ, то там є догмат «домен відокремлений від ентіті». Тому мені цікаве оте «технічне рішення».

Ви навели лише декілька юзкейсів. Наприклад, чи можно додати нову сабтаску до сабтаски до таски яка вже done?... Що при цьому повинно статись з парент тасками?

Так. Оскільки в моїй реалізації domain === entity, всі ці юзкейси відносно легко імплементуються та юніт-тестуються в TaskTest. Лазання по графу від мене приховує JPA.

Окреме питання, — скільки юзерів у системі? Чи можуть два юзера змінювати статуси тасок із однієї ієрархії у паралель?

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

з боротьбою з дідлоками на базі

Не обов’язково дедлоки, починати буду скоріш з optimistic «lock». Але пропоную цю тему не розвивати.

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

Ієрархія і таски — це просто приклад. Вважайте, що там посилання про проекти, атачменти тощо. Гадаю, в загальному випадку можна прийняти, що читання «як один документ» вдарить по перформансу.

Але ж, це все технічна реалізація.

От мене і цікавить, як виглядає ця технічна реалізація. Чи є в ДДД lazy-loading? Чи для різних задач витягуються різні агрегати?

якщо є зміни у паралель lazy load левела за левелом це погана ідея. Бо у вас різні частини графу можуть належати вже до різних версій ієрархії в цілому.

Тут я звісно погоджуюсь, але, здається repeatable read на MVCC базах + optimistic lock не такий дорогий і вирішить read/write skew проблеми.

Взагалі lazy load у більшості випадків створює більше проблем ніж вирішує.

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

Давайте визначимось з терміном Entity. За контекстом я так розумію що ви маєте на увазі Entity не у сенсі DDD, а модель класів які відповідають структурі БД та мапляться на БД за допомогою ORM. Я пропоную далі називати це Data Model.

Чи є догмою що Data Model і Domain Model повинні бути розділені? Загальна відповідь — ні, це не догма. У простих випадках ви можете мапити Domain Model на базу напряму, і не мати проміжної Data Model.

Але з підвищенням складності моделі у такому випадку у вас практично не буде простору для маневру.

На вашому прикладі, Domain Model може виглядати як Project (root task) яка має колекцію Task. Кожен Task має свою колекцію Subtasks.

А ось на рівні Data Model, це може бути таблиця
Tasks(Id, ProjectId, ParentTaskId, Description, Status)

Наявність ProjectId дозволить швидко за допомогою одного плейн запиту завантажити увесь Project.

Або це навіть може бути таблиця Project(Id, Name, ProjectJson, Version) і відповідна ProjectEntity (яка не повинна виходити за контур Data Access Layer).

Репозіторій приймає і повертає об’єкти Domain Model.
Конвертація Domain Model в Data Model, це внутрішні знання реалізації репозіторію. Зміна концепції зберігання (умовно ви вирішили зберігати ваш Project не в базі а в S3 у вигляді файлу) не повинна вплинути на Domain Model ніяким чином.

Repeatable Read вам жодним чином не допоможе. Бо по перше, щоб заблокувати усі таски проекту за його допомогою, вам потрібно (сюрпрайз) їх усі «прочитати», а по друге, тримати відкритим конекшн до бази між усіма ітераціями lazy load.

Досить умовна схема:

1. Завантажити агрегат.
2. Перевірити те, що команда може бути виконана на поточному стані агрегату.
3. Мутувати агрегат (операція може бути імперативною, де класична Rich Model перевіряє себе та мутує себе, або функціональною, де state може бути виражений за допомогою імутабельної Anemic Model (яка не обов’язково дорівнює Data Model), бізнес операції за допомогою чистих функцій, умовно Project state n+1 = CompleteTask(Project state n, taskId), але ви повинні гарантувати що наступна команда буде виконуватись на Project state n+1).
4. Зберегти зміни.

Якщо у вас є черга команд, п.1 можно виконувати не для кожної команди, а для пачки команд із черги. Після п.4 беремо наступну команду із черги і переходимо до п.2. На схожому принципі побудована т.з. Actor Model.

Команди повинні аплаїтись послідовно, навіть якщо немає черги (або песімистичне блокування ДО завантаження, на період виконання команди, або оптімістичне блокування) інакше п.2 може дати помилковий результат.

Дякую, принаймі для себе я дещо прояснив.

Тут у іншій гілці навели наче непогану базову статтю.

Якщо правильно зрозумів, ДДД/гексональна архітектура кажуть: «Дивіться, як класно! Домен з логікою окремо, репозиторій окремо, хопа — монго на касандру поміняли! Будь яке сховище може бути!»

Будь яке key-value сховище, чую я.

Та ні, таких обмежень немає, репозиторій може й у реляційну БД зберігати, хоч за допомогою JPA/Hibernate, хоч на чистому JDBC.

Рілі, перепитую я? От реально зможете

1. Завантажити агрегат.

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

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

Наявність ProjectId дозволить швидко за допомогою одного плейн запиту завантажити увесь Project.

або сподіваючись

А на практиці у 99% юзерів буде не більше 50-100 тасок.

До статті з її прикладом з Order та OrderItems те саме питання: OrderItems завжди завантажуються? Навіть для completeOrder? У key-value БД може і не проблема весь документ витягувати, але для реляційних БД це занадто дорогий підхід.
До речі, комент під статтею про те саме питає.

Якщо продовжуєте наполягати, що зможете

1. Завантажити агрегат.

з цікавістю послухаю.

Будь ласка, гляньте ще сюди
dou.ua/...​rums/topic/45909/#2721579
dou.ua/...​rums/topic/45909/#2721597

Якщо запропонуєте проксі-класи з lazy-loading-ом в домені використовувати, повернемось до розмови про транзакційність.

Також хочу висловити проміжкову подяку за дискусію.

ДДД/гексональна архітектура

ДДД != гексональна архітектура

хопа — монго на касандру поміняли!

Із практики: хопа і GDPR => хопа і потрібно зберігати частину даних в окремій базі у зашифрованому вигляді. І при цьому об’єкт потрібно зібрати з декількох баз або навіть сервісів. В одному випадки ви лише змінюєте внутрішню реалізацію репозіторію, у випадку з лейзи лоадінг (де ви де факто зав’язані на можливості ОРМ) вам буде дуже боляче.

або сподіваючись

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

OrderItems завжди завантажуються? Навіть для completeOrder?

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

У key-value БД може і не проблема весь документ витягувати, але для реляційних БД це занадто дорогий підхід.

Ну то використовуйте технічні інструменти які більш оптимальні для вашої задачи.

де агрегат в рядок таблиці влізає, а колекції і колекції колекцій.

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

І при цьому об’єкт потрібно зібрати з декількох баз або навіть сервісів. В одному випадки ви лише змінюєте внутрішню реалізацію репозіторію

Може, ми з різними кейсами стикались...

І при цьому об’єкт потрібно зібрати з декількох баз або навіть сервісів.

З одного боку, я усіляко підтримую і заохочую тримати логіку в домені, з іншого — виключно за lazy-loading, бо все інше дорого. Дякуючи JPA/Hibernate, в межах реляційної БД виходить два-в-одному, хоча, звісно, є нюанси (на кшалт, що робити з httpCall всередині транзакції?).

Якщо забути про JPA, то, мабуть, робив би те саме — переводив домен на інтерфейси (по-хорошому, це треба робити зразу) і підсовував проксі-класи.

OrderItems завжди завантажуються? Навіть для completeOrder?
Якщо ви маєте на увазі кінцевий стан, то у вас вже немає мутацій, читайте як хочете використовуючи рид модель.

Ні, ні, тут принциповий момент. completeOrder() — це мутація, виставляє статус.
Викликаючи метод, я ж не повинен знати, які дані йому потрібні?
Тому читаю один й той самий aggregate root? З усім шо там є? Знову повертаємось до lazy-loading

Дякую, здається порозумілись.

Давайте визначимось з терміном Entity.

Так, дякую, це слушне зауваження. Якщо правильно зрозумів, у ДДД домен — це бізнес-суть, ентіті — це домен + id (і, скоріш за все, version), а Data Model — відноситься до репозиторію. Якщо це JPA, то там є свої ентіті, що створює плутанину.

Чи є догмою що Data Model і Domain Model повинні бути розділені? Загальна відповідь — ні, це не догма. У простих випадках ви можете мапити Domain Model на базу напряму, і не мати проміжної Data Model.

Зрозумів, згоден.

Якщо правильно зрозумів, у ДДД домен — це бізнес-суть, ентіті — це домен + id (і, скоріш за все, version),

Не впевнений що ви правильно це розумієте.

Спочатку у сенсі DDD. Entity це частина бізнес моделі, яку вам потрібно відслідковувати і змінювати на проміжку часу що має сенс з точки зору вашого бізнесу. Коли мы говоримо про identity, ми маємо на увазі логічну унікальність з точки зору бізнесу.

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

У ролі логічної унікальності може використовуватись як бізнес властивість, наприклад номер картки лояльності, ключова умова, ця характеристика не повинна змінюватись протягом логічного життя бізнес сутності, так і сурогатний Id (автоінкремент, GUID і т.п.), який формально не є частиною домену, але дозволяє технічно ідентифікувати збережений бізнес об’єкт.

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

Version це так само технічна частина, яка, наприклад може допомогти в контролі паралельного доступу (наприклад реалізації оптимістичних блокувань) інвалідації кешу і т.п. Але наврядчи ваш бізнес оперує поняттям версії учасника програми лояльності. У той же самий час, якщо вам технічно треба мати версію на рівні класів Domain Model, не бачу великого сенсу її не добавити тільки тому що це не відповідає догмам.

Гадаю, в загальному випадку можна прийняти, що читання «як один документ» вдарить по перформансу.

Загальний випадок це абстрактний кінь у вакуумі.

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

Також з точки зору конкретного домену можуть бути логічні рестрікшени з огляду на які технічні проблеми які ви вирішуєте, можуть взагалі не бути проблемами у реальному житті. Умовно, ви вирішуєте як оптімізувати загрузку графа з мільйоном тасок. А на практиці у 99% юзерів буде не більше 50-100 тасок.

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

Архітектура і алгоритми це різні речі.
Оптимізацію можна зробити в реактивному стилі, наприклад коли в сабтасці статус Done, вона звертається до свого Parent та робить інкремент ++subTasksDone. Коли subTasksDone == countSubTasks статус парента Done.

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

Оптимізацію можна зробити в реактивному стилі,

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

DDD, доменний обєкт інкапсулює свою логіку роботи,

А оце мене, власне, і цікавить. Як, наприклад, зробити такий юзкейс — примусове переведення таски (і всього графу її підтасок) в DONE — в стилі DDD?

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

Є намагаюсь проілюструвати, що підхід «завантаження даних з БД в домен, логіка в домені, зберігання змін в БД», IMHO, працює тільки на простих кейсах а-ля «один документ»

А оце мене, власне, і цікавить. Як, наприклад, зробити такий юзкейс — примусове переведення таски (і всього графу її підтасок) в DONE — в стилі DDD?

Навіщо грузити граф?
Класика, коли ви викликаєте Node.UpdateStatus, це також викликає у всіх ChildNode.UpdateStatus всередині Domain Object.
Розумію що ви натякаєте на якісь Bulk Update, але той балк буде ефективнішим коли в вас тих 500 сабтасок. Але судячі з вашої задачі то біт оптимізація, тож в вас є всі шанси лишити красиву DDD ієрархію.

Ні-ні-ні, я не натякаю ні на які Bulk Update, різні варіанти збереження графа в БД або якісь оптимізації.

Саме класичне

коли ви викликаєте Node.UpdateStatus, це також викликає у всіх ChildNode.UpdateStatus всередині Domain Object.

ніяких підвохів!

Але щоб викликати ChildNode.UpdateStatus всередині Domain Object треба, очевидно, його туди якось завантажити, ні?

Але при цьому ви кажете

Навіщо грузити граф?

Можете, будь ласка, пояснити, як це технічно реалізувати?
У JPA реалізації це можливе завдяки lazy-loading-у.

А що каже на це ДДД з розділенням домену та ентіті?

Але щоб викликати ChildNode.UpdateStatus всередині Domain Object треба, очевидно, його туди якось завантажити, ні?

Очевидно що ні. Щоб обновити статус вам треба знати лише Id сабтаски, та статус в який ви її переводите. Вся інша інфа зайва в памяті в вашому контексті.

Щоб обновити статус вам треба знати лише Id сабтаски, та статус в який ви її переводите.

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

Очевидно що ні.

Так для того щоб класично рекурсивно(?)

коли ви викликаєте Node.UpdateStatus, це також викликає у всіх ChildNode.UpdateStatus всередині Domain Object.

мені потрібні id всіх сабтасок (та їх сабтасок і т.д.) — тобто граф.

Чи мені не вдається пояснити проблему, чи я не можу зрозуміти ваше рішення?

Ось мій рекурсивний markDone(), який, з одного боку доменний, з іншого — стоїть на JPA lazy-loading.

А що каже на це ДДД з розділенням домену та ентіті?

Повертаємося до початку.

Архітектура і алгоритми це різні речі.

Яка різниця як воно реалізовано всередині. Тут вже що найменьше 3 оптимізації були описані. І ще можна 100500 оптимізацій придумати, щоб воно взагалі за мілісекунди працювало.
Архітектура DDD це про те, що доменний обєкт Task.UpdateStatus(Done) вміє всередині переводити саб таски в Done. Як він це робить — це вже його проблеми. DDD на це питання не має відповідати. DDD лише відповідає на питання, що всі хто працює з Task — не повині писати зайвого коду для обробки Sub Task на своєму рівні, вся ця логіка інкапсульована в доменному обєкті.
І це все.

Тут вже що найменьше 3 оптимізації були описані.

Давайте, будь ласка, не буде відволікатись на оптимізації, справа не в них.

Архітектура DDD це про те, що доменний обєкт Task.UpdateStatus(Done) вміє всередині переводити саб таски в Done. Як він це робить — це вже його проблеми. DDD на це питання не має відповідати.

Це чудово. Саме це я і зробив з JPA.
Але ДДД каже (чи не каже?), що окремо має бути домен, а окремо робота з БД. От мені і цікаво, як з ДДД буде завантажуватись з БД

доменний об’єкт Task з методом UpdateStatus(Done)

Чи буде він завантажувати при цьому сабтаски (і сабтаски сабтасок)?
Якщо так — це погіршить перформанс для кейсів, коли це непотрібно.
Якщо ні — як тоді виконується UpdateStatus(Done)?

Як в ДДД розв’язується це протиріччя?

Давайте, будь ласка, не буде відволікатись на оптимізації, справа не в них.

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

Чи буде він завантажувати при цьому сабтаски (і сабтаски сабтасок)?
Якщо так — це погіршить перформанс для кейсів, коли це непотрібно.
Якщо ні — як тоді виконується UpdateStatus(Done)?

Хто вам заважає не завантажувати весь доменний обєкт. Часткого кешувати, використовувати проксі патерн. Зберігати каунтер в паренті як я вже приводив приклад, зберігати лише айдішники і тільки їх рекурсивно обходити. Це якось суперечить ДДД ? Від того Task перестав бути доменним обєктом що вміє обробляти свої сабтаски мовчки ? Перестав мати бізнес логіку всередині себе? Наче ні.
То що ми зараз обговорюємо?

Але ДДД каже (чи не каже?), що окремо має бути домен, а окремо робота з БД.

Скажіть, будь ласка, як ви це бачите?

То що ми зараз обговорюємо?

Можете, будь ласка, проілюструвати кодом або псевдокодом:

  • доменний клас Task з методом UpdateStatus
  • ентіті TaskEntity
  • перетворення TaskEntity в доменний клас Task

Можу запропонувати взяти свій репозиторій за основу, але ви, здається, на C# пишете.

Скажіть, будь ласка, як ви це бачите?

Що саме бачу? Якщо взяти весь стек вашого коду на Джава
1. На рівні процесору там все кешується в лінійках кешу
2. На рівні дискової системи, все кешується на рівні контроллера.
3. На рівні бази данних там все кешується в своїх локальних кешах
4. І ось прийшов ваш рівень, рівень доменного обєкту і ви такий, нє нє нє ... це не по принципам ДДД бо база данних має бути окремо від домена. Рілі? Яке відношення має одне до іншого. Любий рівень в вашому стеку спроєктований так як спроєктований, та не суперечить доменній архітектурі, тому що купа доменних обєктів що інкапсулюють в собі купу бізнес логіки.

Можете, будь ласка, проілюструвати кодом або псевдокодом:

доменний клас Task з методом UpdateStatus
ентіті TaskEntity
перетворення TaskEntity в доменний клас Task
Можу запропонувати взяти свій репозиторій за основу, але ви, здається, на C# пишете.

Ця задача просто не цікава, вона академічна.
Напишіть в тасці Update Tasks Set Status = Done where ParentId = Id, головне щоб для клієнтського коду нічого не змінилось. Тобто якщо визиває Task.Done(); то виконувалась вся необхідна робота з сабтасками за кулісами.

це не по принципам ДДД бо база данних має бути окремо від домена.

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

Хто вам заважає не завантажувати весь доменний обєкт.

Ем, а так можна?!

Я правильно, зрозумів, в залежності від потреби/юзкейса завантажувати тільки потрібні дані в доменний об’єкт?

Тобто, якщо в мене є domainObject.someMethod(), ви пропонуєте завантажити тільки ті дані, які потрібні для виконання someMethod()?

А що каже на це ДДД з розділенням домену та ентіті?

DDD каже, що не можна змішувати Domain layer з Infrastructure layer (бази данних, черги і т.д.). І у наведеному прикладі це все трохи не ddd. Але я не експерт в цій темі щоб давати поради. Доречі гарний конкретний приклад.

Ось рекомендують щось таке www.baeldung.com/...​l-architecture-ddd-spring
Хоча завжди вважав що в ddd сервісні класи зайві, і вони лише в підході — анемічна модель + сервіси (Transaction Script).

Це гарна стаття, але, нажаль, не відповідає на мої питання.

У статті

public class DomainOrderService implements OrderService {
    @Override
    public void completeOrder(UUID id) {
        Order order = getOrder(id);
        order.complete();

        orderRepository.save(order);
    }
}

Переваги:

  • домен не прив’язаний до конкретної БД чи навіть сховища, що чудово ілюструє стаття
  • гарно виглядає для простих пласких об’єктів

Недоліки:

  • домен завантажується повністю, незалежно від юзкейса; як це відобразиться на перфомансі — неясно, але навряд чи добре
  • що робити з ієрархіями — відкрите питання (принаймі, поки що ніхто не прийшов і не сказав)
  • рівень ізоляції скоріш за все READ_COMMITED з відповідними phenomas
  • ніхто не заважає робити проксі-класи, але, так розумію, ніхто і не допомагає

Я б зробив (і постійно роблю) (у припущенні, що все прив’язане до JPA)

public class DomainOrderService implements OrderService {
    @Transactional
    @Override
    public void completeOrder(UUID id) {
        Order order = getOrder(id);
        order.complete();
    }
}

Недоліки:

  • домен прибитий цвяхами до JPA. Відповідно, одну реляційну БД на іншу замінити ще якось можна, а от переїхати на Mongo/Cassandra — набагато тяжче (тут, звісно, практичне питання — як воно в реальному житті?)

Переваги:

  • Lazy Loading з коробки JPA, який може покращити перформанс
  • Нема проблем з ієрархіями, циклічними посиланнями, так хоч з чим проблем нема)
  • рівень ізоляції можна підняти до REPEATABLE_READ, або JPA Provider його і так частково зробить за рахунок кеша 1-ого рівня

Ось щоб не сумніватись та не ломати голову («бл*дські ці питання зайобують» © Лесь) я і не використовую ddd, та ще специфіка проектів більш data driven, аніж дуже хитра доменна логіка. І перфоманс роботи зі сховищами важливіший, а чи буде то академічний ddd, чи анемічні моделі з сервісами в яких логіка, то вже не так важливо.

Тому я в цій темі чисто подивитись що люди скажуть.

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

1. Треба подивитсь чи можна вирішити цю проблему більш архітектурно. Скоріш за все на етапі операції (сервісний рівень, use case) ви вже знаєте які дані вам будуть потрібні. А значить й мапити JPA Entity в домен можна тільки частину даних. Ще момент який можна розглянути — сабтаски не обовʼязково повинні бути полем в доменому класі. Їх можна передавати як аргумент метода, що запускає бізнес логіку в доменому класі. Не обовʼязково що цей варіант підійде, але буває.

JPA lazy-loading,

2. Треба усвідомити, що lazy-loading — це всього навсього проксі клас. Вам ніщо не забороняє використовувати подібні проксі у домені, тільки писати доведеться самому.

вкрай цікаво, що думає про це чиста/гексональна/DDD архітектура

3. Але найцікавіший момент в тому що чиста архітектура не забовʼязує вас мати окремо JPA entity клас, окремо домен клас. Вона лише каже про напрям залежностей, сервісні класи можуть використовувати домені, але не навпаки. Тобто ви можете запросто зробити один домений клас з логікою й використати його в JPA. Мапінг можна зробити через JPA XML. Тоді lazy-проксі будуть створені фреймворком.

4. Але якщо згадати що ми всі ці підходи використовуємо не для того щоб собі самооцінку потішити, а все ж таки спростити підтримку коду й подальший розвиток проекту, то йдемо на компроміс й просто спокійно вішаємо JPA анотації на домені класи. Таким чином в нас виходить JPA з rich-model. Принаймні я роблю так, хай не 100% Clean Architecture, але вже краще ніж anemic model.

Думаю так само, немає предмету суперечки, тисну руку

А, ні, з оцим

Скоріш за все на етапі операції (сервісний рівень, use case) ви вже знаєте які дані вам будуть потрібні. А значить й мапити JPA Entity в домен можна тільки частину даних.

Це ж те саме?
...
погодитись тяжче.

Так це воно. Я теж такий варіант не люблю, все ж таки обʼєкт має бути в валідному стані (а наполовину завантажений — не дуже валідний), раз він є в коді, інакше це дуже чревато багами. Це я більше як підводку використав до того, що іноді агрегат можна разбити на меньші класи, завантажувати тільки ті класи, які потрбні. Й тоді в нас в методі буде список параметрів, які на рівні коду описують що треба завантажити з репозиторіїв, щоб коректно викликати цю домену логіку.

Але наперед скажу, в реальному проекті, де вже є якийсь код, це дуже важко зробити. Перейти до JPA + rich model набагто простіше, коли є легасі.

Я би сказав, що справжні ідеї DDD розкриває CMDD в Fractal.
Він данні розглядає як RAW ака любий ієрархічний обьєкт в json, анемічний, який не має ніяких властивостей, типів, тощо: github.com/...​/Document/0000000001.json

Далі він наділяє анемічну модель властивостями. Security, Pagination, Sync, Sort, Validation тощо.
Робить це в паралельних Dimensions. github.com/...​bases/SocialNetwork/Users

На виході в нас анемічна модель User, навколо якої сформовано N layers, для того, щоб будь які операції з основним RAW документом приводили до виконування бізнесс логіки в N layers.

Чому цей підхід з Dimensions який будеє N layers навколо анемічного обєкта над потужний.
1. Код дуже добре читається. Різні прошарки бізнесс логіки не змішуються між собою, а існують, як би в паралельних вимірах (dimensions).
2. Він дуже добре розширюється. Ви починаєте з анемічної моделі, але згодом вона наділяється складною бізнес логікою, де моделі стають rich, без переписування (!) коду.
3. Пайплайн доменної моделі який побудований з Dimensions дуже швидко працює

Таким чином Fractal поєднує в собі гнучкість і простоту використання anemic моделей, які гармонічно еволюціонують в прогресивному стилі до rich доменних моделей.

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

Трохи важко розглядати це як розвиток ідей DDD в першу чергу тому, що оті властивості — «Security, Pagination, Sync, Sort, Validation» — наразі виглядають трохи як вигадана «китайська енциклопедія» Борхеса. Бо яка така властивість «пагінація» (чи «сортування») може бути у юзера? Очевидно, що це має сенс тільки в контексті якоїсь колекції юзерів, і більше того — чи чистої води деталь реалізації доступу до колекції, на яку тим же доменним експертам в принципі начхати.

Твоя багатошарова модель — це не те, як уявляє собі домен середньостатистичний предметний експерт в вакуумі, це якась нова абстракція. До того ж, трохи меньше ніж вся доменна логіка крутиться навколо якогось руху/трансфорамції даних, а твої «властивості» — це про статичний стан (принаймні, так це виглядає наразі). Покажи, як за допомогою твої діменшнів змоделювати якийсь простий і більшості зрозумілий процес — щось «тривіальне», на кшталт проходження заказу в інтернет-магазині (можливо вже є приклади?)

Бо яка така властивість «пагінація» (чи «сортування») може бути у юзера?

Насправді, в фундаменті є потужна математична модель. Люба колекція з json документами, це фактично колекція ієрархічних обєктів. В ієрархії можуть бути інші вкладені обєкти. А над цими списками може бути застосована така властивість як Pagination. Фактично цей dimension каже, що всі клієнти будуть чититати не всі обєкти скопом, а пейджами.

Приклад, фотоальбом. Є документи (зараз один але може бути більше)
github.com/...​hotoAlbum/Photos/Document

{'Photos':['Photo1','Photo2','Photo3']}

А є pagination dimension, який каже що не всі обєкти з масиву Photos ми читаємо, а читаємо порціями по два елемента
github.com/...​/Document/0000000000.json

{
  "Photos": {
    "Page": {
      "Attrs": 2,
      "IsAlign": false
    }
  }
}

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

Покажи, як за допомогою твої діменшнів змоделювати якийсь простий і більшості зрозумілий процес — щось «тривіальне», на кшталт проходження заказу в інтернет-магазині (можливо вже є приклади?)

Це дуже просто, наприклад в нас є документ в колекції Backets

{
  "Client": "Bob"
  "Orders": []
}

В нас є шаблон замовлення, в колеції NewOrder

{
  "Name": "Apple M1",
  "Category": "Notebooks",
  "Price": 1000,
  "Quantity": 1
}

Далі ми пишемо в код

CreateNewDocForArray("NewOrder", "Backets", "{'Orders':[$]}").OpenForm();

Відкривається веб форма, після її заповнення, замовлення додається в Backet (корзину)

{
  "Client": "Bob"
  "Orders": [{
  "Name": "Apple M1",
  "Category": "Notebooks",
  "Price": 1000,
  "Quantity": 1
}]
}

Dimensions на справді дуже багато, більше 50
fraplat.com/...​nsionsPages/Document.html

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

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

Там пан автор живе у сферичній БД в вакуумі, я вже його колись питав чи бачив він таку річ як BPMN. Не бачив. Він не зрозуміє проблематики і сенсу твого питання )

BPMN

Багаторівневе Security, що спадкується, відноситься до нього ? fraplat.com/...​nsionsPages/Security.html

StateMachine для проводу документів по статусам, відноситься до нього?
fraplat.com/...​nsPages/StateMachine.html

Синхронизація, відноситься до нього?
fraplat.com/...​DimensionsPages/Sync.html

Транзакції, відноситься до нього?
fraplat.com/...​onsPages/Transaction.html

Дмитро, я дуже люблю практичні приклади по DDD/CMDD і дуже не люблю демагогію, де купу часу витрачають про наслідування між квадратом та прямокутником, setOwner, activeRecord та інший «rocket science» в якому ти нижче з задоволенням приймаєш участь.
Мене цікавлять як замість 50 тис рядків boiler plate, писати 500-1000 рядків і отримувати більш гнучкі, більш конфігуровані та швидкі аплікації. Якщо в людини є прогресивний погляд, та вона відкрита до чогось нового, що дає дійсно класні переваги — я завжди велкам.
Є код, є приклади, є задеплоєні проєкти. Як ні, то ні, не всім бути реформаторами. Деякі відчайдушно чіпляються за старі технології, навіть якщо вони не дають ніяких переваг.

відноситься до нього?

Нічого з того що ти написав не відноситься.

Гарна риса технічного спеціаліста, приводити приклади, що саме маєш на увазі, що саме хочеш запрограмувати. Наприклад Констянтин зробив як гарний спеціаліст, він свій абзац з запитаннями закінчив прикладом, що хоче побачити Ordering в магазині. І я йому прислав приклад коду, що моделює заповнення корзини з товарами, та замовлення.
github.com/...​SupermarketApplication.cs

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

змоделювати якийсь простий і більшості зрозумілий процес
максимально зашифрувати

Зашифрувати? Серьозно?
Я відповів, що ти не зможеш зрозуміти суть питання, бо не знаєш що таке бпмн, і не розумієш проблеми яку він вирішує.
Проблематика, і одночасно відповідь на питання:
en.wikipedia.org/...​_and_Notation?wprov=sfla1

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

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

en.wikipedia.org/...​rocess_Model_and_Notation

І що, навіть найпростіший проєкт що перевіряє світло по таймеру,
та розсилає сповіщення вже формально підпадає під твою діаграму

upload.wikimedia.org/...​ProcessWithNormalFlow.svg

Timer => Check Status Electricity => If Changed, send telegram message to user group => If failed, retry, try resend messages in case of gateway issues

Подробиці з повним розбором коду проєкту
dou.ua/forums/topic/45479

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

What is a BPMN engine?

A BPMN Engine, or a Process Execution Engine for processes stated in the BPMN notation, is capable of interpreting the specification of a business process expressed in BPMN and executing the symbols of that standard.

Погугли нащо це треба, почитай і усвідом, що твоя БД це не вміє принципово. Тому в реальному світі є клас задач, які не вкладаються в raw data + dimensions, що є наріжним каменем твоєї БД.

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

Фрактал це і є такий движок. На ньому біля 70% відсотків системи конфігуриться, що називається на льоту.

Погугли нащо це треба, почитай і усвідом, що твоя БД це не вміє принципово. Тому в реальному світі є клас задач, які не вкладаються в raw data + dimensions, що є наріжним каменем твоєї БД.

Що конкретно не може принципово? Ти досі не можеш сформулювати приклад, який принципово недосяжний на «бд з діменшинами».
Є облікові системи на 300+ таблиць які повністью модулюють твої BPMN на Фрактал, розсилки, проходження по статусам, тікети і багато іншого.

я дуже люблю практичні приклади по DDD/CMDD і дуже не люблю демагогію, де купу часу витрачають про наслідування між квадратом та прямокутником, setOwner, activeRecord та інший «rocket science»

activeRecord в DDD

Сразу чувствуется знаток предмета дискуссии.

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

Про DDD треба знати її історію, щоб розуміти переваги і недоліки.
А вона сягає у ті часи коли була віра у
— ретельні аналіз і проектування
— у всемогутність академічного ООП

Проблеми ж тому виникають відразу, у сучасному пост-аджайл світі

Перший принцип — визначення пріоритетів предметної моделі

Для цього треба обізнанні і досвідченні бізнес-аналітики. І час, десь до 20% від загального, коли — нічого не тільки не пишеться у коді, а навіть архитектура не малюється.
А тільки — сбір і аналіз вимог. Узгодження їх.
Нещодавно послухав подкаст з Вігерсом:
«Tech Lead Journal»
#133 — Software Requirements Essentials — Karl Wiegers
techleadjournal.dev/episodes/133

контрольне питання колись задав бізнес-аналітику на проекті
ви читали «Разработка требований к программному обеспечению»?
контрольне питання 2 — а хто уважно послухає хоча б цей подкаст? «кому воно треба оце все...»

Ще один ключовий принцип — розробка універсальної мови

Саме так, це ключовий принцип, який буде проігноровано у переважній більшості випадків.
Бо він вимагає дуже вправній комунікації вже не просто між замовниками і аналітиками, а між всьома.
Причому, навіть без формалізації (коли ця «мова» документується):
в узгодженні цієї мови повинні приймати участь як мінімум:
замовник
аналітик
архітектор
тех ліди команд розробтки
ліди тестувальників
девопс

це ж вони повинні — «балакати» єдиною мовою.
Скільки часу=грошей на це буде виділено?
Які процедури змін цієї мови і переузгодження? бо точно ж треба буде — змінювати.

DDD також містить стратегічний і тактичний етапи проєктування.

і цей пункт тільки після виконання перших двох.
без них — це буде профанація, типова:
архітектор намалював, деви фік що зрозуміли, і зробили як знали.
бо, як у подкасті не буде виконано:
Practice #1 — Understand the Problem Before Converging on a Solution

Про «всемогутність академічного ООП»
Критики за роки вже набралося ой чимало, не бачу сенса копіпастити.
Скажу тільки що я на власному досвіді пройшов шлях від запеклого фаната у 90их, до фана anemic model, data-driven development, «тримай raw data як найдовше, бо вони першоджерело, а семантика(суть, зміст) даних може і буде різною у різних частинах системи», та
ніяких domain класів і їх ієрахій у коді! Класи повинні бути рівнем абстракції (а то й двома) нижче за domain, і являти собой елементи Lego з яких будуть збиратися речі з domain. І — перезбиратися. Оце другє, переосмислення, перезбірка, переписування, вже виникне після першої третини розробки! і тому ця властивість — можливість змін стане пріорітетною.

«тримай raw data як найдовше, бо вони першоджерело, а семантика(суть, зміст) даних може і буде різною у різних частинах системи», та
ніяких domain класів і їх ієрахій у коді!

Шкідливі поради на доу :)

Технічно можливо всі об’єкти замінить на
Map<Object, Object>
куди складать пропертя, функції і т.д.
Просто, немає обмежень, не треба паритись за «зайві класи».
Чи буде побудований на такому принципі проект життєздатний?

А ви отак робите, да?
Ох не хотів би я розбиратись з вашим кодом «ніяких домен классів».

Шкідливі поради на доу :)

та де ж, DDD і академічне ООП постійно реанімують :)
теоретиком воно тіпа круто бути.

А ви отак робите, да?

З демагогігічними тезами — зробити собі солом’яне опудало і вимагати від когось пояснення що це за потвора — стараюсь не дискувувати.

Ох не хотів би я розбиратись з вашим кодом «ніяких домен классів».

А кому цікаво що ви собі понадумали, не бачивши згадані підходи у купі відомих проектів?

«не хочете» з звоїм опудалом розбиратись? ну не розбирайтесь :)
чи не створюйте собі опудал. то ваша ж вава в голові :)

З демагогігічними

Ніякої демагогії — я сприймаю ваші слова буквально.

зробити собі солом’яне опудало

так ви самі його робите:

ніяких domain класів і їх ієрахій у коді!

...бо такий код «опудалом» і буде.

Отож я і кажу, ви не створюєте доменних класів, значить дані передаєте в Map[Object, Object].
Бо все інше буде нести в собі сліди домену.

Отож я і кажу, ви не створюєте доменних класів, значить дані передаєте в Map[Object, Object].

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

Бо все інше буде нести в собі сліди домену.

нє, не буде.
хто ж буде навіть у випадку «Map[Object, Object]» називати ту змінну скажімо listUsers.
тільки var3452!

ну щоб ніякого сліду не було домену!
Ви ж доводьте свій абсурд до кінця, не зупиняйтесь на півдорозі :)

Я ж знаю як я роблю

Ну то може розкажеш детальніше? А то в першому пості ти описав якісь новітні підходи однією строчкою, не дуже вдалою, а зараз «я знаю но нікому ні скажу»

головне в темі розкрив:
дві головні проблеми DDD.

якісь новітні підходи однією строчкою

цім підходам років як 20.
я їх зустрічав у купі проектів, докладів на конференціях і т.д.
на них і вчився.

інше на доу розкривав роками, була ідея навіть пройтись, і оформити у цикл якийсь статей, але... це настільки масова практика, що хто її бачив — на доу теж про них писав.
зі світу .NET теж. (а саме там DDDшників найбільше)

а зараз «я знаю но нікому ні скажу»

та їх не хочуть знати тільки догматики :)
їх давно кому треба — знають та використуовують.
в мене ніякого секрету немає. просто — погляд на відоме і масове — трохі з іншого боку.

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

зустрічав у купі проектів, докладів ... масова практика ... кому треба — знають

Слів багато, сенсу ноль. І хто ще демагог?

ніяких domain класів і їх ієрахій у коді!
цім підходам років як 20.

Якесь стародавнє таємниче кунфу кода, відоме одночасно всім і нікому, пояснити ж суть «великий магістр» не може. Без коментарів )

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

хочете предметно говорити — то покажіть отой код, що вам не сподобався.

покажіь мій код, з яким ви не хочете працювати.

Ну... це все дуже суб’єктивно. Хтось дивиться на код Linux Kernel та не хоче з таким працювати. Комусь будь-який ООП код здається равіолі.

Тут забавно інше.
Людина каже «біле — то буре а круг — це квадрат», а коли їй кажуть що це брєд а не ДДД починає казать «мій код дуже хароший тільки вам я його не покажу і не розкажу а шоб довести мені шось покажіть мені мій код який я вам не показував». Мене насмішило )

Мене насмішило

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

Та це зрозуміло.

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

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

Сергій нам пише:

ніяких domain класів і їх ієрахій у коді!

Усвідомте цю сентенцію в повному обсязі по RAW.
Для усвідомлення давайте візьмем хелловорд-левел:
Person owner = new Person(name);
Cat cat = new Cat();
cat.setOwner(owner);

А тепер уявіть той самий (у функціональному сенсі) код але без створення класів Cat та Person. Бо це ж доменні класи, а їх писати ми не повинні бо

ніяких domain класів і їх ієрахій у коді!

ну шо, як вам те, що уявляється?

Коли я сам був джуном в самому початку 2000х, я встиг пописати досить багацько коду бізнес-логіки без будь-яких доменних класів. І ваш приклад виглядав приблизно ось так (прохання вважати це псевдо-кодом, а не реальною реалізацією на якомусь конкретному фреймворку, бо я вже не памʼятаю багато специфіки):

dataSource.beginTransaction();

DataRow owner = owners.add();
owner["name"] = name;
owners.update()

DataRow cat = cats.add();
cat["nickname"] = "Barsik";
cat["owner"] = owner["id"];
cats.update();

dataSource.commitTransaction();

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

Та навіть з ORM, якщо умовний клас Cat не містить в собі логіки складнішої, ніж setOwner, то це ІМХО все ще ближче до transaction script, ніж до DDD...

І ваш приклад виглядав приблизно ось так

Так, саме так.
І це — смерть, п****ц, тлєн, мрак і жопа.

У мене таке враження, що Сергій саме оце вважає за ДДД, і це і смішно і сумно.

1. а чому setOwner у класа Cat? а не addStuff у Person?
2. якийсь код отримує 2 структури у одній є поле owner, у іншій stuff[] - чи треба нам для зміни цих структур, для доменної дії — «Add a cat to person’s stuff» створювати класи Cat, Person, StuffItem?
3. у нас є класи Cat і Person. У одного є метод persistentSave у іншого renderList. Це методи з domain? Це в домені кіт вміє писати кудись у папірець, а персона перемальовує список своїх котів у записах ветеринарної клініки? Тобто це класи — domain model?
4. У нас є клас LivingObject де атрібут kind є стрінгом ’cat’,’dog’,’human’ а метод setBelongs викликає метод actIt об’єкта класа RelationType. Які будуть причини створювати класи Cat та Person?
5. у нас є інтерфейси Cat і Person. Нам точно, завжди треба класи CatImpl і PersonImpl?
6. і т.д.

ніяких domain класів і їх ієрахій у коді!

Назвати клас Cat — не означає що цей клас стане класом domain model.
domain класи створюються не просто їх назвами.

а чому setOwner у класа Cat?

Тому що це хеловорлд для приклада.

у нас є класи Cat і Person.

У вас їх не повинно бути — ви самі це сказали.

У одного є метод persistentSave у іншого renderList

Ну, якщо ви їх по дурості туди запхнете — то будуть. У вас. У мене ні.

Це в домені кіт вміє писати кудись у папірець

...і прочій брєд, який ви вивели з попереднього речення. Чому там мають бути ці метода — відомо тільки вам.

У нас є клас LivingObject де атрібут kind є стрінгом ’cat’,’dog’,’human’

Тобто все такі у вас повна деградація доменної моделі до Map[Object, Object]. Це лол.

Назвати клас Cat — не означає що цей клас стане класом domain model

Тобто класс Cat, який описує фактично існуючого в реальному світі кота — це в вашому «ддд» не об"єкт домену. Ок. Панятна.

Загалом дуже смішно шо з такою кашею ви взагалі шось розповідаєте про ДДД.
А статті напишіть. Це буде як мінімум забавно.

У вас їх не повинно бути — ви самі це сказали.

і навів приклади — коли їх і не буде.

У мене ні.

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

Чому там мають бути ці метода — відомо тільки вам.

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

Тобто все такі у вас повна деградація доменної моделі до Map[Object, Object].

а у вас архітектурна астронавтика, створення нафік нікому нікому не потрібних абстракцій :)

Тобто класс Cat, який описує фактично існуючого в реальному світі кота

реального світа не існує. Це ази європейської філософії.
На якій грунтується наприклад наукова методологія.

Ніхто і ніколи не описує кота у реальному світі. Бо це титанічна і нікому не потрібна праця-таким описом неможливо буде користуватись для практичної діяльності.
Кожен домен — описує тільки абстракцію, модель кота, його властивостей, які цікаві для домену. Це вже від класики типа Буча, «Об’єктно-орієнтований аналіз та проектування». Там навіть на рівні жартівлих ілюстрацій це показано, можна й не вміти читати, а второпати.

Загалом дуже смішно шо з такою кашею ви взагалі шось розповідаєте про ДДД.

На взаєм. В мене підозра що ви як згаданий аналітик — навіть не розумієте про що мова, а регочете над власним невіглаством.

класс Cat, який описує фактично існуючого в реальному світі кота — це в вашому «ддд» не об«єкт домену

Але тут я дійсно скоріше згодний із Сергієм. Бо якщо взяти, наприклад, ActiveRecord, то там теж буде клас Cat, от тільки це буде data class, який єдине, що вміє — це зберігати себе в базу та вичитувати з бази (і це ще якщо я не плутаю розподіл обовʼязків в ActiveRecord)

А ось уся доменна логіка буде деінде, а це й є та сама сумнозвісна Anemic Model, яка вважається майже не «найзлішим» антіпатерном у DDD.

Ось навіть цитату самого Фаулера знайшов з цього приводу:

In Martin’s seminal P of EAA book (2002), a Domain Model is defined as ’an object model of the domain that incorporates both behavior and data’. This clearly sets it apart from Entity Objects, which are object representations of only the data stored in a database (relational or not), while the behavior is located in separate classes instead.

При цьому, anemic model (або, якщо хочеш, розподілення на entity classes та operation classes a.k.a business services) — не є якимось злом сама по собі. Для цілої купи застосувань, де власне бізнес-логіка доволі примітивна (на рівні вказати власника кота Васьки) — більшого й не треба.

DDD ж створювали для випадків, де саме бізнес-логіка дуже складна, і вимагає детального опису та моделювання. Ризикну припустити, що саме в цих випадках «канонічне» OOD / OOP як раз може добре працювати як засіб виразу цієї моделі в коді.

То ж, можливо, в проектах, які ви перероблювали за «папередніками», склалася саме така ситуація, коли попри складність бізнес-логіки початково був помилково обраний підхід з «анемічними» entity classes та концентрацією бізнес-логіки в operation classes. Але це говорить тільки про невідповідність обраного підходу до наявної задачі, і ні про що інше.

якщо взяти, наприклад, ActiveRecord

А якщо не взяти ActiveRecord? Мені от наприклад абсолютно пофігу шо там в ньому

от тільки це буде data class, який єдине, що вміє — це зберігати себе в базу

Ну так не робіть дата клас. Зробіть його доменним.
Не хочете називати «збереженого кота» котом — не називайте його так.
Зробіть

class Cat {

     static class Storable extends ActiveRecord {
           ...
     }

     Cat(Cat.Storable s) {
          ....
     }
}
Або як хочете інакше, словом інкапсулюйте обрану технологію в доменну модель. Бо всі ці поділи на «нє нє нє це домен клас» «нє нє нє це дата клас» насправді існують тільки в голові пуристів.
інкапсулюйте обрану технологію в доменну модель

Для цього, спочатку, повинна існувати саме доменна модель. Якщо класи типу Cat не мають в собі бізнес-логіки, то це НЕ доменна модель в термінах DDD. Безвідносно до того, яка саме технологія зберігання даних в них інкапсульована.

Ну так не робіть дата клас. Зробіть його доменним.

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

Якщо класи типу Cat не мають в собі бізнес-логіки

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

І тут ми переходим до наступного хибного твердження:

то це НЕ доменна модель

Знов пурізм? Доменна модель — будь яка структура коду яка є абстракцією на реальний бізнес-світ. Якщо ви пишете ОС для атомного реактора, то погоджусь, class Cat не є частиною домену. Але якщо ви пишете застосунок про домашніх тварин, то, якщо ви хочете працювати в ДДД, то ви обтяжені двумя ідеями:
1) якщо в вашому домені є таке поняття як «кіт» ви повинні мати в коді відповідну їй абстракцію class Cat.
1) сам факт існування в вашому коді class Cat є іменованою тотожністю бізнес-поняттю «кіт», тобто, частиною домену. І ви маєте розглядати цей class Cat як важливу доменну абстракцію.

в термінах DDD.

І до цього. А хто і де сказав що таке «терміни ДДД»? Їх затвердили якимось стандартом?
Бо якщо Джон Доу написав у своїй книзі «моє ДДД» що якщо в классі немає методів логіки то це не ддд, то це не «терміни ддд», це лише терміни книги Джона Доу.
А правда тільки в тому що ДДД це концепція, і критеріїв або стандартів вона не має, зате має купку авторів, які чомусь видумали якісь свої, суб’єктивні стандарти і критерії, які не є частиною ДДД, а є лише їхньою авторською видумкою на тему ДДД.

ДДД це не про те щоб наляпати DTO класів в найпростішій задачі.
ДДД це про те щоб побудувати N layers, де це справді потрбно.
Тобто обєкт обростає кількома прошарками бізнес логіки.
Доречі, гарний приклад є в ігровій індустрії. В них ігрові персонажі та предмети — це фактично доменні обєкти, з купою своєї внутрішньої логіки. А ті хто цим всім оперує — це сценарій, скрипти, які пишуть гейм дизайнери ігрових рівнів. Це класичний приклад DDD, а не твій копійчаний круд з котом.

Доменна модель — будь яка структура коду яка є абстракцією на реальний бізнес-світ

неправда у DDD і підходів з яких вона виросла.

А правда тільки в тому що ДДД це концепція

в якій доменна модель — поза кодом, а код повинен її чітко реалізовувати.
причому для цього треба ще й «універсальна мови». Це відокремлений крок у методі, який відрізняє DDD з інших.

а коли «будь яка структура з назвою Cat» є доменним класом — то тоді
будь що є — DDD

P.S.
а, забув, бо провалилось у «неусвідомлене знання»:
об’єктно-орієнтоване програмування у загальному випадку не тотожнє об’єктно-орієнтованому проектуванню(моделюванню). А те що я називаю «академічним ООП» грунтується на їх тотожності. Ця тотожність можлива, але вимагає набагато більш зусиль, чим просто назвати щось у коді назвами з проектування(моделювання).
У DDD ці зусилля винесени у перші два принципи.

Без виконнання яких — скільки класів Cat не роби — навіть довести що це класи з domain неможливо, бо немає єдиного і узгодженного опису того — а що такє Cat у нашому домені?

Тому, я б замінив

код але без створення класів Cat та Person

на
якщо в коді швиденько з’являються класи Cat та Person — то вони не будуть доменними класами.
А фантазією про домен того хто їв додав. Це звісно — програміст. Якій, зазвичай, знаходиться від замовника декілька поверхами нижче по ієрархії та компетенції у домені. І не часто має досвід розробки інф систем для цього домену. А буває — його досвід ще й підведе, коли він написав декілька систем для вет клинік, і от, треба система для перевізника. Бо Cat у цих доменах — різний.
і про — чимало класичних книжок про проектування і пишуть.

про

Бо це ж доменні класи

а це — НЕдоменні класи. хоча назва обрана однакова.

transaction script

не обво’язково він.
Дуже вже старий холівар Rich model vs Anemic model щоб знову його починати.

Суть же про raw data в тому що — що вони джерело істини, а не абстракції. А абстракції — ще й потічуть, обов’язоково потічуть.
Абстракції — то інтерпретація даних. Бачення їх змісту.
І чим більше ця інтерпретація захардкожена — тим негнучшкіше і зарозуміліше стає код інформаційної системи.

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

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

Як у прикладі — хто чий нащадок — квадрат прямокутника, чи прямокутник квадрата?
І цікавіше що для створення програми — цей зв’язок навіть як буде з’ясованим, у коді взагалі може виглядати просто як атрібут класа Circle

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

цей зв’язок навіть як буде з’ясованим, у коді взагалі може виглядати просто як атрібут класа Circle

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

Ну, якщо код будете писать ви

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

Платіж буде підтипом гаманця

бачив не раз подібне.
і на 99% впевен — у вас в коді такого ж повно.
якщо ви не розумієте що такє доменні класи — то ви й не зможете їх створити.
а з того що ви мені закидаєте — ви й гадки не маєте.

Суть же про raw data в тому що — що вони джерело істини

А програмувать напряму ноліками і одинчками не пробували? Вони ж єдине джерело істини! Ато понапридумували оцих всратих мов високого рівня.

А програмувать напряму ноліками і одинчками не пробували?

не пробував, бо немає сенсу. це не дає значущих переваг.

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

Статті «Чому ООП провалилось» як і дискусії після неї — багато років.
Якщо ви такий молоденький що не в курсі — то просто раджу потратити на вивчення питання деякий час, перед тим як з аплобмом займатись демагогією.

От, до речі, да — скажіть мені як сферичній сторонній особині у вакуумі:
ДДД — це про загальний філософський підхід до вирішення питань,
чи це про використання конкретних шаблонів / фреймворків?
Бо документацію мені звісно ж читати лінь, але в усяких оглядових статтях завжди набігають дотнеччики і аргументують конкретними аргументами зі свого ентіті фреймворк і своїх дотнетівських шаблонів.

Чи буде ДДДшним підхід, коли ми з замовником узгодимо терміни (мову), операції, правила, об’єкти. А далі я на пехопе буду фігачити вульгарний MVC, де оперуватиму методами і об’єктами, котрі ми узгодили раніше?

ДДД — це про загальний філософський підхід до вирішення питань,

Так

чи це про використання конкретних шаблонів / фреймворків?

Ні

набігають дотнеччики і аргументують

Ссаним ганчір’ям по мордам

Чи буде ДДДшним підхід, коли ми з замовником узгодимо терміни (мову), операції, правила, об’єкти. А далі ...<не важливо>... я оперуватиму методами і об’єктами, котрі ми узгодили раніше?

Так

набігають дотнеччики і аргументують конкретними аргументами зі свого ентіті фреймворк і своїх дотнетівських шаблонів

Взагалі-то, у Microsoft колись був офіційний гайд по DDD:
devblogs.microsoft.com/...​nd-sample-app-free-ebook

Та й зараз того ДДД у майкрософта повно
www.google.com/...​&sourceid=chrome&ie=UTF-8

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

Також буде вічна ділема, де розміщувати самі методи: у доменному обʼєкті — тягнуться зайві залежності, у сервісі — а нафіг тоді доменний обʼєкт.

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

а чого не вистачає і в яких мовах програмування?

Взагалі то ДДД це про висловлювання бізнес різонінгу у коді а не про парадигму програмування.

Умовно BulkUpdatePaymentsStatuses (сугубо технічна назва, апдейтимо статус але фіг його знає чому, сказали потрібно на учора от і апдейтимо, можемо хоч з Paid у New, нам не шкода) vs SubmitPaymentsToProcessor, де вже виражена конкретна бізнес операція конкретного процесу і інкапсульовані перевірки (мабуть ми не хочемо сабмітити транзакцію по другому колу).

Чомусь усі вважають что обов’язково DDD==OOP==Reach Model. OOP це тільки одна з технік яка може бути корисною (якщо вміти її готувати). Але не гірше, а іноді і краще, це можно і на FP зробити.

Чомусь усі вважають что обов’язково DDD==OOP==Reach Model.

бо воно продовжує все ті ж ідеї. В нього немає ніякого ноухау. Це все — дискусії про підходи 90их.

Взагалі то ДДД це про висловлювання бізнес різонінгу у коді а не про парадигму програмування.

висловлювання бізнес різонінгу у коді — це і є парадигма програмування :D

Парадигма це лише інструмент висловлювання, такий самий як мова програмування, фреймворки, патерни тощо. Як саме цей інструмент використовувати, — це вже інше питання. Як було сказано вище, DDD це про філософію. Хоч з Rich Model, хоч з Anemic Model, хоч з OOP, хоч з FP, хоч з CQRS на додачу з Event Sourcing, хоч без можно написати код так, що буде виглядати що основна мета бізнесу це Add, Update та Delete строк у БД, а можно намагатись висловлювати бізнес процеси, звісно на тому рівні абстракцій та з тим рівнем деталей, який нам важливий с точки зору задачі, яку ми вирішуємо. Events, Commands можуть мати бізнес сенс а можуть бути у стилі UpdateRowCommand, SomeRowUpdatedEvent і т.п.

Взагалі, якщо брати книгу Еванса, там лише декілька розділів можно напряму притягнути до ООП. Але 80..90% статей про DDD пишуть нажаль ті, хто тільки початок книги і прочитав.

«Raw Data», це, до речі, також абстракція, яка містить ті дані та під тим кутом зору які важливі для конкретної задачі а не описують «весь всесвіт».

«Raw Data», це, до речі, також абстракція, яка містить ті дані та під тим кутом зору які важливі для конкретної задачі а не описують «весь всесвіт».

Звісно що так.

Взагалі, якщо брати книгу Еванса, там лише декілька розділів можно напряму притягнути до ООП. Але 80..90% статей про DDD пишуть нажаль ті, хто тільки початок книги і прочитав.

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

Усього вистачає, просто виходить костильно.
Я кажу зараз не про ДДД у широкому сенсі, а саме про патерн реалізації rich model vs anemic model, де rich model це начебто true DDD, а анемік — це майже процедурне програмування і взагалі непрофесіна річ.

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

DDD у широкому сенсі — це, звісно, не про патерни реалізації.

класна штука, але дуже важко заходить в слабких інженерних командах.

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

То якшо срати не вміють, то не треба мучати сраку.
ДДД це трішки не про те.

В реальному житті є така штука називається performance. Підхід «тянемо все з БД в RAM, там міняємо, пишемо все БД» для проектів для яких performance важливий не дуже підходить. Але бізнес crud це зазвичай не стосується.

чим класна штука?

я суджу от чисто судячи з цієї статті — все занадто відірвано від практики, це все звучить як «оцей кінь — це отой сферичний кінь, а потім це група отаких сферичних коней, яка формує сферичне стадо» і все в такому дусі. Де тут привязка до практики взагалі?

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

дякую за участь у діскуссії, та змістовну відповідь.

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

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

Пам’ятаєте, колись на співбесідах постійно лунало MVC: типові питання, не меньш типові відповіді. Куди воно ділось, звідкіля взялось?

Чи замислюється хтось про MVVM пилячи оті свої ангулярчики?

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

Але, якщо по ділу. DDD — це Domain Driven. Domain він існує не тільки в DDD, це взагалі абстракція іншого рівню. Навіть спроба окреслити ваші сутності хоч якимись базовими бізнес правилами, намагання виділити, та захистити інваріанти — це все вже здатно зробити Ваш код кращим. І ніякі палаци будувати не потрібно.

На самом деле, DDD действительно не всегда полезен или нужен вообще. Если формошлепить на React / Vue / Angular — то там в принципе ничего такого не надо.

в доцільності моделювання предметної області я не сумніваюсь, просто конкретна подача «не мій шматок пирога». Мова ж не просто про доцільність моделі, а саме про DDD

ось наприклад те що мені нормально зайшло www.uber.com/...​icroservice-architecture

В реальному житті є така штука називається performance. Підхід «тянемо все з БД в RAM, там міняємо, пишемо все БД» для проектів для яких performance важливий не дуже підходить. Але бізнес crud це зазвичай не стосується.

Слишком много сущностей. Все должно быть проще. Хотя эту тему я детально не прорабатывал. Времени не хватает и голова одна. Но, в развитии предполагалось. Малые группы, один язык который сам в себе определяет грамматику. Единый синтаксис, универсальный и единый интерфейс и управление подписками на события. Возможность работать статическими объектами не противоречат ООП. Хотя расширено до наличия собственных свойств, методов и событий.

Прикладом є концепція «групи на дві піци». Ідея полягає в тому, що група має бути досить малою, щоб прогодуватися двома піцами.

2 пиццы я и в одно лицо сожрать могу.

теж ніколи не розумів оцього про дві піцци. Дві піцци це на 2-3 людини поїсти.
Може там в США стандарти інші і маються на увза 2 круглі метрові піцци

Про размеры пицц вот что пишут:

Usually 8″ is personal size, 10″ is small, 12″ is medium, 14″ is large and 16″ is extra-large. NY Style pizzas are 18″ in diameter.

А средний размер 2-pizza team — 5-8 человек. Не знаю каким образом их можно накормить двумя NY Style пиццами.

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

У другому абзаці помилка в слові Domain

Розкажіть як в DDD робити Bulk Update по красівому?

На це важко відповісти однією фразою, окрім «it depends».
Домени — це не щось у вакуумі. Вони зазвичай комунікують між собою, але скоріше як із чимось зовнішнім.

Можу запропонувати питання для пошуку відповіді:
1. Чи реально розділені бази даних?
2. Чи визначені межі контексту для кожного домену?
3. Які інтерфейси комунікації? Це можуть бути якісь API, EDA, або якийсь гібрід.
4. Чи можуть bulk operations бути послідовними змінами станів одного домену на інший, чи вони не пов’язані?
5. Чи має система завжди бути консистентною між доменами для зовнішнього користувача? Чи можливо у нас є час на приведення її до консистентного стану?

А з рішень тут, зазвичай, використовуються Orchestration, Choreography, Saga Pattern.

Я про простий Bulk Update в БД без всяких

Orchestration, Choreography, Saga Pattern.

Те що без DDD роблять як “UPDATE ... SET .... WHERE id IN (:ids)”, а з DDD виглядає як overengineering enterprisecraftsmanship.com/...​osts/ddd-bulk-operations

В DDD навіть з якоюсь досить простою валідацією виникають зайві складності enterprisecraftsmanship.com/...​odel-purity-completeness

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

Щось я не розумію чому не можна в методі репозиторію викликати sql з тим апдейтом без отіх усіх понтів з патернами?

Можно, чего ж нельзя. Главное не увлекаться таким подходом, чтобы бизнес-логика в SQL не уехала. Замучаешься потом тестировать. Главная опасность — если одни и теж еправила есть в коде и в SQL. При изменении чего-то одного всегда есть риск забыть про второе, и получить потом очень забавные баги.

Вся сіль у тому, що основна проблема масового апдейту це захардкожений стрінгою сіквел-запит. Звідти ростуть і інші проблеми, серед яких те саме дублювання логіки. І якщо подивитись на те посилання, незважаючи на купу розумних слів від хардкоду сіквел запиту там нікуди не відійшли. Тобто проблему не вирішили.
Але ж так, цілу статтю написали та ще якихось патернів за вуха понатягували нашось. Хоча отим зайвим кодом, який не вирішує основну проблему (захаркожений запит) принесли лише додаткову складність в систему.
Задрали ці всі вийобщики, якщо чесно.

У DDD своя область применения. И чесно говоря туда не особо лезут bulk операции, поэтому и начинается велосипедостроение.

С хардкодом можно бороться. В EF Core начиная с верии 7 есть волшебная операция ExecuteUpdateAsync. Можно тут покрутить, оно позволяет выражения передавать для фильтров, со спецификациями все вполне по DDD-шному будет

У DDD своя область применения. И чесно говоря туда не особо лезут bulk операции, поэтому и начинается велосипедостроение.

Так, але ж іноді треба, тому без лісапеда ніяк.

С хардкодом можно бороться. В EF Core начиная с верии 7 есть волшебная операция ExecuteUpdateAsync. Можно тут покрутить, оно позволяет выражения передавать для фильтров, со спецификациями все вполне по DDD-шному будет

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

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

От вам приклад.
Технічно можливо всі об’єкти замінить на 

Map<Object, Object>
куди складать пропертя, функції і т.д.
Просто, немає обмежень, не треба паритись за «зайві класи».
Чи буде побудований на такому принципі проект життєздатний?
В DDD навіть з якоюсь досить простою валідацією виникають зайві складності enterprisecraftsmanship.com/...​odel-purity-completeness

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

Навіщо тоді ДДД? Клепаєш собі Controllers/Services/Repositories/Entities і все.

ДДД не протирічить

Controllers/Services/Repositories/Entities і все.

аж ніяк.

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

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

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

Якщо щось відбулось в житті — створи івент з такою назвою. Подія несе в собі певні дані? Створи об’єкти цих даних як ідей. Намагайся писати код так, щоб він читався як потік думок, що описують дії в реальному світі. Оце і є ДДД.
І коли ти в цьому прєісполнішся, ходи і пизди ссаним ганчір’ям всіх хто шось белькоче про леєри і те що логіка може бути тільки в User.class

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

що вся доменна логіка має бути в Domain.class або розстріл

ну і є ще матеріали де наклепаєм патернів бо

код логіки можна писать тільки в Domain.class

в цьому всьому є плюс — поки є такі штуки, у нас є робота, AI точно в цьому не розбереться. І завжди будуть адвокати/євангелісти які будуть приходити один за одним і перероблювати проект поки є гроші, а в нас буде робота і пікнік продовжуватиметься)

Більшість матеріалів по ДДД або взагалі ухиляються від скадних питать. Або наголошуть

Бо ДДД це не про конкретний інструментарій, і не про критерії, які можна виміряти приборами якими міряють кал. Це про спосіб бачення, мислення і філософію роботи. Традиційно людям це важко. Тому більшість тих хто щось пише про ддд падають в тупе формалізаторство. Ну а те що хтось написав матеріал то не є автосвідоцтвом що він в ньому розбирається. А популярність знову ж таки, приходить бо думати то важко, а формалність то просто і понятно: ага, я фігачу логіку в Domain.class -> я роблю ДДД. Все просто і зрозуміло. Те що втрачено життєвий сенс, вже не турбує.
Тому і якість матеріалів така.

щось схоже у всіх своє DDD )

а що почитати порекомендуєте з цієї тематики? Еванса?

Статтю на вікіпедії про ДДД.
А потім критично осмислювати кожну написану строчку, процесси, структури, назви.
Наближати свій код до опису сенсу а не технічних інструкцій, якими помазана зверху БД.
І писати, писати, писати.

Іноді відповідь приходить з досвідом в певних класах задач. Балк апдейт іноді зробити неможливо навіть не по красівому.

Нехай є певна сутність якій треба змінити стан.

Ситуація 1: простий проект, де просто змінюється стан сутності, і всі щасливі. Вжухнув балк апдейт — пішов пиляти наступну таску.
Ситуація 2: складна бізнес логіка, де зі зміною стану сутності необхідно викинти про неї івент в чергу. Вжухнув балк апдейт бо так оптимальніше, забув розіслати івенти — маєш недовиконані задачі по процесу. Або звісно можеш вжухнути балк апдейт, а поітм в циклі пушиш івенти в чергу по кожному. А якщо на половині розсилок в чергу пропадає конект в чергу? Треба якось перезаслати в чергу чи відкотити в базі? А які саме відкатувати в базі?

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

Балк — це з сайтів на вордпресі без складних процесів. Якщо процес складний — це робота з конкретною сутністю, оркестрація чи хореографія процесів, саги і інші базворди.

ДДД — не про оптимальність запитів в базу, це не про запити в базу взагалі. Це про фокус уваги на предметі і на бізнес правилах.

Балк — це з сайтів на вордпресі без складних процесів.

ДДД так само тільки для сайтів без складних процесів.

ДДД — не про оптимальність запитів в базу, це не про запити в базу взагалі. Це про фокус уваги на предметі і на бізнес правилах.

ДДД вас ніяк не рятує від того що на віправкі івента пропадає конект в чергу. Треба якось перезаслати в чергу чи відкотити запис в базі? Якщо запис в базі вже хтось встиг змінити то що саме відкатувати в базі? І тут зненацька в ДДД треба знати і про запити в базу і про чергу.

ДДД — це про ідеальний (з точки зору программіста) бізнес процесс у вакуумі. Як тільки ДДД стикається з реальним жіттям то його абстакції дуже сильно протікають...

@Async
@EventListener
void process(DomainNeedToBeUpdaitedEvent event) {
    DomainUpdatedReport report = new DomainUpdatedReport(event);

    List<DomainObject> objectsToUpdate = domainObjectsRepository.findAllMatching(event.requirements());

    objectsToUpdate.forEach((object) -> {
            object.apply(event.update());
            report.addUpdated(object));
    });

    domainObjectsRepository.saveAll(objectsToUpdate);
    reportRepository.save(report);
}
В чім проблема?
трай-кетчами притрусить за смаком. Розбивку на батчі теж можна зробіть.
Те що без DDD роблять як “UPDATE ... SET .... WHERE id IN (:ids)”, а з DDD виглядає як overengineering

вище написав про те, в що згодом перетвориться такий проект.

Багато я на своїй практиці зустрічав таких «філософів» які «без ДДД бо оверінжінірінг я зараз просто швидко UPDATE ... SET ... WHERE».

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

Я навіть не проти, робіть так і далі.
Це гарантує 2 речі:
1) у мене завжди буде високооплачувана робота
2) я завжди буду мати можливість почухувати своє ЧСВ бо після примусової переробки їхнього «простоє» на моє «оверінжінірінг ддд» баги зникають і перероблене працює як годинник, а їм нічого не лишається як сказати «ой пук-среньк ну ми думали так простіше хто ж знав». Я знав.

Так шо кіп гоінг.

Я навіть не проти, робіть так і далі.
Це гарантує 2 речі:

Це вам нічого не гарантує.

1) у мене завжди буде високооплачувана робота

Не факт з таким ЧСВ як у вас.

2) я завжди буду мати можливість почухувати своє ЧСВ бо після примусової переробки їхнього «простоє» на моє «оверінжінірінг ддд» баги зникають і перероблене працює як годинник, а їм нічого не лишається як сказати «ой пук-среньк ну ми думали так простіше хто ж знав». Я знав.

Бачів кучу проектів без ДДД, які з часом не претворилися на те що ви написали. Тут справа не в ДДД, а в прямих руках.

Так що пук серькайте далі.

Може хтось знає де можна купити книжку Еванса по DDD з українським перекладом, бо зустрічав тільки англійською і кацапською(

Підписатись на коментарі