DDD in frontend: wanneer Domain-Driven Design zinvol is | POLPROG Naar de inhoud

DDD in frontend: wanneer Domain-Driven Design zinvol is

Domain-Driven Design is geen mapconventie en ook niet alleen voor backends bedoeld. Het doel is complexe bedrijfsdomeinen beheersbaar te maken met gedeelde taal, expliciete modellen en duidelijke contextgrenzen. In frontend kan DDD veel waarde leveren in grote productapplicaties, maar het kan ook onnodige architectuurkosten veroorzaken. De kernvraag is waar de client echte domeincomplexiteit bevat en waar hij vooral een presentatielaag is.

Gepubliceerd Geschreven door Leestijd 20 min lezen

Domain-Driven Design is geen mapconventie en ook niet alleen voor backends bedoeld. Het doel is complexe bedrijfsdomeinen beheersbaar te maken met gedeelde taal, expliciete modellen en duidelijke contextgrenzen. In frontend kan DDD veel waarde leveren in grote productapplicaties, maar het kan ook onnodige architectuurkosten veroorzaken. De kernvraag is waar de client echte domeincomplexiteit bevat en waar hij vooral een presentatielaag is.

Op deze pagina
  1. 1DDD begint niet met een map `domain`
  2. 2Strategisch DDD en tactisch DDD werken op verschillende niveaus
  3. 3Een Bounded Context is geen technische laag
  4. 4Wanneer DDD in frontend zinvol is
  5. 5Wanneer DDD overengineering wordt
  6. 6Ubiquitous Language hoort ook zichtbaar te zijn in UI-code
  7. 7Organiseer een groot frontend rond domeinen, niet alleen bestandstypen
  8. 8Scheid presentatiegedrag van domeingedrag
  9. 9Praktische lagen binnen één frontendcontext
  10. 10API-model en frontend-domeinmodel hoeven niet identiek te zijn
  11. 11Entities, Value Objects en Aggregates: selectief gebruiken
  12. 12UI-state is niet hetzelfde als domeinstate
  13. 13Grenzen moeten worden afgedwongen, niet alleen beschreven
  14. 14Frontend en backend kunnen een domein delen zonder identieke modellen
  15. 15Testbaarheid is een sterk argument voor geïsoleerde domeinlogica
  16. 16DDD invoeren in een bestaand frontend zonder grote rewrite

DDD begint niet met een map `domain`

Eric Evans presenteert DDD als een verzameling patronen en definities voor werken met domeinmodellen, terwijl Martin Fowler de nadruk legt op het modelleren van complexe domeinen en een gedeelde taal tussen ontwikkelaars en domeinexperts. [1][2][4]

Een structuur `domain/application/infrastructure` maakt een applicatie dus niet automatisch DDD. Een codebase kan perfect benoemde mappen hebben en toch alleen DTO's en procedurele logica bevatten. Strategisch DDD kan ook zonder canonieke lagenstructuur worden toegepast.

Strategisch DDD en tactisch DDD werken op verschillende niveaus

Strategisch DDD gaat over het opsplitsen van een groter domein in subdomeinen en Bounded Contexts en over hun onderlinge relaties. Microsoft beschrijft domeinanalyse als het identificeren van subdomeinen en contexts, terwijl Fowler Bounded Context als centraal strategisch patroon beschouwt. [3][7]

Tactisch DDD werkt binnen één concrete context en omvat onder andere Entities, Value Objects, Aggregates en Domain Services. Microsoft maakt expliciet onderscheid tussen strategische analyse en tactische modellering. [8]

In frontend is het vaak beter om met strategisch ontwerp te beginnen en pas daarna te beslissen welke context echt een rijk tactisch model nodig heeft.

Een Bounded Context is geen technische laag

Fowler beschrijft een Bounded Context als een grens waarbinnen een model intern consistent blijft en termen eenduidig zijn. Hetzelfde begrip, zoals `Customer` of `Product`, kan in verschillende contexten terecht iets anders betekenen. [3]

Daarom zijn `frontend`, `backend`, `React app`, `route` en `micro-frontend` niet automatisch Bounded Contexts. Contextgrenzen komen voort uit domeinmodel en taal, niet uit technologie. Praktische frontend-DDD-bronnen waarschuwen voor dezelfde verwarring. [15][16]

Wanneer DDD in frontend zinvol is

