Value objects spelen ook maar een rol

 
12 augustus 2010

De afgelopen dagen had ik een interessante discussie op de DDD mailinglist over value objects.

Value objects: “These are the objects that describe things” [Uit: DDD, Evans]

De discussie ging over de niet-wijzigbaarheid (immutability) van value objects. Het vreemde is dat veel ontwikkelaars (in de rol van modelleur) deze niet-wijzigbaarheid benadrukken. Hij komt ook altijd op. Terwijl het volgens mij niet de essentie is.

De essentie van een value object  is dat het een object is waarvan de waarde van belang is, niet zijn identiteit. Ieder object is daarmee inwisselbaar voor elk ander object met dezelfde waarde.

Doordat ze voor elkaar inwisselbaar zijn, worden referenties ernaar vaak gedeeld. Dat leidt tot de technische eis dat elk value object niet-wijzigbaar is, omdat je op die manier anders een value object wijzigt terwijl het andere object dat naar dezelfde waarde refereert, ineens naar een andere waarde refereert. Dus dat voorkomt dat, als ik mijn naam wijzig, iederéén met de naam “Rick van der Arend” ineens anders zou heten. Gelukkig zijn dat er niet veel, misschien wel geen, omdat mijn naam niet veel gedeeld wordt. Maar je zal maar Jan de Vries heten.

Tot zover dus nog allemaal duidelijk: value objects vertegenwoordigen waarden en zijn vaak niet-wijzigbaar geïmplementeerd en alle value objecten met dezelfde waarde zijn conceptueel hetzelfde object.

Maar een model geldt alleen binnen een context
Maar: een model geldt voor een bepaalde context. Dit betekent dan ook dat het object binnen die context misschien een value object is, maar dat het buiten die context best wel eens een entiteit zou kunnen zijn. In de context waarin het object een entiteit is, zorgen wijzigingen in het object er niet direct voor dat je in met een ander object te maken hebt. Evans maakt hier op pagina 98 in zijn boek nog speciaal een verwijzing naar, waarin hij zegt dat een adres, voor degene die het gebruikt, over het algemeen een value object is, maar voor de postal service (die de adressen vastlegt) waarschijnlijk niet.

Volgens mij zie je dit patroon ook vaak bij objecten die in een bootup-fase configureerd worden en dan als value object behandeld worden en pas na bootup (en een context switch) entiteiten zijn in hun eigen domein. Denk ook aan een klassieke data access layer, die al je objecten in en uit de database heen-en-weer schuift, zonder zich druk te maken over hun identiteit: als alle attributen, inclusief het ID, hetzelfde zijn, dan is het gewoon hetzelfde object. (OR mappers, zoals Hibernate, gaan daarin verder en maken wel die vertaalslag naar een entiteit).

En wat heb je dan aan die kennis?
Op zich natuurlijk interessant voor degenen die zich met modelleren bezig houden, maar over het algemeen maak je een model voor één bepaalde context en kom je dit probleem dus niet zo vaak tegen. Zou je denken.

Wat ik vaak zie gebeuren is dat men die niet-wijzigbaarheid gebruikt als criterium om te bepalen of iets een value object is en vervolgens dat criterium opleggen aan het (volledige) object in de werkelijkheid. Bij een vaststaand label, zoals een kleur, gaat dat dan ook vaak nog wel goed, aangezien die normaal gesproken niet wijzigt. Met een numerieke waarde, zoals een leeftijd, wordt het soms wat lastig, want de leeftijd van iemand kan veranderen. Ja, maar de leeftijd 30 is de leeftijd 30. Anders gezegd: het getal 30 is het getal 30. Dat lukt dus meestal ook nog wel.

Maar soms wordt een hele klasse aan value objects gemist: die objecten die overduidelijk een identiteit hebben, maar binnen de context waarin gemodelleerd wordt, niet. Bedenk dan: als geldt dat een object binnen jouw context volledig gedefinieerd wordt door zijn/haar attributen, dan is het nog steeds een value object.

Voorbeeld
Ik zal nog even afsluiten met een mooi voorbeeld: het zoeken van een appartement in Barcelona. Voor jou maakt het niet uit welk appartement precies welke is en of deze vorig jaar anders was. Het gaat om het aantal vierkante meters, de ligging in de stad, het aantal kamers, of er een bad in zit, etc. Voor de verhuurder is het echter een heel ander verhaal: die wil zijn appartement onderhouden en een appartement dat recent gerenoveerd is, is niet ineens een heel ander appartement geworden, ook al heeft het nu meer kamers. Uiteraard is het zo dat als je zelf eenmaal een appartement kent, dat dat misschien dezelfde wordt waar je volgend jaar weer naartoe wilt. Als het een beetje hetzelfde is gebleven..


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Architectuur, Development

Tags: , , ,


Reacties (3)

  • Peter Moor schreef:

    Wat ik een interessant verschil vind tussen value en reference types (al is dit een zijspoor van jouw verhaal) is dat reference types ook het begrip null introduceren, terwijl dit bij value types niet wordt gebruikt.
    Conceptueel gezien interessant, maar vooral vanuit praktisch oogpunt ben ik niet altijd blij met dit onderscheid. Als ik in een functie als parameter een int (value type) vraag, weet ik dat ik een int krijg. Vraag ik echter om een domeinobject zoals Customer (reference type), dan weet ik niet zeker of ik een Customer krijg. Ik zou ook een “null” kunnen krijgen, waardoor ik in mijn code actief moet controleren of ik wel iets heb gekregen.
    In C# kun je value types nullable maken door een simpel vraagteken in de declaratie. Volgens mij zou het een goed idee zijn om ook met zo’n simpele constructie (een referentie naar) een reference type niet-nullable te maken. Ik wil soms gewoon een Customer, en geen null.

    (Misschien botst dit met bepaalde OO-principes. Ik heb er vooral vanuit praktisch oogpunt over nagedacht.)

    Geplaatst op 12 augustus 2010 om 14:47 Permalink

    • Overigens nog een extraatje. Je zei: soms wil je gewoon een Customer en geen null.

      Ik ben het ermee eens, ik zou het anders omschrijven: soms is een bepaald onderdeel in je domeinmodel niet optioneel. Het is dan aan degene die een object aanmaakt met dat niet-optionele onderdeel om ervoor te zorgen dat het er komt. Een null value stelt de beslissing uit en zorgt ervoor dat er op allerlei andere plekken rekening mee gehouden moet worden. Erg onhandig.

      Reference types niet nullable kunnen maken en een bepaalde class of een bepaald object kunnen definieren als null-value lijkt zo op het eerste gezicht een mooie oplossing.

      Geplaatst op 13 augustus 2010 om 8:52 Permalink

    • Hoi Peter, ik ben het hierin helemaal met je eens. De null value is vervelend, omdat hij de interface van het type van de referentie niet ondersteund. Sommige talen hebben dat beter opgelost, maar in Java en C# kun je op die manier de beruchte NullPointerException krijgen.. de bedenker van de null reference heeft er overigens zelf ook spijt van: hij noemt het zijn million dollar mistake.

      Punt is wel dat je dus wel een default type zult moeten definieren. Hoewel er nog wel varianten te bedenken zijn. Objective-C schijnt een null value te hebben die alles accepteert en indien nodig weer een null value oplevert als result. In een statische taal zou ik een iets explicietere constructie verwachten.

      Geplaatst op 12 augustus 2010 om 19:40 Permalink