Kod är kommunikation – DDD style

Av någon anledning har jag alltid varit mer intresserad av att koden kommunicerar bra än all den där teknologin som finns runtomkring. Den ser jag mest som något nödvändigt ont som behövs för att få saker och ting att snurra. Visst kan jag emellanåt bli hänförd av coola tekniska lösningar, men i slutändan tappar de sin glans och jag faller tillbaka på det som gör mig sådär löjligt förtjust – att hitta rätt benämningar och modellera med dessa på ett lättförståeligt sätt som i slutändan hjälper till att lösa de problem man har.

Om en systemutvecklares intresse lutar mer åt det lingvistiska hållet finns det knappast något som gör henom så glad som Domain-Driven Design (DDD). En av de riktigt fundamentala delarna i DDD är något som kallas för Ubiquitous Language. Tanken här är att man innan ett systemutvecklingsarbete påbörjas sätter sig ner och fångar upp begrepp som återfinns inom det problemområde den tänkta lösningen avser. Detta arbete utförs av de utvecklare som ska arbeta med systemet och en eller flera domänexperter. Ut ur denna process kommer en uppsättning initiala begrepp som mappar ett till ett med verksamhetens begrepp och som sammantaget utgör ett Ubiquitous Language.

Från början behöver språket inte vara komplett eller perfekt; språket utvidgas och förfinas iterativt allt eftersom utvecklingen fortskrider. Detta språk används sedan uteslutande i systemets domänmodell – det vill säga i de klasser och konstruktioner som utgör hjärtat i systemet – för att göra den begriplig både för utvecklare och domänexperter. Den kommunikation man investerar mellan utvecklare och domänexperter har då potentialen att leda till kod som kommunicerar vertikalt  – från ledning ner till enskild utvecklare – i en organisation. Visst är det vackert?

Utökad förståelse

Så långt var jag med redan innan jag satte tänderna i Vaughn Vernons bok Implementing Domain-Driven Design. Det jag tänkte ta upp här är lite om den utökade förståelsen av DDD som jag fått i och med att jag läst boken. Lämpligt nog för detta inlägg har det just med Ubiquitous Language att göra.

Ubiquitous Languages existerar alltid i något man i DDD-termer kallar för Bounded Contexts. Man kan se dessa som partitioner i systemet i vilket ett visst Ubiquitous Language har mening. Att ett begrepps innebörd bestäms utifrån den kontext det används i är egentligen inget märkvärdigt. Det är ju så våra vanliga språk fungerar när vi kommunicerar människor emellan; med ordet ”hammare” menas förmodligen olika saker beroende på om man pratar med en snickare eller med en öronläkare.

Enligt författaren har i regel även enkla system flertalet Bounded Contexts, vilket då betyder att det i ett och samma system finns flera modeller som hålls åtskilda från varandra. Genom att man skyddar den interna modellen i en Bounded Context från att läcka ut kan den refaktoreras efter behov utan att omvärlden – det vill säga andra Bounded Contexts – behöver påverkas nämnvärt. Det gör också att modellen kan tillåtas att vara fokuserad eftersom den inte används för att lösa världens alla problem. Hur många gånger har man inte vågat ändra på en klass eftersom man då riskerar att förstöra någon helt annan del av applikationen? När jag då tänker på Bounded Context i samband med saker som Single Responsibility Principle blir jag nästan lite tårögd. 😉

Broar mellan öarna

Hur är det då tänkt att dessa isolerade ”öar” av Bounded Contexts ska hänga ihop om de inte får ta del av varandras modeller? För att förklara detta har jag skapat ett fiktivt exempel i form av en Context Map. Context Maps i DDD används för att hitta och synliggöra inbördes beroenden mellan olika Bounded Contexts. I mitt exempel (Figur 1) finner vi tre påhittade Bounded Contexts: Person registry context, Patient context och Billing context. Exemplet blir lite krystat eftersom vi inte har någon som helst insikt i domänen, men för mitt resonemang är det inte hela världen.

Figur 1
Figur 1: Context Map

Vi skulle kunna tänka oss att Figur 1 avser en Context Map för ett system inom sjukvården. U och D står för Upstream och Downstream respektive – analogin här är att upstream rinner downstream – och kan utläsas som att Patient context är beroende av Person registry context. I Person registry context – som jag tänker mig är en enkel CRUD-applikation för att registrera personuppgifter – hittar vi ett Ubiquitous Language som bland annat innehåller begrepp som ”Person” och ”Address”.

Men i Patient context finns inte begreppet ”Person” – här pratar man istället om ”Patient”. I DDD kastar man inte in begrepp bara för att det underlättar rent tekniskt – om personbegreppet inte passar in har det helt enkelt inte där att göra. Vidare ser vi att Billing context har ett beroende av både Person registry context och Patient context. I vårt exempel hämtas personinformation från Person registry context och underlag för fakturering från Patient context. I Billing context finner vi inte begrepp som ”Person” eller ”Patient” – här används istället termen InvoiceReceiver för att modellera räkningsmottagaren. Värt att notera är att både Patient och InvoiceReceiver kan ses som olika roller som kan antas av en Person. Det är tänkbart att dessa roller förmodligen håller en id-referens till aktuell Person i Person registry context.

Oavsett hur systemets Bounded Contexts hänger ihop rent tekniskt – man skulle till exempel kunna tänka sig web services – är min poäng att de aldrig, aldrig, aldrig exponerar sin interna modell. Och det finns ingen egentlig mening med det eftersom modellerna inte ska ha någon innebörd utanför sin kontext. För kommunikation mellan Bounded Contexts kan man använda Data Transfer Objects (DTOs) eller något motsvarande som endast syftar till dataöverföring. Mappning till det interna formatet sker via något som man i DDD-termer kallar för Anti Corruption Layer (ACL). Namnet låter finare än vad det egentligen är; med ACL menar vi helt enkelt ett lager som hanterar mappning från ett externt till ett internt format.

Den kritiske läsaren kanske tänker att upplägget jag beskrivit och illustrerat i Figur 1 inte är revolutionerande rent arkitektoniskt. En annan rimlig reflektion är att exemplet i Figur 1 inte heller är revolutionerande rent tekniskt. Här håller jag helt med. Vad som däremot kan anses som revolutionerande är att vi med DDD sätter lingvistik i första rummet, före coola tekniska lösningar. Om man som jag främst ser kod som kommunikation är detta perspektiv på systemutveckling oerhört intressant.

Kommentera

E-postadressen publiceras inte. Obligatoriska fält är märkta *