Secure by Design - Hoofdstuk 3

Ingediend door Dirk Hornstra op 16-mar-2020 21:05

In januari zijn we gestart met hoofdstuk 1 (link), in februari met hoofdstuk 2 (link) en nu wordt het tijd voor het echte werk: hoofdstuk 3!

De introductie is voorbij, we gaan nu beginnen met de "fundamentals". Volgens de schrijvers zou je op basis van wat we nu gaan lezen/behandelen een andere mind-set moeten krijgen hoe je jouw software veilig gaat bouwen.

De auteurs zijn fan van het concept "Domain Driven Design", DDD. Veel projecten die dit principe niet volgens zijn volgens de auteurs vaak projecten "waarbij het moet werken", als er een probleem gevonden wordt er een extra if-statement wordt toegevoegd en het model onder het project uiteindelijk onvolledig of niet consistent is.

Met DDD pak je een project als volgt aan:

  • Leg je focus op het hoofddomein
  • Werk modellen uit in samenspraak tussen ontwikkelaars en gebruikers van het systeem
  • Spreek in normaal Nederlands en zorgt dat je geen jargon gebruikt

Deze 3 punten zijn afkomstig uit het boek Domain Driven Design Reference van Eric Evans

DDD stelt dat je niet alleen moet zorgen dat het systeem werkt, maar dat je ook weet wat je bouwt. Je moet het probleem begrijpen, niet alleen snappen hoe er een werkende oplossing gebouwd wordt. Door op deze wijze je code te bouwen, waarbij je dus uitgaat van het probleem en niet een directe implementatie van de oplossing bouwt, volg je DDD. Door te focussen op het snappen van het probleem worden we betere ontwikkelaars. Pas later kwam men er achter dat op deze wijze ook je code een stuk veiliger wordt.

DDD is behoorlijk uitgebreid, bevat ook weer referenties naar agile werken e.d., het boek van Eric Evans bevat rond de 500 pagina's. Dat gaan we dus niet hier bespreken, de auteurs richten zich alleen op de zaken die focussen op veiligheid. Mocht je zelf meer willen lezen over DDD, dan heb je het boek van Eric, ook kun je nog kiezen voor het mini-boek Domain-Driven Design Quickly, is hier gratis als PDF te downloaden (als je je e-mailadres achter laat of account aanmaakt: https://www.infoq.com/minibooks/domain-driven-design-quickly). Patterns, Principles, and Practices of Domain-Driven Design (Wrox, 2015), door Scott Millett is ook een goed boek om mee te beginnen.

Als je aan het bouwen bent en je model in code verwerkt, is het handig om "building blocks" te hebben. Deze worden onderverdeeld in value objects, entities en als het grote structuren betreft spreken we over "aggregates".

3.1 Modellen zorgen voor een beter inzicht

Hoe zit dat nu met DDD? Je hebt modellen die je met UML maakt, databasemodellen en meer. Met DDD moet je het model van de "business" weer kunnen geven. Het boek geeft 2 voorbeelden, het zelf bouwen van een netwerk-router en het bouwen van een bagage-afhandeling-systeem op een luchthaven. Bij het bouwen van die netwerk-router heb je niet zoveel aan DDD, omdat het over het algemeen over het technische deel gaat (weinig latency, zorgen voor snelle doorvoer van data). Met DDD kun je de structuur van de pakketjes en routing tables uitwerken, maar die 2 technische zaken kun je hier niet mee afhandelen.

Het bagage-systeem echter wel. Het zal gebruik maken van database-systemen, GUI voor invoer en controle. Je zult een interface moeten bouwen wat je bagage weergeeft en wat door het systeem getransporteerd kan worden, net als de echte bagage. Als er een foutje in jouw code zit kunnen koffers in het verkeerde vliegtuig geladen worden, bij de verkeerde gate uitgeladen worden en wat al niet meer: boze passagiers, vertrouwen in de luchthaven verdwijnt. Maar het is nog erger: er zitten behoorlijke security-gerelateerde zaken gekoppeld aan jouw code. Een tas gaat alleen het vliegtuig in als de passagier ook in het toestel zit. Als de tas ingecheckt is, maar de passagier verschijnt niet aan de gate, dan moet de tas weer uitgeladen/uitgecheckt kunnen worden.

