DDD у фронтенді: коли Domain-Driven Design має сенс | POLPROG Перейти до вмісту

DDD у фронтенді: коли Domain-Driven Design має сенс

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

Опубліковано Автор Час читання 9 хв читання

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

На цій сторінці
  1. 1DDD не починається з папки `domain`
  2. 2Стратегічний DDD і тактичний DDD працюють на різних рівнях
  3. 3Bounded Context не є технічним шаром
  4. 4Коли DDD у фронтенді має сенс
  5. 5Коли DDD стає надмірною архітектурою
  6. 6Ubiquitous Language має бути видно і в UI-коді
  7. 7Великий фронтенд організовуйте за доменами, а не лише за типами файлів
  8. 8Відокремлюйте презентаційну поведінку від доменної
  9. 9Практичні шари всередині одного frontend-контексту
  10. 10API-модель і доменна модель фронтенду не зобов'язані бути однаковими
  11. 11Entities, Value Objects і Aggregates використовуйте вибірково
  12. 12UI-стан і доменний стан це не одне й те саме
  13. 13Межі потрібно примусово підтримувати, а не лише документувати
  14. 14Frontend і backend можуть ділити домен без ідентичної моделі
  15. 15Тестованість є сильним аргументом за ізоляцію доменної логіки
  16. 16Як запровадити DDD в існуючий фронтенд без великого rewrite

DDD не починається з папки `domain`

Eric Evans представляє DDD як набір патернів і визначень для роботи з доменними моделями, а Martin Fowler підкреслює моделювання складних доменів і розвиток спільної мови між розробниками та доменними експертами. [1][2][4]

Тому структура `domain/application/infrastructure` сама по собі не робить застосунок DDD. Кодова база може мати ідеально названі папки й усе одно складатися лише з DTO та процедурної логіки. Стратегічний DDD також можна застосовувати без канонічної шарової структури.

Стратегічний DDD і тактичний DDD працюють на різних рівнях

Стратегічний DDD стосується поділу великого домену на піддомени та Bounded Contexts і визначення зв'язків між ними. Microsoft описує доменний аналіз як ідентифікацію піддоменів і обмежених контекстів, а Fowler називає Bounded Context центральним стратегічним патерном. [3][7]

Тактичний DDD працює всередині конкретного контексту та включає, зокрема, Entities, Value Objects, Aggregates і Domain Services. Microsoft явно розділяє стратегічний аналіз і тактичне моделювання. [8]

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

Bounded Context не є технічним шаром

Fowler описує Bounded Context як межу, всередині якої модель залишається узгодженою, а терміни мають однозначне значення. Один і той самий концепт, наприклад `Customer` або `Product`, може мати різний зміст у різних контекстах. [3]

Тому `frontend`, `backend`, `React app`, `route` чи `micro-frontend` не є автоматично Bounded Contexts. Межі походять із доменної моделі та мови, а не з технології. Практичні джерела про DDD у фронтенді застерігають від тієї самої помилки. [15][16]

Коли DDD у фронтенді має сенс

СигналНапрямЧому
Багато змінних бізнес-правилDDDДоменна модель не дає правилам розпорошуватися
Ті самі поняття мають різний зміст у різних частинах продуктуDDDBounded Contexts дозволяють підтримувати кілька узгоджених моделей
Співпрацюють кілька команд і доменних експертівDDDUbiquitous Language зменшує неоднозначність
Простий CRUD і формиПростіша модульністьВартість тактичного DDD може перевищити користь
Фронтенд переважно відображає APIПростіша модульністьМало власної клієнтської доменної логіки
Малий застосунок з одним узгодженим доменомПростіша модульністьДодаткові межі та шари можуть давати мало користі

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

DDD також корисний, коли над великим продуктом працює кілька команд і ті самі слова мають різне значення в різних областях. Bounded Contexts призначені саме для роботи з кількома узгодженими моделями. [3]

DDD Crew рекомендує спочатку зрозуміти домен, визначити стратегічно важливі піддомени, потім окреслити відповідальності Bounded Contexts і лише після цього кодувати модель. [10]

Коли DDD стає надмірною архітектурою

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

Microsoft прямо зазначає, що для простого CRUD-контексту анемічної моделі даних може бути достатньо, а складніші DDD-патерни не завжди виправдовують витрати. Багата модель стає кориснішою за наявності багатьох змінних бізнес-правил. [9]

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

Ubiquitous Language має бути видно і в UI-коді

Ubiquitous Language це спільна й точна мова, яку розробники та доменні експерти будують навколо моделі. Fowler підкреслює, що вона повинна розвиватися разом із розумінням домену. [4]

У фронтенді назви use case, дій, типів, модулів, екранів і станів повинні використовувати бізнес-терміни, коли вони описують доменну поведінку.

Якщо бізнес говорить `approveApplication`, а код використовує `setFlag2` або `handleData`, код перестає бути зрозумілим відображенням доменної мови.

Великий фронтенд організовуйте за доменами, а не лише за типами файлів