SignaalRichtingWaarom
Veel veranderende bedrijfsregelsDDDEen domeinmodel voorkomt verspreide regelimplementatie
Dezelfde begrippen betekenen iets anders per productgebiedDDDBounded Contexts ondersteunen meerdere coherente modellen
Meerdere teams en domeinexperts werken samenDDDUbiquitous Language vermindert ambiguïteit
Eenvoudige CRUD en formulierenEenvoudigere modulariteitDe kosten van tactisch DDD kunnen hoger zijn dan de baten
Frontend weerspiegelt vooral een APIEenvoudigere modulariteitEr is weinig eigen domeinlogica aan clientzijde
Kleine applicatie met één coherent domeinEenvoudigere modulariteitExtra grenzen en lagen kunnen weinig waarde leveren

Het sterkste signaal is complexe en vaak veranderende bedrijfslogica aan clientzijde: configurators, meerstapsprocessen, pricing, rechten, statusovergangen, workflows of verschillende modellen van hetzelfde begrip in verschillende productgebieden.

DDD is ook waardevol wanneer meerdere teams aan een groot product werken en dezelfde woorden in verschillende gebieden iets anders betekenen. Bounded Contexts zijn juist bedoeld om meerdere coherente modellen te beheren. [3]

DDD Crew adviseert eerst het domein te begrijpen, strategisch belangrijke subdomeinen te identificeren, vervolgens de verantwoordelijkheden van Bounded Contexts te definiëren en pas daarna het model te coderen. [10]

Wanneer DDD overengineering wordt

Als het frontend vooral data ophaalt, toont, formulieren bewerkt en eenvoudige CRUD-commando's verstuurt zonder noemenswaardige bedrijfslogica aan clientzijde, lost de volledige set tactische DDD-patronen meestal geen echt probleem op.

Microsoft stelt expliciet dat in een eenvoudige CRUD-context een anemisch datamodel voldoende kan zijn en dat complexere DDD-patronen niet altijd de kosten waard zijn. Een rijk model wordt relevanter bij veel veranderende bedrijfsregels. [9]

Hetzelfde criterium hoort in frontend te gelden: architecturale verfijning moet passen bij domeincomplexiteit, niet bij technische ambitie.

Ubiquitous Language hoort ook zichtbaar te zijn in UI-code

Ubiquitous Language is een gedeelde, precieze taal die ontwikkelaars en domeinexperts rond het model opbouwen. Fowler benadrukt dat deze taal moet evolueren wanneer het domeinbegrip groeit. [4]

In frontend zouden namen van use cases, acties, types, modules, schermen en states daarom bedrijfswoorden moeten gebruiken wanneer ze domeingedrag beschrijven.

Als de business `approveApplication` zegt terwijl de code `setFlag2` of `handleData` gebruikt, verliest de code zijn rol als duidelijke representatie van de domeintaal.

Organiseer een groot frontend rond domeinen, niet alleen bestandstypen

Fowler merkt op dat een top-level structuur `view/model/data` voor kleine systemen kan werken, maar dat grotere applicaties beter top-level domeinmodules kunnen hebben die intern hun eigen lagen bevatten. [5]

De actuele Nx-documentatie volgt een vergelijkbare richting: mappen kunnen domein-ownershipgrenzen vormen en libraries binnen een domein kunnen worden geclassificeerd als `feature`, `ui`, `data-access` en `util`. [12]

Een praktische structuur kan dus `libs/orders/...`, `libs/billing/...` en `libs/identity/...` gebruiken in plaats van één globale boom `components/`, `services/`, `models/`.

Scheid presentatiegedrag van domeingedrag

Fowler beschrijft de scheiding tussen presentatie, domeinlogica en data access als effectieve modularisatie. Hij merkt ook op dat UI-code vaak moeilijker te testen is, waardoor domeinlogica buiten presentatie extra waarde heeft. [5][6]

Een frontendcomponent hoort vooral te renderen, UI-events af te handelen en bewerkingen te delegeren. Regels zoals of een bestelling geannuleerd mag worden, hoe korting wordt berekend of welke statusovergang geldig is, verdienen een expliciete plek buiten JSX, templates of componentklassen.

Dit verbiedt presentatielogica niet. View-validatie, zichtbaarheid, lokale focus en animatie zijn iets anders dan bedrijfsregels.

Praktische lagen binnen één frontendcontext

LaagVerantwoordelijkheidVoorbeelden
presentation / uiRendering en interfacegedragcomponents, routes, view state
applicationCoördinatie van use casescommands, use cases, orchestration
domainDomeinregels, begrippen en invariantenentities, value objects, policies
infrastructure / data-accessTechnische integratiesHTTP, storage, SDKs, mappers

DDD schrijft geen verplichte mapstructuur voor. De scheiding `presentation`, `application`, `domain` en `infrastructure` is een praktische interpretatie van verantwoordelijkheidsseparatie, geen formele eis van Evans. [1][5]