Als je niet weet welke zaken allemaal onder het "bagage-afhandeling-systeem" vallen kun je een halfbakken product afleveren. Maar het grote risico is dat het schadelijk voor het bedrijf is en zelfs potentieel gevaarlijk voor de passagiers. Dit is niet een voorbeeldsituatie, de luchthaven van Denver is in 1990 pas anderhalf jaar later geopend omdat het systeem niet goed was. Als je focus ligt om een optimale database te bouwen, dan richt je je niet op het domein. Je moet gaan uitwerken wat het systeem allemaal moet doen. In dit geval wordt het domein de 'critical complexity' genoemd. Hoewel het lijkt dat het domein niet technisch is, maar dat is niet zo: het domein is altijd technisch. Als voorbeeld wordt een compiler genoemd die je bouwt om code te optimaliseren. De "ciritcal complexity" is hier dat het programma na deze optimalisatie nog steeds hetzelfde programma oplevert als voor de optimalisatie.

De link met veiligheid. Het is moeilijk om een systeem te maken dat "alle mogelijke opties" kan afvangen. Het is soms al moeilijk genoeg met bestaande data. Nog moeilijker is het om invoer die juist met opzet gemanipuleerd is af te vangen. Het systeem moet daar nog steeds op een "sound and safe way" op reageren. Volgens de auteurs is het focussen op domain models de manier om deze zaken tegen te gaan.

Een domein model moet voldoen aan:

  • Het moet simpel zijn, zodat je je op de essentiële onderdelen kunt richten
  • Houdt de boel strict zodat het een basis is voor het schrijven van code
  • Snap wat er allemaal gebeurt zodat het systeem bruikbaar en ondersteunend is
  • Zorg dat je vanuit pragmatisch oogpunt de beste keuze maakt
  • Zorg voor duidelijke beschrijvende namen, zodat, als je het systeem aan iemand moet uitleggen, het kwartje meteen valt en je niet met "en dan moet je i + 5 doen, zodat hij gelijk wordt aan de factor M"

Er is in je model altijd een "critical complexity". Zorg dat je weet of het een technisch aspect is of het domein.

3.1.1 Modellen zijn versimpelde weergaves

Een model is de weergave van een realiteit waarbij je overbodige zaken weg haalt. Als iemand zijn/haar tas incheckt zorgt je dat je registreert hoe zwaar die tas is. Of de eigenaar schoenmaat 45 of 47 heeft doet niet ter zake en daar doe je dus ook niets mee.

Ons model is geen diagram, het is een gekozen set van abstracties.

Het begrip "model" wordt verder uitgewerkt. Model in DDD wordt vergeleken met een "model trein . Er wordt gekeken welke eigenschappen van echte treinen overgenomen worden bij de modeltrein, dat zijn kleur, relatieve afmetingen, de vorm en het kunnen rijden over spoor. Ook wordt gekeken wat dan de verschillen zijn, het materiaal (een echte trein van plastic is niet zo'n goed idee), de absolute grootte, gewicht, etc. Hoewel je makkelijk veel verschillen kunt vinden heeft iedereen toch de uitgesproken mening dat het een prima weergave van een trein is. Door slechts 3 goede eigenschappen (kleur, relatieve afmeting, beweging) die matchen vinden we het prima. Als maar één van deze eigenschappen mist is het voor ons niet meer een speelgoedtrein.

Een model is een simplificatie van de realiteit, waarbij we die simplificatie accepteren als een geldige representatie van de realiteit.