Fowler зазначає, що структура верхнього рівня `view/model/data` може бути достатньою для малих систем, але зі зростанням краще мати доменно орієнтовані модулі верхнього рівня, які вже всередині містять свої шари. [5]

Актуальна документація Nx показує подібний напрям: папки можуть бути межами ownership доменів, а бібліотеки всередині домену можна класифікувати як `feature`, `ui`, `data-access` і `util`. [12]

Практична структура може виглядати як `libs/orders/...`, `libs/billing/...`, `libs/identity/...` замість одного глобального дерева `components/`, `services/`, `models/`.

Відокремлюйте презентаційну поведінку від доменної

Fowler описує поділ презентації, доменної логіки та доступу до даних як ефективну модульність. UI-код також зазвичай важче тестувати, тому винесення доменної логіки за межі презентації покращує тестованість. [5][6]

Фронтенд-компонент має переважно рендерити, обробляти UI-події та делегувати операції. Правила на кшталт можливості скасування замовлення, розрахунку знижки або допустимого переходу стану повинні мати явне місце поза JSX, шаблонами чи класами компонентів.

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

Практичні шари всередині одного frontend-контексту

ШарВідповідальністьПриклади
presentation / uiРендеринг і поведінка інтерфейсуcomponents, routes, view state
applicationКоординація use casecommands, use cases, orchestration
domainДоменні правила, поняття та інваріантиentities, value objects, policies
infrastructure / data-accessТехнічні інтеграціїHTTP, storage, SDKs, mappers

DDD не вимагає обов'язкової структури папок. Розділення на `presentation`, `application`, `domain` та `infrastructure` є практичною інтерпретацією розподілу відповідальностей, а не формальною вимогою Evans. [1][5]

Шар domain може містити правила, поняття й поведінку, незалежні від framework; application координує use case; infrastructure адаптує HTTP, storage та зовнішні SDK; presentation містить компоненти, routing і суто UI-стан.

API-модель і доменна модель фронтенду не зобов'язані бути однаковими

Bounded Context може мати власну модель певного поняття, а різні контексти можуть мапити між різними представленнями. Fowler використовує `Customer` і `Product` як типові приклади термінів із різним значенням у різних контекстах. [3]

Тому backend DTO не зобов'язаний напряму потрапляти в кожен компонент. Mapper або adapter може перетворити транспортний контракт у модель конкретного frontend-контексту.

Така межа особливо корисна, коли API обслуговує кілька клієнтів, є legacy, поєднує кілька доменів або розвивається незалежно. Для простого CRUD endpoint додатковий mapping-шар може бути зайвим.

Entities, Value Objects і Aggregates використовуйте вибірково

DDD розрізняє, зокрема, Entities, Value Objects, Services і Aggregates. Fowler називає їх частиною словника Evans, а Microsoft описує агрегати як тактичний патерн для підтримки узгодженості моделі. [2][8]

Фронтенд не повинен копіювати backend-модель 1:1 лише заради однакових класів. Value Object корисний для `Money`, `DateRange` або `Email`, якщо він захищає реальні інваріанти. Aggregate має сенс, якщо клієнт справді повинен контролювати правила узгодженості.

Якщо тип це лише дані для рендерингу таблиці, простий TypeScript-тип може бути кращим за Entity, Factory, Repository і Service.

UI-стан і доменний стан це не одне й те саме

React розглядає організацію стану як задачу дизайну та радить уникати надлишкового чи дубльованого стану. Це корисна технічна база, але reducer, store або signals самі по собі не створюють доменну модель. [14]

Стан на кшталт `isModalOpen`, активної вкладки або позиції scroll належить презентації. Життєвий цикл замовлення, дозволені переходи процесу або правила конфігуратора можуть належати доменній моделі.

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

Межі потрібно примусово підтримувати, а не лише документувати

Nx дозволяє визначати project tags і декларативні dependency constraints, наприклад блокувати imports між scope або типами бібліотек. `@nx/enforce-module-boundaries` може перевіряти imports під час lint. [11]

Nx також підтримує кілька вимірів tags, тому окремо можна моделювати `scope`, тип бібліотеки, стабільність або client/server аспекти. [13]

Рішення DDD таким чином можуть стати CI-правилами: `billing` не імпортує внутрішні частини `identity`, `ui` не залежить від `data-access`, а `domain` залишається незалежним від framework.

Frontend і backend можуть ділити домен без ідентичної моделі

Bounded Context є межею моделі та мови, тому його не слід автоматично проводити по мережевій межі між браузером і сервером. Практичні матеріали про DDD у фронтенді підкреслюють, що контексти можуть перетинати технічні шари й реалізовуватися спільно фронтендом і бекендом. [15][16]

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

Micro-frontend також не повинен з'являтися лише тому, що існує Bounded Context. Доменна межа та межа deployable-артефакту це різні рішення.

Тестованість є сильним аргументом за ізоляцію доменної логіки

Fowler називає тестованість перевагою відокремлення презентації від домену. Логіку поза UI можна тестувати без рендерингу компонентів і без залежності від деталей інтерфейсу. [5][6]

