Hexagonal Architecture en DDD

Timboektoe

Active member
Crowdfunder FE
Hallo,

Ik vraag me af of er hier op het forum nog mensen bezig zijn met "hexagonal architecture" en "domain-driven design"? :) Clean architecture of "ports and adapters" zijn andere synoniemen voor hexagonal architecture.

Ik werk nu zo een 12 à 13 jaar als softwareontwikkelaar en ben in die periode met heel veel talen (voornamelijk PHP, JavaScript, Python en Java/Kotlin) en frameworks in aanraking gekomen. De laatste 2-3 jaar verdiept in DDD en hexagonal architecture en dat is voor mij toch wel een openbaring geweest: ik zou eigenlijk nooit nog terug willen. Ik vind het spijtig dat ik zo relatief laat in mijn carrière daarmee in aanraking ben gekomen eigenlijk. Aan jongere developers die hier passeren op het forum kan ik alleen maar aanraden om te verdiepen in die topics en niet zozeer een expert proberen te worden in een bepaald framework.

Ik heb wel veel projecten gedaan waar men claimde om DDD toe te passen, terwijl dat in de praktijk helemaal niet het geval was, ofwel niet op de juiste manier. De challenge bij DDD vind ik zelf wel dat veel materie nogal abstract is erover en er soms weinig goede concrete voorbeelden te vinden zijn. Ik speel dan ook met het idee om een blog te starten met hoe ik het heb leren toepassen.

Maar ben vooral benieuwd of er hier mensen mee bezig zijn of interesse hebben en eventueel ook welke nadelen jullie er van ondervinden.

Groeten,
T.
 
Hey,

Ikzelf heb heel wat minder ervaring ( vier jaar aan de slag als developer ).
Toevallig heb ik net de fameuze boeken Domain Driven Design en Implementing Domain Driven Design gekocht.

Geen foute keuze dus :D

Ik werk op een project waar oorspronkelijk DDD werd toegepast maar men zit nu in een team waar niemand nog weet wat het exact is.
Iemand die het al tien jaren toepast en opleidingen hierover geeft heeft me trouwens aangeraden om meteen te starten met "Implementing Domain Driven Design" en het eerste boek te skippen.
 
Als je een team wil introduceren kan ik wel de DDD Accelerator course eens aanraden, een aantal maanden 1 halve dag per week online les met theorie en wat praktijk.

Ik heb er zelf intern ook al een paar workshops rond gegeven, maar waar ik zelf altijd mee worstel is inderdaad goeie voorbeelden vinden die eenvoudig zijn. In grotere projecten is dat vaak makkelijker om die goeie abstractie laag te maken, maar dat vertaald zich niet altijd even goed naar kleinere projecten. Zeker als je naar een microservice verhaal gaat, dan valt voor mij al een stuk van de abstractie weg. Beschouw uw {REST|gRPC|...}-surface dan al als uw ACL bijvoorbeeld. Idem voor repositories. Een deftig ORM zoals Entity Framework is al een soort repository implementatie. Om er dan nog een boven te zetten "om aan de regels te voldoen" is vaak nutteloos werk. En daar ligt voor mij, in kleinere projecten al vaak een soort bottleneck. Je moet in het achterhoofd houden dat heel wat van de theorie en regels ontwikkeld zijn in de tijd dat men grote monolithische applicaties maakten, waar we vandaag de dag veel gedistribueerder werken.