De domain-laag kan frameworkonafhankelijke regels, begrippen en gedrag bevatten; application coördineert use cases; infrastructure adapteert HTTP, storage en externe SDK's; presentation bevat componenten, routing en puur UI-state.

API-model en frontend-domeinmodel hoeven niet identiek te zijn

Een Bounded Context kan een eigen model van een begrip hebben en verschillende contexten kunnen tussen representaties mappen. Fowler gebruikt `Customer` en `Product` als typische voorbeelden van termen die per context anders kunnen betekenen. [3]

Een backend-DTO hoeft daarom niet rechtstreeks ieder component binnen te komen. Een mapper of adapter kan het transportcontract omzetten naar het model van een specifieke frontendcontext.

Deze grens is vooral nuttig wanneer een API meerdere clients bedient, legacy is, meerdere domeinen combineert of onafhankelijk evolueert. Voor een eenvoudige CRUD-endpoint kan een extra mappinglaag overbodig zijn.

Entities, Value Objects en Aggregates: selectief gebruiken

DDD onderscheidt onder andere Entities, Value Objects, Services en Aggregates. Fowler noemt ze als onderdeel van Evans' DDD-vocabulaire, terwijl Microsoft aggregates beschrijft als tactisch patroon voor modelconsistentie. [2][8]

Het frontend moet het backendmodel niet één op één kopiëren om dezelfde klassen te hebben. Een Value Object kan nuttig zijn voor `Money`, `DateRange` of `Email` wanneer het echte invarianten bewaakt. Een Aggregate is zinvol wanneer de client daadwerkelijk consistentievoorwaarden moet bewaken.

Als een type alleen tabeldata voor rendering is, kan een eenvoudig TypeScript-type beter zijn dan Entity, Factory, Repository en Service.

UI-state is niet hetzelfde als domeinstate

React behandelt state-organisatie als ontwerpvraag en adviseert redundante of gedupliceerde state te vermijden. Dat is nuttige technische begeleiding, maar reducers, stores of signals creëren op zichzelf geen domeinmodel. [14]

State zoals `isModalOpen`, actieve tab of scrollpositie hoort bij presentatie. De levenscyclus van een order, toegestane procesovergangen of configuratieregels kunnen tot het domeinmodel behoren.

Deze scheiding voorkomt globale stores waarin serverdata, bedrijfslogica en UI-details door elkaar lopen.

Grenzen moeten worden afgedwongen, niet alleen beschreven

Nx laat projecttags en declaratieve dependency constraints definiëren, bijvoorbeeld om imports tussen scopes of librarytypes te blokkeren. `@nx/enforce-module-boundaries` kan imports tijdens linting controleren. [11]

Nx ondersteunt ook meerdere tagdimensies, zodat `scope`, librarytype, stabiliteit of client/server-aspecten apart gemodelleerd kunnen worden. [13]

Frontend-DDD-beslissingen kunnen zo CI-regels worden: `billing` importeert geen interne delen van `identity`, `ui` hangt niet af van `data-access` en `domain` blijft frameworkonafhankelijk.

Frontend en backend kunnen een domein delen zonder identieke modellen

Een Bounded Context is een model- en taalgrens en hoort daarom niet automatisch langs de netwerkgrens tussen browser en server te lopen. Praktische frontend-DDD-bronnen benadrukken dat contexts technische lagen kunnen doorsnijden en gezamenlijk door frontend en backend worden geïmplementeerd. [15][16]

De clientrepresentatie kan toch verschillen van de serverrepresentatie door andere behoeften rond interactie, lokale state en presentatie. Belangrijk zijn correcte semantiek en expliciete mapping, niet het kopiëren van klassen.

Een micro-frontend hoeft ook niet alleen te ontstaan omdat er een Bounded Context bestaat. Domeingrens en deploymentgrens zijn verschillende ontwerpbeslissingen.

Testbaarheid is een sterk argument voor geïsoleerde domeinlogica

Fowler noemt testbaarheid als voordeel van scheiding tussen presentatie en domein. Logica buiten de UI kan worden getest zonder componenten te renderen of van interfacedetails afhankelijk te zijn. [5][6]

In frontend maakt dit snelle tests mogelijk van regels, Value Objects, use cases en statusovergangen als gewoon TypeScript of JavaScript. Componenttests blijven nodig, maar hoeven niet de enige plek te zijn waar bedrijfsregels worden gevalideerd.

Een waarschuwingssignaal is dat iedere wijziging in een bedrijfsregel het mounten van een compleet component en mocks voor router, store, HTTP en browser-API's vereist.

DDD invoeren in een bestaand frontend zonder grote rewrite

