DDD w frontendzie: kiedy Domain-Driven Design ma sens | POLPROG Przejdź do treści

DDD w frontendzie: kiedy Domain-Driven Design ma sens

Domain-Driven Design nie jest zestawem folderów ani wzorcem zarezerwowanym dla backendu. Jego celem jest uporządkowanie złożonej domeny biznesowej poprzez wspólny język, jawne modele i granice kontekstów. W frontendzie DDD może być bardzo wartościowe w dużych aplikacjach produktowych, ale równie łatwo może stać się kosztownym przerostem architektury. Najważniejsze jest rozpoznanie, gdzie frontend naprawdę zawiera złożone reguły i procesy domenowe, a gdzie pozostaje prostą warstwą prezentacji.

Opublikowano Autor Czas czytania 9 min czytania

Domain-Driven Design nie jest zestawem folderów ani wzorcem zarezerwowanym dla backendu. Jego celem jest uporządkowanie złożonej domeny biznesowej poprzez wspólny język, jawne modele i granice kontekstów. W frontendzie DDD może być bardzo wartościowe w dużych aplikacjach produktowych, ale równie łatwo może stać się kosztownym przerostem architektury. Najważniejsze jest rozpoznanie, gdzie frontend naprawdę zawiera złożone reguły i procesy domenowe, a gdzie pozostaje prostą warstwą prezentacji.

Na tej stronie
  1. 1DDD nie zaczyna się od folderu `domain`
  2. 2Strategic DDD i Tactical DDD to dwa różne poziomy decyzji
  3. 3Bounded Context nie jest warstwą techniczną
  4. 4Kiedy DDD w frontendzie ma sens
  5. 5Kiedy DDD jest przerostem formy
  6. 6Ubiquitous Language powinien być widoczny także w kodzie UI
  7. 7Organizuj duży frontend według domen, a nie wyłącznie typów plików
  8. 8Oddziel prezentację od logiki domenowej
  9. 9Praktyczne warstwy w jednym kontekście frontendu
  10. 10Model API i model domeny frontendu nie muszą być tym samym
  11. 11Encje, Value Objects i agregaty: używaj selektywnie
  12. 12Stan UI to nie to samo co stan domenowy
  13. 13Granice muszą być egzekwowane, nie tylko narysowane
  14. 14Frontend i backend powinny dzielić domenę, ale niekoniecznie identyczny model
  15. 15Testy są jednym z najmocniejszych argumentów za czystym modelem domenowym
  16. 16Jak wdrażać DDD w istniejącym frontendzie bez wielkiego rewrite

DDD nie zaczyna się od folderu `domain`

Eric Evans opisuje DDD jako zestaw wzorców i definicji służących pracy z modelem domeny, a Martin Fowler podkreśla, że sednem jest modelowanie złożonej domeny i rozwijanie języka używanego wspólnie przez programistów oraz ekspertów domenowych. [1][2][4]

Sama struktura `domain/application/infrastructure` nie oznacza więc DDD. Można mieć perfekcyjnie nazwane katalogi i nadal operować wyłącznie na DTO oraz procedurach bez modelu domenowego. Można też stosować strategiczne idee DDD bez klasycznego układu warstw.

Strategic DDD i Tactical DDD to dwa różne poziomy decyzji

Strategiczne DDD zajmuje się podziałem większej domeny na subdomeny i Bounded Contexts oraz relacjami między nimi. Microsoft opisuje analizę domeny jako etap identyfikacji subdomen i ograniczonych kontekstów, a Fowler wskazuje Bounded Context jako centralny wzorzec strategicznego DDD. [3][7]

Taktyczne DDD działa wewnątrz konkretnego kontekstu i obejmuje między innymi encje, Value Objects, agregaty i Domain Services. Microsoft wyraźnie rozdziela analizę strategiczną od taktycznego modelowania. [8]

W frontendzie często warto zacząć od poziomu strategicznego i dopiero później zdecydować, czy dany kontekst rzeczywiście potrzebuje bogatego modelu taktycznego.

Bounded Context nie jest warstwą techniczną

