Ubiquitous Language

Domain-driven design (DDD) is geen nieuw onderwerp (Eric Evans formaliseerde het concept meer dan een decennium geleden). Het is echter merkwaardig hoe, tot vandaag, de basisconcepten over het onderwerp de interesse wekken, vooral in .NET gemeenschappen. Nog vreemder is hoe diezelfde concepten verkeerd worden geïnterpreteerd.

De basis van DDD ligt in het concept van de alomtegenwoordige taal. Het ligt niet in het bepalen wat een entiteit, waardeobject, aggregaat, service of repository is.

De indruk die we krijgen is dat iedereen die begint met het bestuderen van DDD de eerste paar pagina’s van het Evans boek of het Vernon boek leest en snel doorspringt naar de pagina’s die de patronen beschrijven.

We spreken vaak met IT-ers die denken dat het hebben van een gedefinieerde alomtegenwoordige taal betekent dat de naam van klassen en eigenschappen, geschreven door programmeurs, overeenkomt met het vocabulaire van domeinexperts. Dit is echter een grove versimpeling.

Om het belang van ubiquitous taal te begrijpen, laten we beginnen met de “entiteit” hieronder:

public class Employee{ public string Id { get; set; } public string Name { get; set; } public string Cpf { get; set; } public decimal Salary { get; set; }}

Hier is een uitstekend voorbeeld van anemische implementatie! Maar voordat je verder leest, probeer uit te vinden waarom.

In ons dagelijks werk, wanneer we dit voorbeeld gebruiken, horen we suggesties dat deze klasse gedrag ontbeert (Wat juist is!). Maar als we vragen wat zo’n “gedrag” zou zijn, merken we dat mensen algemene argumenten herhalen (van mensen die het idee ook niet “begrepen” hebben) en niet kunnen aangeven wat er mis is.

Een van de meest voorkomende argumenten is dat property setters private zouden moeten zijn. In plaats daarvan zouden we “DefineXXX” methoden moeten hebben die validaties zouden implementeren. Dit idee slaat nergens op, want het doel van setters is juist om waarden voor de eigenschappen in te stellen, en het zou geen probleem zijn om validatielogica in hen te implementeren.

De methoden van een entiteit moeten de beweegredenen voor zijn toestandsveranderingen beschrijven. Ook moet er ten minste één constructor zijn die de entiteit vanaf het begin kan instantiëren, in een geldige toestand.

Het is niet ongewoon om klasse-implementaties te vinden met eigenschappen die bijvoorbeeld controleren of de waarde die je probeert in te stellen niet-null is, zelfs als diezelfde eigenschappen null als standaardwaarde hebben. Wat is hier de logica?

Zie een ander voorbeeld van een anemische klasse:

public class Customer { public string Name { get; private set; } public string Email { get; private set; } public DateTime BirthDate { get; private set; } public Customer(Guid id, string name, string email, DateTime birthDate) { Id = id; Name = name; Email = email; BirthDate = birthDate; } }

In de klasse hierboven hebben we een typisch “record” voor een functionele implementatie. Maar in een objectgeoriënteerde taal is dit geen entiteit.

Het idee zou zelfs zinvol zijn in een zuiver functionele taal waar de “entiteit” conceptueel is uitgespreid over verschillende functies die het gedrag definiëren, maar in C# heeft het geen enkele zin.

Laten we een tweede versie proberen voor de klasse Werknemer:

public class Employee{ public string Id { get; private set; } public string Name { get; private set; } public string Cpf { get; private set; } public decimal Salary { get; private set; } public Employee(string name, string cpf) { //.. } public void RaiseSalary(decimal amount) { //.. }}

Deze keer is de entiteit minder anemisch. Tenslotte zijn de beweegredenen voor het veranderen van de waarde van de eigenschap duidelijk. Merk op hoe zelfs de waarde van het testschrijven explicieter wordt.

Hoe dan ook, we hebben nog steeds problemen. We hebben de methode “RaiseSalary” genoemd, maar dit is niet de manier waarop domeinexperts deze operatie beschrijven. De enige manier om de werkelijke redenen voor het veranderen van de waarde van een eigenschap te definiëren, is door met deze experts te praten.

Om na te denken over…

Het identificeren en implementeren van de alomtegenwoordige taal is niet alleen voor het definiëren van klassenamen of eigenschappen. De alomtegenwoordige taal moet vooral blijken uit de motivaties voor de toestandsveranderingen van onze entiteiten expliciet.

Het begrijpen van alomtegenwoordige taal is veel belangrijker dan het leren van de normen. Zonder echte kennis van het domein is de waarde van design patterns nihil.

Werk eerst aan het leren van het belangrijkste. Neem de tijd en moeite om het domein goed te begrijpen en zijn alomtegenwoordige taal te spellen. Dan kan het zelfs zinvol zijn om te denken aan het associëren van DDD met meer geavanceerde technische concepten.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.