Overzicht: wat is Domain-Driven Design (DDD)?
Waarom voelt complexe software soms als een verzameling losse features in plaats van een logisch geheel dat het domein van de organisatie echt weerspiegelt? Domain-Driven Design, vaak afgekort als DDD, is een aanpak om precies dat probleem op te lossen.
DDD is een manier van denken over software, waarin het domein (het bedrijfsprobleem dat je oplost) centraal staat. Het is geen framework of library, maar een set van principes, patronen en taalafspraken die helpen om code, architectuur en businesskennis strak op elkaar af te stemmen.
Kernpunten in dit artikel:
Heldere definitie van Domain-Driven Design en het doel ervan
Uitleg van strategische patronen zoals domeinen, subdomeinen en bounded contexts
Uitleg van tactische bouwstenen zoals entities, value objects, aggregates, repositories en domain events
Het belang van de ubiquitous language en samenwerking tussen domeinexperts en developers
Hoe DDD zich verhoudt tot microservices, legacy monolieten en moderne stacks zoals Laravel
Praktische richtlijnen om te bepalen wanneer DDD wel en niet zinvol is
Aan het eind heb je een concreet begrippenkader en kun je gefundeerd beslissen hoe en in welke mate je DDD binnen je eigen project wilt inzetten.
Wat is Domain-Driven Design precies?
Domain-Driven Design (DDD) is een aanpak voor het ontwerpen en bouwen van software, waarbij het probleemdomein van de business centraal staat. Met domein bedoel je het inhoudelijke werkgebied waar de software over gaat, bijvoorbeeld vermogensbeheer, studentenhuisvesting, e-commerce of logistiek.
De kern van DDD is dat je niet begint met technische lagen, maar met het begrijpen en modelleren van het domein zelf. Dat model vertaal je vervolgens in software, zodat de structuur van je code de structuur van het bedrijfsprobleem weerspiegelt. DDD richt zich dus zowel op taal en samenwerking als op architectuur en ontwerp.
Belangrijk is dat DDD geen kant en klaar framework is. Het is een verzameling concepten en patronen die je op verschillende manieren kunt combineren. Je kunt DDD toepassen binnen een Laravel monoliet, in een set microservices, of zelfs in een bestaand legacy systeem dat je stap voor stap wilt opknippen in logischere componenten.
Een toegankelijke video-introductie die veel van deze concepten visueel maakt, is deze uitleg: https://www.youtube.com/watch?v=pMuiVlnGqjk. Die sluit goed aan op de begrippen die in dit artikel worden uitgewerkt.
Waarom is DDD ontstaan?
DDD is ontstaan vanuit de observatie dat veel softwareprojecten falen of onnodig complex worden, niet door technische beperkingen, maar door misverstanden tussen domeinexperts en ontwikkelaars. Business gebruikt bepaalde woorden, IT gebruikt andere woorden, en ergens in de vertaling gaat de essentie verloren.
Typische symptomen zijn:
Dezelfde term betekent in verschillende teams iets anders
Businessregels zitten verspreid in controllers, services en UI code
Aanpassingen in het domein leiden tot onvoorspelbare bijeffecten in de code
Niemand kan meer uitleggen waarom het systeem werkt zoals het werkt
DDD probeert deze kloof te dichten door een directe koppeling te leggen tussen de taal van het domein en de structuur van de software. De voordeel is dat domeinkennis niet alleen in hoofden van mensen zit, maar ook expliciet en consistent verankerd is in het model en in de code.
Strategische DDD concepten: domeinen, subdomeinen en bounded contexts
Op strategisch niveau helpt DDD je om grote, complexe systemen logisch op te knippen. Dat gebeurt via begrippen als domein, subdomein en bounded context. Deze concepten bepalen waar bepaalde modellen en definities gelden en waar niet.
Een domein is het totale probleemgebied waar je applicatie over gaat. Voor een vastgoedplatform kan dat bijvoorbeeld woningverhuur zijn, inclusief onderwerpen als huurcontracten, betalingen, matchen van vraag en aanbod en compliance rond regelgeving.
Binnen dat grote domein maak je onderscheid in subdomeinen. Dit zijn logische deelgebieden met eigen doelen en terminologie, zoals verhuurbeheer, betalingen of klantportaal. Elk subdomein heeft andere prioriteit en complexiteit, en DDD moedigt je aan om juist de kernsubdomeinen met de meeste strategische waarde het beste te modelleren.
De term bounded context is vervolgens de afbakening van één specifiek model en één specifieke taal. Binnen een bounded context hebben termen een scherpe, consistente betekenis. Buiten die context mag dezelfde term iets anders betekenen. Een voorbeeld is het begrip klant: in een facturatiecontext heeft klant andere attributen en regels dan in een marketingcontext, zelfs als beide over dezelfde persoon gaan.
Tussen bounded contexts bestaan relaties. Die kun je expliciet maken in context maps, waarin je weergeeft hoe modellen op elkaar aansluiten, welke teams verantwoordelijk zijn en via welke integraties gegevens uitgewisseld worden. Daarbij spelen patronen als conformist, anticorruption layer en shared kernel een rol, die helpen structureren hoe sterk of zwak contexten met elkaar gekoppeld zijn.
DDD sluit op dit strategische niveau goed aan op moderne architectuurstijlen zoals microservices. Elke microservice kan één bounded context representeren, met een eigen model en dataopslag. Maar DDD is daar niet toe beperkt, je kunt dezelfde begrippen ook toepassen binnen een modulair monoliet, waarin bijvoorbeeld verschillende Laravel modules of packages elk een bounded context vormen.
Ubiquitous language: gedeelde taal binnen de context
Een fundamenteel onderdeel van DDD is de ubiquitous language, de gedeelde, alomtegenwoordige taal die door zowel ontwikkelaars als domeinexperts gebruikt wordt. Deze taal ontstaat niet uit documenten, maar uit gesprekken, workshops en iteratief modelleren.
De regels zijn eenvoudig maar streng:
Begrippen uit de ubiquitous language krijgen een precieze definitie binnen de bounded context
Deze begrippen komen terug in de code, in klassennamen, methoden en modules
Wanneer de taal verandert, moet het model en uiteindelijk de code mee veranderen
Het praktische effect is groot. Als in een domein is afgesproken dat er sprake is van een contractaanvraag, een conceptcontract en een definitief contract, dan zie je dat terug in de types en services. Dat maakt discussies over gedrag concreet en voorkomt dat er onzichtbare logica sluipt in generieke namen zoals data, item of record.
Tactische DDD bouwstenen: entities, value objects en aggregates
Naast de strategische patronen kent DDD ook tactische bouwstenen, die helpen om het domeinmodel concreet te maken in code. De bekendste begrippen zijn entities, value objects, aggregates, repositories en domain events. Deze concepten zijn taalonafhankelijk en kunnen prima toegepast worden in bijvoorbeeld PHP of Laravel.
Een entity is een object met een stabiele identiteit over tijd. Het verandert van staat, maar blijft dezelfde entiteit. Een huurder, een order of een gebruiker zijn typische entities. De identiteit wordt meestal gerepresenteerd door een id, dat ook gebruikt wordt in referenties en relaties.
Een value object representeert een waarde zonder eigen identiteit. Voorbeelden zijn geldbedragen, perioden, adressen of emailadressen. Value objects zijn onveranderlijk, als een waarde wijzigt, maak je een nieuw object. Ze worden vergeleken op basis van hun waarde en niet op basis van een id. Dit maakt ze heel geschikt om domeinlogica te encapsuleren, bijvoorbeeld validatieregels of berekeningen.
Aggregates zijn clusters van entiteiten en value objects die je als één consistent geheel behandelt. Binnen een aggregate is er één centrale entiteit, de aggregate root. Toegang tot andere onderdelen van de aggregate verloopt altijd via die root. Dit dwingt consistency rules af, zeker wanneer je met transacties en concurrency te maken hebt.
Een vereenvoudigd voorbeeld in PHP stijl kan er zo uitzien:
final class Order
{
private OrderId $id;
/** @var OrderLine[] */
private array $lines = [];
private OrderStatus $status;
public function addLine(ProductId $productId, Quantity $quantity): void
{
if (!$this->status->isDraft()) {
throw new DomainException('Only draft orders can be modified.');
}
$this->lines[] = new OrderLine($productId, $quantity);
}
public function confirm(): void
{
if ($this->status->isConfirmed()) {
throw new DomainException('Order already confirmed.');
}
$this->status = OrderStatus::confirmed();
// Hier zou je een domain event kunnen raisen, bijvoorbeeld OrderConfirmed
}
}
In dit voorbeeld zie je meerdere DDD patronen terug. De `Order` is de aggregate root, `OrderLine` is een onderliggend onderdeel, en klassen als `ProductId`, `Quantity` en `OrderStatus` kunnen als value objects worden gemodelleerd met hun eigen regels.
Repositories en domain events
Repositories vormen de brug tussen het domeinmodel en de onderliggende dataopslag. In DDD formaliseren ze een domeincollectie met querymethoden die passen bij het domein. In plaats van generieke databasecalls definieer je interfaces zoals `OrderRepository` met methoden als `byId`, `byCustomer` of `save`. De implementatie kan gebruikmaken van bijvoorbeeld Eloquent in Laravel, maar die details blijven buiten het domein.
Domain events zijn berichten die aangeven dat er iets belangrijks in het domein is gebeurd. Ze helpen losse onderdelen van het domein en van het systeem te koppelen zonder harde afhankelijkheden. Denk aan events als `OrderConfirmed`, `CustomerRegistered` of `PaymentFailed`. Deze events kunnen binnen dezelfde bounded context worden verwerkt, of over contextgrenzen heen, bijvoorbeeld door integraties of messaging.
Samen zorgen deze tactische bouwstenen voor een model dat dicht bij de business staat, maar toch technisch solide blijft. Ze maken expliciet waar gedrag thuishoort en waar data slechts een representatie is van domeinconcepten, niet andersom.
Wat is Domain-Driven Design in eenvoudige woorden?
Domain-Driven Design is een manier van software ontwerpen waarbij je vertrekt vanuit het bedrijfsprobleem en de taal van de organisatie. Je bouwt eerst een helder domeinmodel en past daar je code en architectuur op aan, in plaats van andersom. Daardoor sluit software beter aan op hoe het bedrijf echt werkt.
Is DDD een framework of een tool die ik kan installeren?
Nee, DDD is geen framework, library of specifieke tool. Het is een verzameling principes, patronen en denkmodellen. Je kunt DDD toepassen in allerlei technische omgevingen, bijvoorbeeld binnen een Laravel applicatie, een microserviceslandschap of zelfs een bestaande monoliet. De tools die je gebruikt zijn ondergeschikt aan de manier waarop je het domein modelleert.
Wanneer is het zinvol om Domain-Driven Design te gebruiken?
DDD wordt vooral interessant zodra je domein complex wordt en veel bedrijfsregels bevat. Uit ervaring blijkt dat projecten in finance, legal, zorg, logistiek of vastgoed vaak profiteren omdat daar ingewikkelde processen en regelgeving spelen. Voor heel eenvoudige CRUD applicaties kan DDD juist te zwaar zijn, dan is een eenvoudiger architectuurpatroon vaak voldoende.
Hoe verhoudt DDD zich tot microservices?
DDD en microservices versterken elkaar, maar zijn niet hetzelfde. Microservices gaan over het opdelen van een systeem in zelfstandig deploybare services. DDD helpt bepalen hoe je die grenzen slim legt, bijvoorbeeld langs bounded contexts. Teams die dit combineren merken in de praktijk dat de services beter aansluiten op echte domeinverantwoordelijkheden en minder willekeurig gesneden zijn.
Kan ik DDD toepassen in een bestaande legacy monoliet?
Ja, dat kan, en het is zelfs een veelvoorkomend scenario. Een praktische aanpak is om eerst de domeinen, subdomeinen en bounded contexts te identificeren, en dan stap voor stap modules of componenten te herstructureren volgens DDD principes. Vaak wordt begonnen met een kernsubdomein dat veel pijn veroorzaakt, zodat je snel merkt dat de nieuwe structuur meer overzicht en stabiliteit geeft.
Hoe begin ik concreet met DDD binnen mijn team?
Een goed startpunt is om gezamenlijke sessies met domeinexperts en ontwikkelaars te organiseren, bijvoorbeeld event storming of modelleersessies met whiteboards. Daarin breng je processen, gebeurtenissen en begrippen in kaart. Daarna vertaal je de belangrijkste concepten naar entities, value objects en aggregates in code. Teams die dit stap voor stap doen, merken dat de ubiquitous language vanzelf groeit en dat discussies over gedrag concreter en productiever worden.
Heb ik voor DDD altijd volledige buy-in van de hele organisatie nodig?
Volledige buy-in helpt, maar is niet strikt noodzakelijk. Je kunt DDD ook binnen één team of één bounded context toepassen en van daaruit laten zien wat het oplevert. In de praktijk zien teams dat een klein, goed gemodelleerd domein vaak als voorbeeld gaat fungeren voor de rest van de organisatie. Succesvolle casussen zorgen dan vanzelf voor meer draagvlak bij collega teams en stakeholders.