Fowler definiuje Bounded Context jako granicę, wewnątrz której model jest spójny i ma jednoznaczne znaczenie. Ten sam termin, na przykład `Customer` albo `Product`, może mieć inne znaczenie w różnych kontekstach. [3]

Z tego wynika ważna konsekwencja dla frontendu: `frontend`, `backend`, `React app`, `route` czy `micro-frontend` nie są automatycznie Bounded Contexts. Granica kontekstu wynika z modelu i języka domenowego, a nie z technologii. Praktycy DDD w frontendzie również zwracają uwagę, że frontend jako całość nie powinien być mechanicznie traktowany jako osobny kontekst. [15][16]

Kiedy DDD w frontendzie ma sens

SygnałKierunekDlaczego
Wiele zmiennych reguł biznesowychDDDModel domenowy ogranicza rozproszenie reguł
Te same pojęcia mają różne znaczenia w produkcieDDDBounded Contexts pozwalają utrzymać kilka spójnych modeli
Wiele zespołów i ekspertów domenowychDDDUbiquitous Language zmniejsza niejednoznaczność
Prosty CRUD i formularzeProstsza modularnośćKoszt taktycznego DDD może przewyższać korzyść
Frontend głównie odwzorowuje APIProstsza modularnośćNiewiele własnej logiki domenowej do modelowania
Mała aplikacja z jedną spójną domenąProstsza modularnośćGranice i warstwy mogą nie wnosić realnej wartości

Najmocniejszy sygnał to złożone, często zmieniające się reguły biznesowe widoczne także po stronie klienta: konfiguratory, procesy wieloetapowe, pricing, uprawnienia, zależności między stanami, rozbudowane workflow lub różne modele tego samego pojęcia w różnych częściach produktu.

DDD jest również uzasadnione, gdy kilka zespołów rozwija duży produkt i te same słowa mają różne znaczenia w różnych obszarach. Bounded Contexts są właśnie mechanizmem radzenia sobie z wieloma modelami i zespołami. [3]

DDD Crew zaleca najpierw zrozumieć domenę, wskazać strategicznie najważniejsze subdomeny i dopiero później definiować odpowiedzialności Bounded Contexts oraz kodować model. [10]

Kiedy DDD jest przerostem formy

Jeżeli frontend głównie pobiera dane, wyświetla je, edytuje formularze i wysyła proste komendy CRUD bez istotnych reguł biznesowych po stronie klienta, pełny zestaw taktycznych wzorców DDD zwykle nie rozwiązuje realnego problemu.

Microsoft wprost wskazuje, że dla prostego kontekstu CRUD anemiczny model danych może być wystarczający i nie zawsze warto wdrażać bardziej złożone wzorce DDD. Dopiero duża liczba zmiennych reguł biznesowych uzasadnia bogatszy model. [9]

To samo kryterium warto stosować w frontendzie: poziom architektury powinien odpowiadać złożoności domeny, a nie ambicji technicznej zespołu.

Ubiquitous Language powinien być widoczny także w kodzie UI

Ubiquitous Language to wspólny, rygorystyczny język budowany przez programistów i ekspertów domenowych na bazie modelu. Fowler podkreśla, że język powinien ewoluować wraz ze zrozumieniem domeny. [4]

W frontendzie oznacza to, że nazwy use case'ów, akcji, typów, modułów, ekranów i stanów powinny używać terminów biznesowych zamiast technicznych skrótów tam, gdzie opisują zachowanie domeny.

Jeżeli biznes mówi `approveApplication`, a frontend wszędzie operuje na `setFlag2` albo `handleData`, zespół traci jedną z głównych korzyści DDD: kod przestaje być czytelnym nośnikiem modelu.

Organizuj duży frontend według domen, a nie wyłącznie typów plików

Fowler wskazuje, że podział najwyższego poziomu na `view/model/data` może wystarczać w mniejszych systemach, ale wraz ze wzrostem aplikacji lepiej dzielić najwyższy poziom na moduły domenowe, które dopiero wewnętrznie mają własne warstwy. [5]