Ik zal het proberen te illustreren met een voorbeeld. Stel ik heb een basic REST API met wat CRUD endpoints. Ik identificeer in mijn domein model een many-to-many relatie: Producten en categorieën. Ik voorzie CRUD endpoints voor beide en kan aan de hand van mijn ORM een type mapping maken van mijn domein model naar mijn database. Zo moet ik niet 2 klassen onderhouden (Product domain en Product database, die identiek gaan zijn.) Eigenlijk zijn dat 2 aparte dingen, dus ik zou 2 aggregate roots moeten hebben. En dan is de link tussen beide aan de hand van id's. Werkt perfect voor mijn CUD, maar niet ideaal voor mijn R, want dan moet ik als ik 1 resultaat wil geven 2 queries uitvoeren. Terwijl mijn ORM en onderliggende DB's dat wel performant in 1 query kunnen doen. Als ik het in 1 query wil doen, dan moet ik een apart read model voorzien. In grotere projecten is dat een opsplitsing die ik vaak maak (zeker als ik daar nog de messaging approach kies en ook een aparte read DB heb), in kleinere is dat wat overhead, want ik moet mogelijks weer 2 klassen/implementaties in-sync houden. Stel dat op mijn categorie een computed property zit en ik wil dat returnen in mijn GET van een product maar ook in de GET van mijn catogorie... Je begrijpt hopelijk een beetje wat ik wil zeggen. :D En dan vind ik het voor mijn huis-, tuin-, en keukenprojecten teveel overhead.

Ik ben daarom vaak fan van een meer pragmatische aanpak, waar we wel functionaliteit naar het domein duwen (zie bvb Jimmy Bogard - Domain-Driven Refactoring - NDC London 2022), maar niet alles altijd volgens "de regels" moeten doen. Echter, dit is een bewuste keuze, en eentje die je alleen mag maken als je begrijpt hoe het zou moeten, en waarom je iets wel of niet doet.
 
Laatst bewerkt:
Iemand die het al tien jaren toepast en opleidingen hierover geeft heeft me trouwens aangeraden om meteen te starten met "Implementing Domain Driven Design" en het eerste boek te skippen.
Can confirm, the blue book is goed, maar redelijk zware kost, the red book is een pak eenvoudiger te begrijpen.
Wat ik ook kan aanraden is Building Microservices van Sam Newman, wat geen puur DDD boek is, maar uw microservice is uw bounded context en wij merken bij onze juniors dat dit vaak conceptueel duidelijker is voor hun. En het komt op hetzelfde neer.

Het is bij mij pas bij het herlezen, na wat ervaring, dat ik Eric Evans zijn boek kunnen doorworstelen heb en het beter begreep.
 
Ik heb er zelf intern ook al een paar workshops rond gegeven, maar waar ik zelf altijd mee worstel is inderdaad goeie voorbeelden vinden die eenvoudig zijn. In grotere projecten is dat vaak makkelijker om die goeie abstractie laag te maken, maar dat vertaald zich niet altijd even goed naar kleinere projecten. Zeker als je naar een microservice verhaal gaat, dan valt voor mij al een stuk van de abstractie weg. Beschouw uw {REST|gRPC|...}-surface dan al als uw ACL bijvoorbeeld. Idem voor repositories. Een deftig ORM zoals Entity Framework is al een soort repository implementatie. Om er dan nog een boven te zetten "om aan de regels te voldoen" is vaak nutteloos werk. En daar ligt voor mij, in kleinere projecten al vaak een soort bottleneck. Je moet in het achterhoofd houden dat heel wat van de theorie en regels ontwikkeld zijn in de tijd dat men grote monolithische applicaties maakten, waar we vandaag de dag veel gedistribueerder werken.
Ik lees hier een paar dingen.

Ten eerste zouden de domain entities (aggregates) volledig moeten losstaan van de persistence layer. Maakt dat overhead? Absoluut, maar ik vind in een bedrijfscontext (waar maintainability toch wel belangrijk is) het de kost wel waard. Het domain van een toepassing zou compleet losgekoppeld moeten zijn van technologie en frameworkjes. Iets wat je in het domain zou kunnen modelleren als "many to many" hoeft in de persistence layer niet persé zo te moeten zijn. Ik heb heel veel projecten gedaan waar ze claimden DDD toe te passen, maar wat deden ze in de praktijk? Hun "domain entities" waren gewoon de mapping van hun database tables en in de ORM entities voegden ze dan hier en daar de "business logic" toe. Je zou dat kunnen interpreteren als "een vorm van DDD", maar je blijft gekoppeld aan de technologie. Als je dus ooit het ORM wil upgraden of swappen met een andere technologie, kan dat wel eens pijnlijk worden met de approach van domain en ORM te willen combineren...