We gaan naar het voorbeeld van een model waarbij we een persoon weergeven met een naam, leeftijd, schoenmaat en wel of niet een huisdier. We krijgen een aantal weergaves te zien (een soort database-tabel-structuur, een tekening met 2 poppetjes en een hond en daaronder de tekst dat Joe 34 jaar is, schoenmaat 48 en hond Fikkie heeft. Deze weergaves zijn niet het model. Het model is het conceptuele aspect dat het model uit de essentiële onderdelen naam, leeftijd, schoenmaat, huisdier bestaat.

3.1.2 Modellen zijn strict

Een model is niet alleen een versimpelde weergave van de realiteit, maar ook een striktere weergave. Concepten, gedrag, relaties en attributen zijn maar voor één uitleg vatbaar. Je kunt mensen op tig manieren beschrijven. Maar door je alleen te richten op naam, leeftijd, schoenmaat en huisdier kun je een exacte weergave geven van wat je in jouw systeem als een "persoon" beschouwt. Om te bepalen welke aspecten wel en welke niet behouden moeten/hoeven worden is een domein-expert nodig.

De mensen die het domein begrijpen zijn de domein experts.

Het schrijven van software is een samenwerking tussen de developers en de "business". De business moet hun terminologie in de software terug zien, anders snappen ze niet wat er gebouwd is. Dan heb je gefaald. Het systeem moet redelijk, consistent werken en te begrijpen zijn. Het bagage-controle-systeem wordt er weer bijgehaald. Als bij inchecken de term "number of bags" gebruikt wordt, bij de gate heet het "bagage count" en op de tablet van de mensen die het laden en lossen doen staat de term "luggage", dan heeft het geen zin dat de ene persoon met de andere persoon mee kijkt, want spreken we nu over hetzelfde of niet?

Waarschuwing: als er veel synoniemen gebruikt worden om hetzelfde concept te beschrijven, dan is dat vaak een teken dat het model niet strict is.

Een volgend voorbeeld gaat over ERP-systemen. Ooit opgezet voor processen in machinefabrieken, maar steeds meer uitgebreid en aan het domein aangepast om er een all-round-pakket van te maken. Iets wat in basis over machines en grondstoffen gaat wordt nu gebouwd om de proces-verbalen van de politie in op te slaan. Om de boel in elkaar te laten schuiven wordt de code steeds minder specifiek en daardoor ook minder precies. Dit resulteert vaak in een algemeen object management systeem, waar alles een object is. Zo'n systeem is gevoelig voor fouten en daardoor ook voor bugs die security-issues opleveren.

We komen terug op het voorbeeld van de personen. Kan een persoon meerdere huisdieren hebben? De vraag stellen aan de "domein expert" levert op dat het "ongebruikelijk  is. Echt duidelijk maakt dit het niet, je kunt nu zeggen: ik hou het op één, of je gaat een lijst toevoegen zodat het wel kan. Als je zelf de beslissing neemt zal het nooit goed zijn. Er komt een situatie waarbij het wel voorkomt en jij het onmogelijk gemaakt hebt, of het komt nooit voor en nu heb je het model veel complexer gemaakt met extra lijsten. Jij moet de keuze niet maken, die hoort bij de expert te liggen. Dus daar leg je de vraag neer: moet ik het beperken tot 1 of moet ik de mogelijkheid bieden om meerdere te koppelen?

Een volgende functie is dat mensen hun huisdier kunnen omwisselen. Is het dan één huisdier, of worden meteen alle dieren omgewisseld? Je moet dus ook hier weten wat er moet komen.

Als je model niet een weergave van het business-model is, laat je de business-mensen in de kou staan. Als je jouw code niet strikt maakt en allemaal dynamische lijsten gaat toevoegen die nooit gebruikt zullen/kunnen worden, dan laat je jouw mede-ontwikkelaars in de kou staan. Als je strikte code schrijft bouw je een fundament waarop het model verder uitgebouwd kan worden.

Persoonlijke noot: helaas voel ik me hierdoor aangesproken. Ik ken een project waarbij de klant steeds meer zaken wil en waarbij de database steeds meer extra velden gaat bevatten. Ook zijn een aantal tabellen uit elkaar getrokken, waarbij de klant het wel eens over "X" heeft terwijl hij volgens mij "P" bedoelt. Het maakt de boel onoverzichtelijk en bevordert het maken van fouten.

3.1.3 Modellen bevatten een diep begrip van het systeem

Jij als programmeur zult een dieper begrip van het model moeten hebben dan de domein expert die dagelijks met onderdelen van de applicatie in contact komt. Want jij zult het complete plaatje moeten bouwen. Het voorbeeld van het rijden op een fiets wordt erbij gehaald. De meeste mensen kunnen fietsen zonder erbij na te denken. Slecht wegdek, een balk op je schouder, geen probleem. Nu niet meer tenminste. Toen je als klein kind leerde fietsen was het een heel ander verhaal. Die kennis is vergelijkbaar met de expertise van een domein expert: je weet hoe het werkt. Jij, als programmeur hebt het heel wat moeilijker. Je bouwt een programma wat zonder jouw hulp/interventie de klus moet klaren. Als je een fietsende robot wilt bouwen zul je de krachten en werking van wrijving moeten begrijpen, anders gaat jouw robot bij een afslag meteen onderuit.

De beste modellen ontstaan door interactie tussen bouwer en de domein expert: na verloop van tijd en na verschillende iteraties

3.1.4 Als je een model maakt, betekent dat je een keuze gemaakt hebt

Er is geen één "beste" model. Kies het model wat het beste bij het doel past.

DDD gebruikt de term "een model destilleren".  We krijgen de vergelijking met een brouwer van whiskey. Sommige delen houden we, andere gooien we weg. Als je bezig bent om een model op te zetten, probeer dan 3 verschillende modellen uit te werken en dan te kijken hoe goed ze jouw domeinproblemen weergeven. Hierdoor kun je onder woorden brengen waarom het ene wel en het andere niet aan jouw eisen voldoet.

3.1.5 Het model wordt beschreven met eenduidige taal

Als je een model bouwt begin je een bepaald soort taal te spreken. De domein-experts die met elkaar spreken doen dat in de domain-taal. Zelf gebruik je ook begrippen waarbij een domein-expert niet zal begrijpen wat je bedoelt (een object moet gedisposed worden, je hebt het maximum aantal connecties in de "pool" bereikt). Als je een logistieke applicatie moet maken lijkt het een goed idee om ook in code die begrippen te gebruiken. Volgens de auteurs is dat een gebrekkige aanpak, omdat logistieke experts vaak zaken algemeen benoemen, zoals developers ook wel eens classes, objecten, "instanties" en dergelijke door elkaar halen. Hoewel we elkaar begrijpen, is dat niet éénduidig. Als je een systeem gaat bouwen moeten zaken juist precies benoemd worden, geen mogelijke verwarring/misverstanden opleveren. Als je tijdens het beschrijven van je model al denkt, dit klinkt best wel raar, dan is dat vaak een indicatie dat er gerefactord moet worden. Je moet dezelfde termen gebruiken in de applicatie, in de handleiding, in de benodigdheden, user-stories, code en database-tabellen. Als het op de ene plaats "quantity" heet, de andere plaats "volume" en op plaats 3 "amount", dan ben je niet goed bezig. Wel wordt nog genoemd dat het uitgewerkte model kan afwijken van het conceptuele model, bijvoorbeeld omdat je tabellen moet joinen en daar extra velden voor nodig hebt of je in de code om speciale redenen dat moet doen. Wat ik hierbij als voorbeeld kan bedenken is dat je een variabele een naam geeft die gereserveerd is voor het systeem (net zoals je niet vaak een "order" veld aanmaakt, omdat ORDER BY al een TSQL eigenschap is). De auteurs benadrukken dat bij overleg tussen domein-experts en ontwikkelaars altijd de taal van het model gebruikt wordt om misverstanden te voorkomen. Hierbij komen we nog tot de eigenschap "bounded context". Hiermee wordt in DDD aangegeven dat de termen die je hier in jouw model gebruikt beperkt blijven tot dit model. De termen die je gebruikt kunnen in een ander model hele andere eigenschappen hebben of andere functies hebben. Beperk je dus hiermee tot dit model.

3.2 Blokken bouwen voor jouw model

Om je model in code weer te geven heb je "building blocks" nodig. Deze moeten goed gedefinieerd zijn, het doel moet zijn om orde en structuur in complexe modellen aan te brengen. Dit geeft je een framework waar je domein logica gescheiden kan blijven van de rest van de code en je door de technische problemen heen kan leiden. De building blocks van DDD zijn al eerder genoemd, het zijn de entities, value objecten en aggregates. In de afbeelding die erbij getoond wordt zie je een entity "customer", een "shop" die een entity zou kunnen zijn, maar hier een "aggregate root" wordt genoemd omdat aan dit object de klanten en een naam gekoppeld zit. De "naam" en "leeftijd" worden hier "value objects" genoemd, om deze zaken zit een scheiding, deze lijn wordt een "aggregate boundary" genoemd.

3.2.1 Entities / entiteiten

Een entiteit is een model object met bepaalde onderscheidende eigenschappen. Een entiteit is speciaal omdat:

  • het heeft een identiteit en is daardoor te onderscheiden van andere entiteiten
  • deze identiteit is consistent tijdens de "life cycle"
  • het kan andere entiteiten of value objecten bevatten
  • het is verantwoordelijk voor de acties die op de entiteit uitgevoerd kunnen worden

Dit betekent dat als je wilt weten of 2 entiteiten dezelfde entiteit zijn, je dit controleert op basis van de identiteit.  Als voorbeeld wordt een auto gegeven, die van eigenaar kan wisselen, onderdelen die vervangen worden. Maar het chassis-nummer blijft altijd hetzelfde: de identiteit. Een entiteit is verantwoordelijk voor de objecten die het bevat, dit is een cuciale eigenschap om veilige code te maken.

Soms wordt een domein object gedefinieerd door zijn attributen, maar hoewel die attributen soms wijzigen hoeft dit niet te betekenen dat het domein object verandert. Als je een klant zou definiëren op basis van naam, leeftijd en adres, wat zou er gebeuren als deze klant gaat verhuizen (ander adres?). Je moet dus een klant niet definiëren op basis van de attributen, maar op basis van de identiteit. Vaak wordt de identiteit opgebouwd door een automatisch oplopend volgnummer, soms op basis van bepaalde attributen. Daarbij moet je dan dus attributen kiezen die niet wijzigen. Een gegenereerd volgnummer geniet dus de voorkeur.

De identiteit van een entiteit is belangrijk, maar de scope waarbinnen de identiteit uniek is kan variëren. Je zou een klant uniek kunnen maken op zijn/haar BSN nummer. Ook buiten ons systeem is dit een unieke waarde (externally unique identifier). Maar omdat dit een extern gedefinieerde identiteit is, kan dit weer security-problemen opleveren (dit komt in een volgend hoofdstuk terug). Vaak wordt een eigen uniek identifier gebruikt (globally unique). Als je met een gedistribueerd systeem werkt en de ID's niet alleen uniek, maar ook oplopend moeten zijn, kan dat nog wel eens technische problemen opleveren. Sommige entiteiten maken onderdeel uit van een andere entiteit. Soms is het voldoende dat ze binnen deze entiteit uniek zijn (een eigen volgnummer bijvoorbeeld, dit is local to the owning entity).

Op het moment dat je de entiteiten aan het bouwen bent moet je in je achterhoofd houden dat je alleen de eigenschappen en het gedrag toevoegt wat essentieel is om een entiteit te kunnen onderscheiden. Overige attributen en gedrag moet niet in de entiteit zitten, maar onderdeel uitmaken van model objecten die onderdeel van de entiteit zijn. Deze model objecten kunnen andere entiteiten of value objects zijn.

Entiteiten zijn niet alleen  verantwoordelijk voor de coördinatie en acties die op zichzelf uitgevoerd worden, maar ook op die van objecten de de entiteit bevat. Omdat de entiteit verantwoordelijk is voor de eigen "state" moeten ook de acties op de objecten die het bevat via de entiteit uitgevoerd worden. Als dat niet gebeurt heb je dat het een "anemic model" is, nader toegelicht door Martin Fowler: link. Het voorbeeld wat hierbij wordt gegeven is de vliegtuig-entiteit. Deze bevat passagier-objecten. De enige manier om hier aan toegevoegd te kunnen worden is via de "board"-methode. Deze voert de validaties uit, controleert of er niet meer mensen instappen dan er in mogen. Als je een vliegtuig-object aanmaakt en je kunt bij het passagier-object komen en er zelf willekeurig mensen aan toevoegen of weghalen, dan heb je een probleem.

3.2.2 Value objecten

Een value-object wordt onderscheiden door deze eigenschappen:

  • een value-object heeft geen identeit, maar wordt gedefinieerd door de waarde
  • het is onveranderlijk
  • het definieert een conceptueel geheel
  • het kan referenties naar entiteiten bevatten
  • het bevat belangrijke voorwaarden en dwingt deze af
  • het kan als een attribuut van een entiteit of ander value-object gebruikt worden
  • de levensduur kan kort zijn

In de komende hoofdstukken wordt besproken dat deze eigenschappen een belangrijke rol spelen bij het veiliger maken van je code.

Gedefinieerd door de waarde

Als voorbeeld wordt geld gegeven. Voor een gewone webshop is elke 5 euro die betaald wordt hetzelfde bedrag, een value-object dus. Bij een bank geldt dit niet, elke 5 euro die in bezit is, is anders dan het andere 5 euro biljet (ander serienummer). Daar zal het geld "dus" een entiteit zijn.

Onveranderlijk

Als een product een prijs value-object bevat, waarbij de waarde 5 euro is, moet deze waarde ook 5 euro blijven. Als het product-object door het systeem gaat en op een bepaald punt de prijs "ineens" 10 euro geworden is, dan is de "immutable"-eigenschap geschonden.

Een conceptueel geheel

Een value-object kan uit één of meerdere attributen of andere value-objecten bestaan. Ook kan het een referentie naar een entiteit bevatten (referentie, niet de entiteit zelf). De reden is dat de waarde van een entiteit kan veranderen. Hierdoor zou ook de "immutable"-eigenschap geschonden worden. Een value-object moet een geheel vormen (er wordt verwezen naar het CHECK pattern language: link). Als het door het systeem gaat heeft het betekenis en kunnen ook de beperkingen/restricties doorgevoerd/behouden worden. Er wordt een voorbeeld getoond van een entiteit Customer met een value-object CustomerInfo waar adresgegevens, leeftijd en contactgegevens in staan en één waarbij de entiteit Customer gekoppeld zit aan een value-object "leeftijd", value-object "contactinformatie" en value-object "adresgegevens". Ook wordt GPS genoemd, je zou een value-object GPS-point kunnen hebben die zelf een methode bevat die de afstand kan berekenen van zichzelf ten opzichte van een ander punt wat als parameter gegeven wordt.

Stel dat je een value-object leeftijd hebt. Met een integer waarde kun je hier meer ongeldige waardes aan geven dan waardes die juist zijn. Je moet daarom restricties toevoegen om dat af te dwingen. Deze restricties moet je in het value-object zelf inbouwen en NIET in andere domein-objecten of helper-methoden implementeren. Deze "invariants" zijn andere acties dan validaties (bijvoorbeeld of een order wel een verzendadres heeft), dat type validatie heeft meestal betrekking op meerdere domein-objecten en wordt bij voorkeur zo laat mogelijk uitgevoerd.

3.2.3 Aggregates (collecties?)

Als je met een object werkt wat een life-cycle heeft, dan kan dat betekenen dat je bepaalde logica moet implementeren (bijvoorbeeld locking) om gelijktijdige acties te ondersteunen en bijvoorbeeld updates naar een opslag-medium, zoals een database. Het wijzigen van objecten vindt plaats binnen transacties. Hierbij komen aggegates om de hoek kijken. Dit is een conceptuele grens om delen van het model te groeperen. Hiervoor moet het aan een aantal regels voldoen, deze zijn door Eric Evans opgesteld:

  • iedere aggregate heeft een grens en een bron/"root"
  • de bron is één specifieke entiteit binnen deze collectie
  • de bron is het enige object waar objecten buiten de grens bij kunnen/mogen komen
  • * de bron heeft dus een globale identiteit
  • * de bron bepaalt alle toegang tot de objecten binnen de grens
  • * alle andere entiteiten hebben een lokale identiteit. Deze identiteiten hoeven buiten de grens niet bekend te zijn.
  • * de bron kan referenties van interne entities naar andere objecten doorgeven, maar deze referenties kunnen alleen tijdelijk en niet permanent gebruikt worden
  • * de bron kan referenties van value-objecten aan andere objecten doorgeven
  • invarianten tussen de onderdelen van een collectie worden altijd binnen een transactie afgedwongen
  • invarianten die onderdeel uitmaken van meerdere collecties kunnen inconsistent zijn, maar uiteindelijk kunnen ze consistent worden
  • objecten binnen een collectie kunnen referenties naar andere collecties bevatten

 

In veel gevallen is een collectie en de bron van een collectie één en hetzelfde object. Omdat de bron het enige toegangspunt voor objecten buiten de collectie is, kun je deze via een repository benaderen. Via een repository kun je de bron van collecties opslaan en op een later tijdstip weer opvragen. Ook kun je eerder opgeslagen bronnen van collecties verwijderen, als je model dat ondersteunt.

We gaan naar een voorbeeld. We hebben een bedrijf met werknemers. We maken van het bedrijf een entiteit omdat we op zich meerdere bedrijven/BV's zouden kunnen hebben, globally identfiable dus. Het bedrijf heeft een naam, dat kan een value-object zijn. De werknemers zijn entiteiten. Een werknemer kan een rol hebben, dat is een value-type. Na het gesprek met de domein-expert heeft de programmeur het inzicht dat de werknemer een local identity kan hebben, de rol die iemand heeft soms maar door één persoon ingevuld kan worden (bijvoorbeeld één controller). De bedrijf-entiteit zou verantwoordelijk moeten zijn voor het wel of niet toewijzen van rollen. Een bedrijf wordt een collectie, het bedrijf wordt de bron van de collectie. Wil je acties uitvoeren op de werknemers, dan moet dat altijd via het bedrijf.

3.3 Bounded Context

We komen terug op de "ubiquitous language", de taal die overal hetzelfde wordt gesproken. We krijgen weer een voorbeeld van een programmeur die een ordersysteem bouwt. Hij vraagt waar een order uit kan bestaan en krijgt het antwoord dat deze uit "verkoopbare" en "niet verkoopbare" producten kan bestaan. Die niet verkoopbare producten blijken gratis producten te zijn die soms worden toegevoegd (maar niet via de shop gekocht kunnen worden). We zien een schets met overzicht van "producten", "dingen", "gratis producten", een zootje. Na een gesprek waarin vast is komen te staan dat het "gewoon" producten zijn en het niet uitmaakt of het verkoopbaar of niet verkoopbaar is, wordt de schets een stuk kleiner en strikter. Maar er is wat gemist, een order bestaat niet uit producten, maar een order bestaat uit een package (doos) waarin de producten zitten. De developer heeft alleen zitten praten met de mensen op de expeditie-afdeling, maar als hij vervolgens naar de financiële afdeling gaat. Hier missen ze de uiterste betaaldatum en de informatie over de betaling. We zien dus 2 verschillende contexten, in de ene heb je de "financiële order" en in de andere de "fysieke order". Maar hoe moet dat dan als de order van de ene naar de andere context moet / er gegevens overgedragen moeten worden?

3.4 Interacties tussen contexten

Elke keer als data van de ene context naar de andere gaat en dus een grens overschrijdt, wordt de semantiek van de "ubiquitous language" van de andere context impliciet geaccepteerd. Dit zorgt ervoor dat elke keer als er geen actie ondernomen wordt er een potentieel beveiligingsrisico optreedt. Als je dit hoort klinkt het logisch, maar dit type probleem komt vaak voor. Ironisch genoeg komt dit vaak door het DRY-principe: don't repeat yourself. Andrew Hunt en David Thomas hebben dit principe benoemd als "elk stukje kennis moet een enkele, voor één uitleg vatbare, autoritaire weergave in het systeem hebben. Veel mensen interpreteren dit als "je moet geen copy-paste acties in je code hebben", maar dit principe slaat juist op de semantiek, wat ons dus weer naar de "ubiquitous language" terug brengt. Het boek gaat dit verduidelijken met een voorbeeld van de shipping- en finance-context.

3.4.1 Een model delen tussen contexten

Beide contexten gebruiken order, product en prijs. Het is daarom interessant om te kijken of het samen te voegen is en we kopieën van datastructuren/code kunnen voorkomen. Het model is een succes, het enige minpunt is dat er ongebruikte concepten in het model zitten (?). Maar zoals altijd blijft het model in beweging, er moet een nieuwe business-requirement voor de shipping toegevoegd worden. Er zitten "gratis producten" in de pakketten, door de prijs op 0 te zetten werden ze in het label/document niet in het aantal producten meegenomen. Maar omdat er orders naar het buitenland gaan en de douane het aantal producten wil hebben moeten ook deze producten in het totaal-aantal meegenomen worden. Daarom moet de prijs niet meer 0 euro zijn, maar de normale prijs. Zo komt het juiste aantal op het etiket/document en zijn er geen problemen meer bij de douane. In het systeem gaan er echter alarmbellen af omdat zo nu en dan de business-rule "gereserveerd aantal groter of gelijk aan totaal van alle prijzen" een false-waarde teruggeeft. Iets wat ontstaan is door deze schijnbaar simpele aanpassing. Door zaken samen te voegen heb je ervoor gezorgd dat een context niet meer onafhankelijk van een andere context kan zijn en geen eigen beslissingen meer kan nemen. Maar als je het model niet deelt, hoe weet je dan welke onderdelen extra aandacht nodig hebben als er over grenzen communicatie plaats vindt? Volgens de auteurs van het boek is dat het uittekenen van een context-map.

3.4.2 Een context-map tekenen

Een context-map is een conceptueel overzicht van hoe verschillende contexten met elkaar interacties aangaan. Dit kan een tekening zijn waarbij in tekst uitgewerkt is wat er gebeurt of een interpretatie die tussen de teams gedeeld wordt. Hoe het ook weergegeven wordt, het is de bedoeling dat duidelijk wordt dat het aandachtspunten zijn. Je moet hiervoor wel weten wat de grenzen van een context zijn. Een goede eerste stap hierbij is om de wet van Conway te gebruiken:

Elke organisatie die een systeem ontwerpt zal een systeem maken waarbij de structuur een kopie is van de communicatie-structuur binnen het bedrijf

In het boek wordt een afbeelding getoond waarbij nu de onderdelen van het bedrijf de hoofdzaken vormen. Shipping, Finance en nog een blok "Account". Er staan pijlen tussen de blokken die de communicatie aangeven (van finance naar shipping: process order, van shipping naar finance: notify order shipped, van finance naar account: reserve amount, van finance naar account: complete transaction). We hebben ook de omschrijvingen van de acties:

  • finance ontvangt een nieuwe order
  • het totaal-bedrag van de order wordt gereserveerd op het account op basis van de betaal-informatie
  • finance stuurt de order naar shipping om het te verwerken
  • shipping verwerkt de order en verstuurt deze
  • shipping stelt finance op de hoogte
  • finance rondt vervolgens de financiële transactie af


In de volgende afbeelding zien we een duidelijke afscheiding van shipping en finance, met ieder hun eigen blokje "order". In de volgende hoofdstukken gaan we zien hoe het met security zit (en hoe bounding contexts daarbij helpen), hoofdstuk 9 gaat over het afhandelen van fouten en in hoofdstuk 12 en 13 wordt het werken met legacy-code onder de loep genomen.