Aktualna dokumentacja Nx pokazuje podobny kierunek: foldery mogą wyznaczać granice ownershipu domen, a wewnątrz domen biblioteki mogą być klasyfikowane jako `feature`, `ui`, `data-access` i `util`. [12]

Praktyczny układ może więc wyglądać jak `libs/orders/...`, `libs/billing/...`, `libs/identity/...`, zamiast jednego globalnego `components/`, `services/` i `models/`.

Oddziel prezentację od logiki domenowej

Fowler opisuje rozdzielenie prezentacji, logiki domenowej i dostępu do danych jako skuteczny sposób modularizacji aplikacji informacyjnych. Wskazuje też, że UI jest trudniejsze do testowania, dlatego logika domenowa korzysta na umieszczeniu poza warstwą prezentacji. [5][6]

W praktyce frontendowej komponent powinien przede wszystkim renderować, obsługiwać zdarzenia UI i delegować operacje. Reguły takie jak `czy zamówienie można anulować`, `jak policzyć rabat` albo `jaki status jest dozwolony po poprzednim` powinny mieć jedno jawne miejsce poza JSX, template lub komponentem.

Nie oznacza to zakazu logiki prezentacyjnej w komponencie. Walidacja widoku, widoczność elementu, lokalny focus czy animacja to inny rodzaj logiki niż reguła biznesowa.

Praktyczne warstwy w jednym kontekście frontendu

WarstwaOdpowiedzialnośćPrzykładowe elementy
presentation / uiRenderowanie i zachowanie interfejsucomponents, routes, view state
applicationKoordynacja przypadków użyciacommands, use cases, orchestration
domainReguły, pojęcia i niezmienniki domenyentities, value objects, policies
infrastructure / data-accessIntegracje techniczneHTTP, storage, SDK, mappers

DDD nie narzuca jednego obowiązkowego układu katalogów. Rozdzielenie `presentation`, `application`, `domain` i `infrastructure` jest praktyczną interpretacją opartą na separacji odpowiedzialności, a nie formalnym wymaganiem Evansa. [1][5]

Warstwa domenowa może zawierać reguły, typy i zachowanie niezależne od frameworka; application może koordynować use case'y; infrastructure może adaptować HTTP, storage i zewnętrzne SDK; presentation może zawierać komponenty, routing i stan czysto interfejsowy.

Model API i model domeny frontendu nie muszą być tym samym

Bounded Context może mieć własny model tego samego pojęcia, a różne konteksty mogą mapować między swoimi reprezentacjami. Fowler podaje `Customer` i `Product` jako typowe przykłady pojęć o różnych znaczeniach w różnych kontekstach. [3]

Dlatego DTO z backendu nie musi przenikać bezpośrednio do każdego komponentu. Mapper lub adapter może przekształcić kontrakt transportowy do modelu używanego przez konkretny kontekst frontendu.

Taki krok ma największą wartość, gdy API jest współdzielone przez kilka klientów, jest historyczne, łączy kilka domen albo zmienia się niezależnie. Dla prostego endpointu CRUD dodatkowa warstwa mapowania może być zbędna.

Encje, Value Objects i agregaty: używaj selektywnie

DDD rozróżnia między innymi Entities, Value Objects, Services i Aggregates. Fowler wskazuje te pojęcia jako część klasyfikacji wprowadzonej przez Evansa, a Microsoft opisuje agregaty jako taktyczny wzorzec utrzymujący spójność modelu. [2][8]

Frontend nie powinien jednak kopiować modelu backendowego 1:1 tylko po to, aby mieć te same klasy. Value Object może być bardzo użyteczny dla `Money`, `DateRange` albo `Email`, jeżeli ma własne niezmienniki. Agregat ma sens wtedy, gdy klient rzeczywiście musi pilnować reguł spójności konkretnego modelu.

Jeżeli typ jest jedynie strukturą danych do renderowania tabeli, prosty typ TypeScript może być lepszy niż encja z fabryką, repozytorium i warstwą usług.

Stan UI to nie to samo co stan domenowy