Ten tweede is een monolitische applicatie niet per definitie iets slecht. Als je in (sub)domains gaat denken, dan is authenticatie+autorisatie al vaak iets wat je kan afsplitsen in een aparte microservice (vaak in de vorm van een "identity provider"). En DDD + hexagonal architecture leent zich juist héél goed om een toepassing te bouwen die andere microservices gebruikt: in het domain wordt de microservice (extern systeem) dan een zgn. "domain service" waar je enkel de input en ouput definieert in een interface. In een "backend adapter" (ook wel "driven adapter" genoemd) kan je dan die "domain service" implementeren tegen de eigenlijke microservice (of dat nu een REST call, SOAP call of anders is). Op die manier blijft het domain wel abstract t.o.v. de technische details van de microservice in kwestie. Bij Wikipedia staat op de pagina van hexagonal architeture zelfs het volgende:
According to some authors, the hexagonal architecture is at the origin of the microservices architecture.
Maar let's face it: als je geen Google of geen Netflix bent, heb je microservices waarschijnlijk niet nodig. Ik ben in genoeg projecten terecht gekomen waar microservices compleet overbodig waren en voor veel meer problemen zorgde dan dat het iets opbracht. De IT-sector is nogal gevoelig aan het "bouwen van luchtkastelen" en die hype/trend rond microservices is daar zeker een goed voorbeeld van IMO. :)

Ik zal het proberen te illustreren met een voorbeeld. Stel ik heb een basic REST API met wat CRUD endpoints. Ik identificeer in mijn domein model een many-to-many relatie: Producten en categorieën. Ik voorzie CRUD endpoints voor beide en kan aan de hand van mijn ORM een type mapping maken van mijn domein model naar mijn database. Zo moet ik niet 2 klassen onderhouden (Product domain en Product database, die identiek gaan zijn.) Eigenlijk zijn dat 2 aparte dingen, dus ik zou 2 aggregate roots moeten hebben. En dan is de link tussen beide aan de hand van id's. Werkt perfect voor mijn CUD, maar niet ideaal voor mijn R, want dan moet ik als ik 1 resultaat wil geven 2 queries uitvoeren. Terwijl mijn ORM en onderliggende DB's dat wel performant in 1 query kunnen doen. Als ik het in 1 query wil doen, dan moet ik een apart read model voorzien. In grotere projecten is dat een opsplitsing die ik vaak maak (zeker als ik daar nog de messaging approach kies en ook een aparte read DB heb), in kleinere is dat wat overhead, want ik moet mogelijks weer 2 klassen/implementaties in-sync houden. Stel dat op mijn categorie een computed property zit en ik wil dat returnen in mijn GET van een product maar ook in de GET van mijn catogorie... Je begrijpt hopelijk een beetje wat ik wil zeggen. :D En dan vind ik het voor mijn huis-, tuin-, en keukenprojecten teveel overhead.
Ik begrijp wat je wil zeggen. Wij maken ook een opsplitsing tussen een "read model" en een "write model". In het "write model" stockeren wij de domain entities en leggen we enkel primary en foreign keys. In het "read model" maken we dan een "projectie" van een bepaalde domain entity, aangevuld met extra kolommen met velden van gerelateerde domain entities. Daar definiëren we dan meer indexen op verschillende kolommen, omdat dat read model ook gebruik wordt voor veel read queries met veel parameters om te filteren. Het read model wordt dan altijd bijgewerkt o.b.v. domain events: het read model loopt dus, asynchroon, altijd een beetje achter ("eventual consistency"), maar in de praktijk is dat heel vaak geen probleem.

Maar je vermeldt zeker iets wat ik ook nadelig/overhead vindt aan DDD en hexagonal architecture: je krijgt doorheen alle layers een "data transfer object" (bijv. een product met ID, title, en prijs) en je moet dat doorheen alle layers altijd "mappen". Dat zorgt op den duur voor heel veel monkey code... In mijn eigen project durf ik dan wel eens de trade-off te namen van de DTO's te centraliseren in 1 module i.p.v. altijd maar door "mappen". Het domain op zich blijf ik wel zinvol vinden om dat los te koppelen van ORM.