DDD Crew adviseert een iteratief proces: domein begrijpen, belangrijke subdomeinen bepalen, verantwoordelijkheden van Bounded Contexts definiëren en daarna het model coderen. [10]

In een bestaand frontend is het veiliger één problematisch bedrijfsgebied te kiezen, taal en grens vast te leggen, presentatie van regels te scheiden, de publieke module-API te definiëren en daarna pas andere features te migreren.

Niet de hele applicatie hoeft te worden gemigreerd. DDD kan worden toegepast waar bedrijfscomplexiteit de modellering terugverdient, terwijl eenvoudige gebieden lichte featuremodules blijven.

  • Begin met een bedrijfsprobleem, niet met mappen.
  • Identificeer termen, regels en dubbelzinnige begrippen.
  • Definieer één Bounded Context en zijn publieke contract.
  • Scheid domeinregels van componenten en transport-DTO's.
  • Voeg alleen tactische patronen toe die concrete complexiteit oplossen.
  • Leg grenzen vast als lint- of CI-regels.
  • Meet dependencies, cycles, regressies en kosten van cross-cutting wijzigingen.
  • Migreer eenvoudige CRUD-gebieden niet alleen voor architecturale symmetrie.

DDD in frontend is zinvol wanneer de client deel uitmaakt van een complex product en niet alleen data rendert. Strategisch DDD levert meestal de meeste waarde: gedeelde taal, contextgrenzen en afdwingbare modulegrenzen. Tactische patronen horen pas toegevoegd te worden waar echte regels, invarianten en gedrag een rijker model rechtvaardigen. Als de applicatie vooral CRUD, formulieren en directe API-weergave bevat, zijn goede modulariteit en scheiding tussen presentatie en data meestal waardevoller dan volledig DDD.

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

Veelgestelde vragen

Is DDD zinvol in frontend?

Ja, wanneer het frontend deelneemt aan een complex domein en profiteert van expliciete grenzen, gedeelde taal of client-side bedrijfsregels. DDD is niet backend-only. [1][2][15]

Moet ieder frontend DDD gebruiken?

Nee. Voor eenvoudige CRUD-applicaties kan volledig tactisch DDD onnodig zijn. Microsoft stelt expliciet dat eenvoudige CRUD-contexten niet altijd een rijk model rechtvaardigen. [9]

Is frontend een aparte Bounded Context?

Niet automatisch. Een Bounded Context begrenst een coherent model en taal, niet een technologie. [3][16]

Moet elke Bounded Context een micro-frontend worden?

Nee. Een domeingrens vereist geen apart deployment. Micro-frontendarchitectuur is een aparte beslissing. [3][15]

Heb ik een map domain nodig?

Nee. DDD schrijft geen verplichte mapstructuur voor. Een map kan helpen, maar creëert op zichzelf geen domeinmodel. [1][5]

Waar hoort bedrijfslogica in frontend?

Buiten pure presentatie, in een expliciet model of use-case-laag van de context. Fowler adviseert presentatie en domeinlogica te scheiden. [5][6]

Kan een API-DTO het domeinmodel zijn?

In een triviaal geval wel, maar het hoeft niet. Verschillende contexten kunnen hetzelfde begrip anders modelleren, waardoor mapping passend kan zijn. [3]

Is Repository nuttig in frontend?

Alleen wanneer het een echt abstractieprobleem rond modeltoegang oplost. Voor eenvoudige datafetching kan een extra Repository overbodig zijn. [1][9]

Zijn Redux, NgRx, Zustand of Signals onderdeel van DDD?

Nee. Het zijn state-managementmechanismen. Ze kunnen domeinstate bevatten, maar definiëren geen model, taal of Bounded Context. [14]

Moet het frontend-domeinmodel identiek zijn aan backend?

Nee. De juiste domeinsemantiek moet behouden blijven, maar de representatie mag aan clientbehoeften worden aangepast. [3]

Hoe dwing je domeingrenzen af in een monorepo?

Nx kan tags en @nx/enforce-module-boundaries gebruiken om ongewenste dependencies automatisch te blokkeren. [11][13]

Waar begin je in een bestaande applicatie?

Bij één complex bedrijfsgebied: begrijp taal, regels en contextgrens. DDD Crew adviseert een iteratief pad van domein naar contexts en code. [10]

Bronnen en referenties

  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

Was dit nuttig?

Ontvang nieuwe artikelen per e-mail

Eén korte e-mail per nieuw blogartikel. Geen spam, uitschrijven in één klik.

We gebruiken je e-mail alleen om nieuwe artikelen te sturen. Geen delen met derden.

Terug naar de blog