React rozróżnia problemy organizacji stanu i zaleca unikanie redundantnego oraz zduplikowanego stanu. To dobra baza techniczna, ale samo użycie reducerów, store'a lub sygnałów nie tworzy modelu domenowego. [14]

Stan typu `isModalOpen`, aktywna zakładka czy pozycja scrolla należy do prezentacji. Stan reprezentujący cykl życia zamówienia, dostępne przejścia procesu albo reguły konfiguratora może należeć do modelu domenowego.

Rozdzielenie tych dwóch kategorii ogranicza globalne store'y zawierające przypadkową mieszankę danych serwera, zachowań biznesowych i szczegółów UI.

Granice muszą być egzekwowane, nie tylko narysowane

Nx pozwala definiować tagi projektów i deklaratywne reguły zależności, na przykład blokować importy pomiędzy określonymi scope'ami lub typami bibliotek. Reguła `@nx/enforce-module-boundaries` może sprawdzać importy podczas lintowania. [11]

Nx obsługuje też wiele wymiarów tagów, dzięki czemu można osobno modelować `scope`, typ biblioteki, stabilność czy stronę klient/server. [13]

Dla DDD oznacza to możliwość zapisania decyzji architektonicznych jako reguł CI, na przykład: `billing` nie importuje wnętrza `identity`, `ui` nie zależy od `data-access`, a `domain` nie importuje frameworka.

Frontend i backend powinny dzielić domenę, ale niekoniecznie identyczny model

Bounded Context jest granicą modelu i języka, dlatego nie powinien być wyznaczany automatycznie linią sieciową pomiędzy przeglądarką i serwerem. Praktyczne opracowania frontendowego DDD podkreślają, że konteksty mogą przecinać warstwy techniczne i być realizowane wspólnie przez frontend oraz backend. [15][16]

Jednocześnie konkretna reprezentacja modelu w kliencie może różnić się od reprezentacji serwerowej, bo ma inne potrzeby dotyczące interakcji, lokalnego stanu i prezentacji. Kluczowe jest zachowanie semantyki i jawne mapowanie, a nie kopiowanie klas.

Micro-frontend również nie powinien być tworzony tylko dlatego, że istnieje Bounded Context. Granica domenowa i granica deployowalnego artefaktu to osobne decyzje.

Testy są jednym z najmocniejszych argumentów za czystym modelem domenowym

Fowler wskazuje testowalność jako jedną z korzyści oddzielenia prezentacji od domeny. Logika poza UI może być testowana bez renderowania komponentów i bez zależności od szczegółów interfejsu. [5][6]

Dla frontendu oznacza to szybkie testy reguł, Value Objects, use case'ów i przejść stanu jako zwykłego TypeScript lub JavaScript. Testy komponentów pozostają potrzebne, ale nie muszą być jedynym miejscem weryfikacji reguł biznesowych.

Dobrym sygnałem ostrzegawczym jest sytuacja, w której każda zmiana reguły biznesowej wymaga montowania całego komponentu, mockowania routera, store'a, HTTP i przeglądarki.

Jak wdrażać DDD w istniejącym frontendzie bez wielkiego rewrite

DDD Crew proponuje proces iteracyjny: zrozumieć domenę, wskazać ważne subdomeny, zdefiniować odpowiedzialności kontekstów i dopiero potem kodować model. [10]

W istniejącym frontendzie bezpieczniej wybrać jeden problematyczny obszar, nazwać jego język, ustalić granicę, odseparować prezentację od reguł, zdefiniować publiczne API modułu i dopiero później przenosić kolejne funkcje.

Nie trzeba przenosić całej aplikacji. DDD można stosować tam, gdzie złożoność biznesowa daje zwrot z dodatkowego modelowania, a pozostałe obszary pozostawić jako prostsze feature modules.

  • Zacznij od problemu biznesowego, nie od struktury katalogów.
  • Zidentyfikuj terminy, reguły i niejednoznaczne pojęcia.
  • Wyznacz jeden Bounded Context i jego publiczny kontrakt.
  • Oddziel logikę domenową od komponentów i transportowych DTO.
  • Dodaj tylko te wzorce taktyczne, które rozwiązują konkretną złożoność.
  • Zapisz granice jako reguły lintowania lub CI.
  • Mierz liczbę zależności, cykli, regresji i trudność zmian przekrojowych.
  • Nie migruj prostych CRUD-ów tylko dla architektonicznej symetrii.