Je moet ook vertrekken vanuit een logisch, duidelijk domain model. Als je een RESTful API zou willen bouwen volgens het boekje, dan moet je alles mooi organiseren in "CRUD REST resources". We weten allemaal als developer dat dat voor veel toepassingen een utopie is: niet elk domain of business model laat zich gemakkelijk "vertalen" in REST resources met CRUD operaties. En als je dat juist wel ver begint door te drijven, begint het soms wat onlogisch aan te voelen.

Ik ben daarom vaak fan van een meer pragmatische aanpak, waar we wel functionaliteit naar het domein duwen (zie bvb Jimmy Bogard - Domain-Driven Refactoring - NDC London 2022), maar niet alles altijd volgens "de regels" moeten doen. Echter, dit is een bewuste keuze, en eentje die je alleen mag maken als je begrijpt hoe het zou moeten, en waarom je iets wel of niet doet.
Uiteindelijk is dat ook het doel van DDD: de complexiteit in het domain houden en daarmee de maintainability verhogen. En daar blijft veel grijze zone en ruimte voor interpretatie van alles. :) Maar je moet doen waar je jezelf (en als team) goed bij voelt. Voor mijn eigen projecten pas ik ook DDD toe (zelfs al single developer vind ik het zinvol), maar ik durf al sneller een trade-off te pakken. Ik heb voor mijn eigen projecten nu eenmaal ook geen 8 uur per dag om aan te besteden...
 
Can confirm, the blue book is goed, maar redelijk zware kost, the red book is een pak eenvoudiger te begrijpen.
Wat ik ook kan aanraden is Building Microservices van Sam Newman, wat geen puur DDD boek is, maar uw microservice is uw bounded context en wij merken bij onze juniors dat dit vaak conceptueel duidelijker is voor hun. En het komt op hetzelfde neer.

Het is bij mij pas bij het herlezen, na wat ervaring, dat ik Eric Evans zijn boek kunnen doorworstelen heb en het beter begreep.
Hier heb ik er dezelfde ervaring mee. Ik ben 7 à 8 jaar geleden eens naar een lecture geweest over DDD en toen raadden ze ook het blauwe boek aan. Ik ben die kort daarna beginnen lezen, maar het was voor mij allemaal te theoretisch en niet praktisch genoeg.