У фронтенді це дозволяє швидко тестувати правила, Value Objects, use case і переходи станів як звичайний TypeScript або JavaScript. Тести компонентів залишаються потрібними, але не повинні бути єдиним місцем перевірки бізнес-правил.

Тривожний сигнал це ситуація, коли кожна зміна бізнес-правила вимагає mount повного компонента та mock router, store, HTTP і browser API.

Як запровадити DDD в існуючий фронтенд без великого rewrite

DDD Crew рекомендує ітеративний процес: зрозуміти домен, визначити важливі піддомени, окреслити відповідальності Bounded Contexts і лише потім кодувати модель. [10]

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

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

  • Починайте з бізнес-проблеми, а не з папок.
  • Визначте терміни, правила та неоднозначні поняття.
  • Окресліть один Bounded Context і його публічний контракт.
  • Відокремте доменні правила від компонентів і транспортних DTO.
  • Додавайте лише тактичні патерни, які вирішують конкретну складність.
  • Закріпіть межі як lint або CI-правила.
  • Вимірюйте залежності, цикли, регресії та вартість наскрізних змін.
  • Не мігруйте прості CRUD-області лише заради архітектурної симетрії.

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

DDD Domain-Driven Design Frontend Architecture Software Architecture Bounded Context Ubiquitous Language TypeScript Nx React Angular

Часті запитання

Чи має DDD сенс у фронтенді?

Так, якщо фронтенд працює зі складним доменом і виграє від явних меж, спільної мови або клієнтських бізнес-правил. DDD не обмежений бекендом. [1][2][15]

Чи кожен фронтенд повинен використовувати DDD?

Ні. Для простих CRUD-застосунків повний тактичний DDD може бути зайвим. Microsoft прямо зазначає, що прості CRUD-контексти не завжди потребують багатої моделі. [9]

Чи є фронтенд окремим Bounded Context?

Не автоматично. Bounded Context це межа узгодженої моделі та мови, а не технології. [3][16]

Чи кожен Bounded Context має стати micro-frontend?

Ні. Доменна межа не вимагає окремого deployment. Micro-frontend архітектура є окремим рішенням. [3][15]

Чи потрібна папка domain?

Ні. DDD не встановлює обов'язкової структури директорій. Папка може допомогти, але сама не створює доменну модель. [1][5]

Де повинна бути бізнес-логіка у фронтенді?

Поза чистою презентацією, в явній моделі або use-case шарі конкретного контексту. Fowler рекомендує розділяти презентацію і доменну логіку. [5][6]

Чи може API DTO бути доменною моделлю?

У тривіальному випадку може, але не зобов'язаний. Різні контексти можуть по-різному моделювати одне поняття, тому mapping часто доречний. [3]

Чи має Repository сенс у фронтенді?

Лише якщо вирішує реальну проблему абстракції доступу до моделі. Для простого отримання даних додатковий Repository може бути зайвим. [1][9]

Redux, NgRx, Zustand або Signals це частина DDD?

Ні. Це механізми керування станом. Вони можуть містити доменний стан, але не визначають модель, мову або Bounded Context. [14]

Чи має frontend-доменна модель бути ідентичною backend-моделі?

Ні. Вона повинна зберігати правильну доменну семантику, але представлення може бути адаптоване до потреб клієнта. [3]

Як примусово підтримувати доменні межі в monorepo?

Nx може використовувати tags і @nx/enforce-module-boundaries, щоб автоматично блокувати заборонені залежності. [11][13]

З чого почати в існуючому застосунку?

З однієї складної бізнес-області: зрозуміти її мову, правила та межу. DDD Crew рекомендує ітеративний шлях від домену до контекстів і коду. [10]

Джерела та примітки

  1. Eric Evans, DDD Reference12345
  2. Martin Fowler, Domain Driven Design123
  3. Martin Fowler, Bounded Context12345678
  4. Martin Fowler, Ubiquitous Language12
  5. Martin Fowler, Presentation Domain Data Layering123456
  6. Martin Fowler, Presentation Domain Separation123
  7. Microsoft Azure Architecture Center, Use Domain Analysis to Model Microservices
  8. Microsoft Azure Architecture Center, Use Tactical DDD to Design Microservices12
  9. Microsoft .NET, Design a microservice domain model123
  10. DDD Crew, DDD Starter Modelling Process123
  11. Nx, Enforce Module Boundaries12
  12. Nx, Monorepo Folder Structure
  13. Nx, Tag in Multiple Dimensions12
  14. React, Managing State12
  15. ANGULARarchitects, DDD in Angular & Frontend Architecture1234
  16. Tomasz Ducin, Your Frontend itself is NOT a Bounded Context123

Чи було це корисно?

Отримуйте нові статті електронною поштою

Один короткий лист на кожну нову статтю Навчання. Без спаму, відписка в один клік.

Ми використовуємо вашу пошту лише для надсилання нових статей. Без передачі третім сторонам.

Назад до Навчання