DDD w frontendzie ma sens wtedy, gdy frontend jest częścią złożonego produktu, a nie tylko rendererem danych. Największą wartość daje zwykle strategiczne DDD: wspólny język, podział na konteksty i egzekwowane granice. Taktyczne wzorce warto dodawać dopiero tam, gdzie istnieją reguły, niezmienniki i zachowania wymagające własnego modelu. Jeśli aplikacja jest głównie CRUD-em, formularzami i prostym odczytem API, dobra modularność i separacja prezentacji od danych będą zwykle lepsze niż pełne DDD.

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

Najczęściej zadawane pytania

Czy DDD ma sens we frontendzie?

Tak, jeśli frontend uczestniczy w złożonej domenie i korzysta z jasnych granic, wspólnego języka lub własnych reguł biznesowych. DDD nie jest ograniczone do backendu. [1][2][15]

Czy każdy frontend powinien używać DDD?

Nie. Dla prostych aplikacji CRUD pełne taktyczne DDD może być zbędne. Microsoft wprost wskazuje, że proste konteksty CRUD nie zawsze wymagają bogatego modelu. [9]

Czy frontend jest osobnym Bounded Context?

Nie automatycznie. Bounded Context jest granicą spójnego modelu i języka, a nie granicą technologii. [3][16]

Czy Bounded Context powinien być micro-frontendem?

Nie. Granica domenowa nie wymusza osobnego deploymentu. Decyzja o micro-frontendzie jest dodatkową decyzją architektoniczną. [3][15]

Czy trzeba mieć folder domain?

Nie. DDD nie definiuje obowiązkowej struktury katalogów. Folder może pomóc w separacji, ale sam nie tworzy modelu domenowego. [1][5]

Gdzie trzymać logikę biznesową w frontendzie?

Poza czystą prezentacją, w jawnej części modelu lub use case'ach danego kontekstu. Fowler zaleca separację logiki prezentacyjnej i domenowej. [5][6]

Czy DTO z API może być modelem domenowym?

Może w bardzo prostym przypadku, ale nie musi. Różne konteksty mogą mieć różne modele tych samych pojęć, więc mapowanie bywa właściwą granicą. [3]

Czy Repository ma sens w frontendzie?

Tylko gdy rozwiązuje realny problem abstrakcji dostępu do modelu. Dla prostego fetchowania danych dodatkowe Repository może być zbędne. DDD nie wymaga stosowania wszystkich wzorców wszędzie. [1][9]

Czy Redux, NgRx, Zustand lub Signals są częścią DDD?

Nie. To mechanizmy zarządzania stanem. Mogą przechowywać stan domenowy, ale same nie definiują modelu, języka ani Bounded Context. [14]

Czy model domenowy frontendu powinien być identyczny z backendem?

Nie. Powinien zachować właściwą semantykę domeny, ale może mieć inną reprezentację dostosowaną do klienta. Bounded Contexts mogą mieć różne modele tych samych pojęć. [3]

Jak egzekwować granice domen w monorepo?

Nx pozwala używać tagów i @nx/enforce-module-boundaries, aby automatycznie blokować niedozwolone zależności. [11][13]

Od czego zacząć wdrażanie DDD w istniejącej aplikacji?

Od jednego złożonego obszaru biznesowego: zrozumienia języka, reguł i granicy kontekstu. DDD Crew rekomenduje iteracyjne przejście od domeny do kontekstów i kodu. [10]

Źródła i przypisy

  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

Czy ten artykuł był pomocny?

Nowe artykuły na e-mail

Jeden krótki e-mail przy każdym nowym artykule. Bez spamu, wypisujesz się jednym kliknięciem.

Wykorzystujemy e-mail wyłącznie do wysyłki nowych artykułów. Bez udostępniania stronom trzecim.

Wróć do bazy wiedzy