Het is pas ongeveer 3 jaar geleden dat ik hem terug uit de kast heb gehaald voor een nieuw project. Ik heb toen het geluk gehad dat ik veel kon doen aan pair/mob programming met mensen veel meer ervaring hadden met DDD dan mij (collega's van het kaliber dat elk jaar naar DDD Europe gaat - zeer verrijkend geweest voor mij :)). Sinsdien was het voor mij elke dag DDD met constante "feedback loop" van mensen met veel ervaring ermee.
 
Ik zal het proberen te illustreren met een voorbeeld. Stel ik heb een basic REST API met wat CRUD endpoints. Ik identificeer in mijn domein model een many-to-many relatie: Producten en categorieën.
Het Crud gegeven in een Domain Driven omgeving. Ik blijf het een raar gegeven vinden. Waar DDD een vertaling van je real life problemen vertaald naar code is CRUD eigenlijk het tegenovergestelde.
En een many-to-many: Khorikov heeft er ooit eens een goed artikel over geschreven. (zijn videos op Pluralsight over ddd zijn ook best goed).
Het is de kunst om zo weinig mogelijk many to many relaties te hebben.
https://enterprisecraftsmanship.com/posts/modeling-relationships-in-ddd-way/



Can confirm, the blue book is goed, maar redelijk zware kost, the red book is een pak eenvoudiger te begrijpen.
Voor mij zijn ze referentieboeken. DDD en alles er rond leer je door te doen, fouten te maken, en samen als team te leren uit die fouten. En vooral goede documentatie hebben. Zo is er bij mijn huidige klant geen ruimte voor interpretatie inzake het wat en waar je dingen gaat implementeren. (EventStorming sessies kunnen hier echt goed bij helpen)


Als je dus ooit het ORM wil upgraden of swappen met een andere technologie, kan dat wel eens pijnlijk worden met de approach van domain en ORM te willen combineren...
Volledig akkoord met je visie over domain en ORM gescheiden te houden. Wij hebben het al een een paar keer moeten doen. Maar tbh, het merendeel van de projecten blijven vast zitten aan hun ORM.

Ik ben in genoeg projecten terecht gekomen waar microservices compleet overbodig waren en voor veel meer problemen zorgde dan dat het iets opbracht. De IT-sector is nogal gevoelig aan het "bouwen van luchtkastelen" en die hype/trend rond microservices is daar zeker een goed voorbeeld van IMO.
Volledig akkoord. Zeker als je de eindmeet ziet van een project is het vaak overkill. Als je van het begin weet dat je project modulair gaat worden zou ik het op zijn minst proberen om de DDD principes heel strikt toe te passen. Je kan altijd voor een monolith gaat met DDD en op een gegeven moment gaan "af pellen". Probleem bij DDD en microservices is dat de analyse vaak niet deftig is gedaan, met alle gevolgen van dien (chatty services, domain leaking,...)

In het "read model" maken we dan een "projectie" van een bepaalde domain entity, aangevuld met extra kolommen met velden van gerelateerde domain entities. Daar definiëren we dan meer indexen op verschillende kolommen, omdat dat read model ook gebruik wordt voor veel read queries met veel parameters om te filteren. Het read model wordt dan altijd bijgewerkt o.b.v. domain events: het read model loopt dus, asynchroon, altijd een beetje achter ("eventual consistency"), maar in de praktijk is dat heel vaak geen probleem.
In theorie hoeft je read model zelfs geen projectie te zijn van een bepaald domain entity. Het belangrijkste is dat het performant is (dus geen fk en dergelijke). Wij hanteren in de meeste gevallen 1 projectie per scherm. Je eventual consistency is iets waar men mee moet leren leven, maar dat valt idd wel mee. Het lastige aan eventual consistency en domain events, is dat je vaak, niet out of de box controle hebt over de volgorde van afhandeling. Hoeft niet problematisch te zijn, maar het is wel iets waar je in sommige gevallen rekening mee moet houden.

Maar je vermeldt zeker iets wat ik ook nadelig/overhead vindt aan DDD en hexagonal architecture: je krijgt doorheen alle layers een "data transfer object" (bijv. een product met ID, title, en prijs) en je moet dat doorheen alle layers altijd "mappen". Dat zorgt op den duur voor heel veel monkey code... In mijn eigen project durf ik dan wel eens de trade-off te namen van de DTO's te centraliseren in 1 module i.p.v. altijd maar door "mappen". Het domain op zich blijf ik wel zinvol vinden om dat los te koppelen van ORM.
Een soort contract module dan?
 
Het Crud gegeven in een Domain Driven omgeving. Ik blijf het een raar gegeven vinden. Waar DDD een vertaling van je real life problemen vertaald naar code is CRUD eigenlijk het tegenovergestelde.
En een many-to-many: Khorikov heeft er ooit eens een goed artikel over geschreven. (zijn videos op Pluralsight over ddd zijn ook best goed).
Het is de kunst om zo weinig mogelijk many to many relaties te hebben.
https://enterprisecraftsmanship.com/posts/modeling-relationships-in-ddd-way/
We moeten, als softwareontwikkelaars, ook gewoon af van die mentaliteit van "denken in tabelletjes". Je moet vertrekken van een logisch opgebouwd domain model. De eventuele "value objects" op een domain entity moet je ook niet persé afsplitsen in een aparte database-tabel in de persistence layer: zo slaan wij sommige van die value objects gewoon op als JSON-string in de database. Of kloppen we de velden van een value object gewoon "plat" tot op hetzelfde niveau van de rest van de velden van een domain entity.

Ik denk ook dat het "denken in tabelletjes" vooral een restant/mentaliteit is van wat je op de hogeschool/unief krijg aangeleerd bij een vak à la "databases": daar wordt altijd heel vaak de nadruk gelegd op relationele databases en alles mooi afsplitsen in tabellen en relaties leggen, enz. enz. enz. Maar in de praktijk heeft dat niet voor elke soort informatie/model zin om het op die manier te persisteren. Je zou, extreem genomen, uw domain entities ook gewoon kunnen persisteren in een CSV-bestand of plain text file. Je moet in DDD compleet los denken van de technologie en gewoon focussen op een juist domain model.

En dan worden er tegenwoordig vooral REST API's gebouwd die je rond REST CRUD resources zou moeten oriënteren, maar in de praktijk vloekt dat gewoon vaak met het interne domain model en moet je dan zondigen tegen de "REST constraints", bijv. een POST/PUT/PATCH gebruiken voor een bepaalde handeling/actie in het domein. Of een andere klassieker: toch een POST moeten implementeren voor een leesoperatie, omdat je query zoveel payload nodig heeft, dat het niet in als query parameter kan bij een GET.
Volledig akkoord met je visie over domain en ORM gescheiden te houden. Wij hebben het al een een paar keer moeten doen. Maar tbh, het merendeel van de projecten blijven vast zitten aan hun ORM.
In de praktijk is het vaak zo dat er een ORM-library wordt geïmplementeerd, om die achteraf eigenlijk nooit meer te veranderen. De enigste reden die ik daarvoor kan bedenken (die business value zou leveren) is dat het geïmplementeerde ORM zoveel technical debt zou geven, dat een migratie naar ander ORM toch de moeite waard is. Maar vandaag de dag zijn er genoeg degelijke en volwassen ORM libraries beschikbaar (ik kijk dan vooral naar Hibernate in de enterprise space en SQLAlchemy en/of Django ORM - ik heb de laatste 2 jaar ook heel goede ervaringen met jOOQ).

In theorie hoeft je read model zelfs geen projectie te zijn van een bepaald domain entity. Het belangrijkste is dat het performant is (dus geen fk en dergelijke). Wij hanteren in de meeste gevallen 1 projectie per scherm. Je eventual consistency is iets waar men mee moet leren leven, maar dat valt idd wel mee. Het lastige aan eventual consistency en domain events, is dat je vaak, niet out of de box controle hebt over de volgorde van afhandeling. Hoeft niet problematisch te zijn, maar het is wel iets waar je in sommige gevallen rekening mee moet houden.
Wij passen command-query separation toe en daarmee is in de code het read en write model van elkaar gescheiden: welke tabellen je dan achterliggend aanspreekt om het read/write model te voeden, kan je nog kiezen. Ofwel lees je gewoon van de "write tables", ofwel lees je van een projectie. Eventual consistency is vaak geen probleem, maar het hangt ook van de toepassing af. Zo zijn wij nu een REST API aan het bouwen die geconsumeerd wordt door andere backends. Puur machine to machine communication dus. Daar is de eventual consistency minder problematisch. Maar als onze REST API rechtstreeks zou geconsumeerd worden door, bijv. een Angular app, dan was het wel een concern en minder wenselijk in sommige gevallen.

Een soort contract module dan?
Wij werken met een vocabulary module: hierin worden alle "domain primitives" gezet en die zijn toegankelijk voor alle modules in het project. Voorbeeld van domain primitives: een schoenmaat, een e-mailadres, een ISBN-nummer, een dossiernummer, ... Je kan die vocabulary ook "misbruiken" door daar de DTOs te zetten, die bijgevolg ook toegankelijk zijn voor alle modules. Dan moet je een pak minder mappen natuurlijk, maar ben je vocabulary ook een beetje als "vuilbak" aan 't gebruiken...

In een vorig project werkten ze dan inderdaad met een "contract module": die bevatte dan alleen maar een boel interfaces die typisch alleen maar GETTERS hadden. Alle DTOs binnen heel het project, in verschillende modules, implementeerden die interfaces dan, zodat er wel een soort van interchangeability onstond, zonder dat je altijd moest "mappen".

@Ayrossi Ik hoop ook dat je kan samenwerken of op zijn minst wat kan "sparren" met dieje mens die het 10 jaar toepast, want je gaat het meest bijleren van mensen die het al eens gedaan hebben. Het pairen/mobben van de laatste jaren heeft mij toch enorm geholpen om het te leren.
 
Laatst bewerkt:
@Ayrossi Ik hoop ook dat je kan samenwerken of op zijn minst wat kan "sparren" met dieje mens die het 10 jaar toepast, want je gaat het meest bijleren van mensen die het al eens gedaan hebben. Het pairen/mobben van de laatste jaren heeft mij toch enorm geholpen om het te leren.

Volledig mee akkoord. Theorie en tutorials zijn tof om de concepten te ontdekken. Maar het samenwerken op enterprise schaal de manier om echt iets te leren.
De maanden die ik gespendeerd heb aan het verzamelen van informatie aan bv. event sourcing (in al zijn glorie) zijn peanuts vergeleken met wat je leert gedurende 1 week in zo een enterprise omgeving.
Als je de concepten wat kent en de motivatie is er zal je tijdens een sollicitatie niet afgeschoten worden omdat je nog geen echte ervaring hebt opgedaan. Zo een projecten hebben vaak een steilere leercurve om er in te rollen, dus bedrijven zien dit vaak als een investering.
 
Het Crud gegeven in een Domain Driven omgeving. Ik blijf het een raar gegeven vinden. Waar DDD een vertaling van je real life problemen vertaald naar code is CRUD eigenlijk het tegenovergestelde.
Maar dat is nu eenmaal de vertaalslag die velen in het begin maken, en zeker naar een eenvoudig voorbeeld zoekend is de vraag dan nogal snel: "waarom al die DDD overhead als het simpel ook kan?". 't Is dat wat ik probeer aan te geven met mijn voorbeeld. Niet tegen DDD, absoluut niet, maar niet elke service is even praktisch in een message based architectuur te implementeren, of de overhead waard. (Dan heb ik het wel eerder over hobby projectjes, dan enterprise omgevingen, waar kost en resources vaak belangrijker zijn.)
En een many-to-many: Khorikov heeft er ooit eens een goed artikel over geschreven. (zijn videos op Pluralsight over ddd zijn ook best goed).
Het is de kunst om zo weinig mogelijk many to many relaties te hebben.
https://enterprisecraftsmanship.com/posts/modeling-relationships-in-ddd-way/
Zo weinig mogelijk, ja, maar niet altijd haalbaar.

We moeten, als softwareontwikkelaars, ook gewoon af van die mentaliteit van "denken in tabelletjes". Je moet vertrekken van een logisch opgebouwd domain model. De eventuele "value objects" op een domain entity moet je ook niet persé afsplitsen in een aparte database-tabel in de persistence layer: zo slaan wij sommige van die value objects gewoon op als JSON-string in de database. Of kloppen we de velden van een value object gewoon "plat" tot op hetzelfde niveau van de rest van de velden van een domain entity.

Ik denk ook dat het "denken in tabelletjes" vooral een restant/mentaliteit is van wat je op de hogeschool/unief krijg aangeleerd bij een vak à la "databases": daar wordt altijd heel vaak de nadruk gelegd op relationele databases en alles mooi afsplitsen in tabellen en relaties leggen, enz. enz. enz. Maar in de praktijk heeft dat niet voor elke soort informatie/model zin om het op die manier te persisteren. Je zou, extreem genomen, uw domain entities ook gewoon kunnen persisteren in een CSV-bestand of plain text file. Je moet in DDD compleet los denken van de technologie en gewoon focussen op een juist domain model.
Niets aan toe te voegen, maar het is wel moeilijker te verkopen. Zeker naar management die huiveren bij het hele eventual consistency en niet relationele database verhaal.
En dan worden er tegenwoordig vooral REST API's gebouwd die je rond REST CRUD resources zou moeten oriënteren, maar in de praktijk vloekt dat gewoon vaak met het interne domain model en moet je dan zondigen tegen de "REST constraints", bijv. een POST/PUT/PATCH gebruiken voor een bepaalde handeling/actie in het domein. Of een andere klassieker: toch een POST moeten implementeren voor een leesoperatie, omdat je query zoveel payload nodig heeft, dat het niet in als query parameter kan bij een GET.
Hier valt over te discussiëren, en discussieert het internet al jaren over. Een zoek opdracht is een leesopdracht, maar wel een actie, dus is een POST gerechtvaardigd.
Wij passen command-query separation toe en daarmee is in de code het read en write model van elkaar gescheiden: welke tabellen je dan achterliggend aanspreekt om het read/write model te voeden, kan je nog kiezen. Ofwel lees je gewoon van de "write tables", ofwel lees je van een projectie.
Hier ben ikzelf ook enorme fan van. Al is het maar in-process om mijn code te scheiden en gaat dat achterliggend nog altijd naar dezelfde database. Die splitsing is er dan toch al.
 
Laatst bewerkt:
Maar dat is nu eenmaal de vertaalslag die velen in het begin maken, en zeker naar een eenvoudig voorbeeld zoekend is de vraag dan nogal snel: "waarom al die DDD overhead als het simpel ook kan?". 't Is dat wat ik probeer aan te geven met mijn voorbeeld. Niet tegen DDD, absoluut niet, maar niet elke service is even praktisch in een message based architectuur te implementeren, of de overhead waard. (Dan heb ik het wel eerder over hobby projectjes, dan enterprise omgevingen, waar kost en resources vaak belangrijker zijn.)
Veel (business) analysten of product owners zijn ook vaak (maar zeker niet altijd) informaticus van opleiding en tegen dat die spreken met ontwikkelaars, hebben ze dikwijls al een halfgebakken oplossing uitgewerkt die vaak ook gebaseerd is op de mentaliteit van "denken in tabellen" (ook dikwijls te wijten aan de schoolse kennis/mentaliteit). Terwijl die juist moeten afkomen met een analyse van "business problemen" en niet zozeer concrete, technische oplossingen.

Ik was bij het opstarten van deze thread eigenlijk vooral benieuwd naar wat anderen benoemen als nadeel van DDD en ik ben het er zeker mee eens dat het allemaal overhead creëert, maar in een bedrijfscontext (waar maintainability en overdraagbaarheid toch niet onbelangrijk is), vind ik het die overhead wel waard. DDD "zit" ook niet alleen in de code: het is ook belangrijk om, als team, te werken aan een "ubiquitous language" rond de business problemen die je wil oplossen. Ook dat vergt tijd en veel communicatie tussen verschillende teamleden om tot een "mentaal model" te komen waar iedereen mee is. Terwijl in veel organisaties vaak verschillende mensen van eenzelfde team nogal hard "op een eiland" leven.
Niets aan toe te voegen, maar het is wel moeilijker te verkopen. Zeker naar management die huiveren bij het hele eventual consistency en niet relationele database verhaal.
Asynchroniteit / eventual consistency krijg je vaak wel uitgelegd/verkocht naar mijn ervaring, want je hebt dat vaak wel nodig voor scalability. De niet-relationele database is idd moeilijker... Vaak ook omdat ze bijvoorbeeld al een Oracle / SQL Server license hebben van (absurd) veel geld per jaar en een team van database/system administrators. oi22 En dan "moet" dat maar gebruikt worden. En ik wil ook zeker niet beweren dat een relationele database per definitie slecht is hoor, maar je moet gewoon wel goed nadenken over de consequenties van indexen, lezen van "write model" i.p.v. "read model" apart maken, relaties al dan niet leggen, enz.
Hier valt over te discussiëren, en discussieert het internet al jaren over. Een zoek opdracht is een leesopdracht, maar wel een actie, dus is een POST gerechtvaardigd.
Nog een eeuwige klassieker: PUT vs PATCH. Volgens het REST-boekje:
  • PUT: dient om een resource te VERVANGEN in zijn geheel;
  • PATCH: dient om een partiële update door te voeren van een resource.
Toch worden ze allebei gewoon door elkaar gebruikt voor gewoon hetzelfde: de U in CRUD. Dat "vervangen van een resource" is ook vaak iets wat je niet kunt vertalen van/naar een business probleem.
 
Terug
Bovenaan