In podcast 132 van Scott Hanselman werden het Repository Design Pattern en het Active Record Design Pattern genoemd. Ik heb hier 2 boeken staan over Design Patterns (waaronder het boek van de Gang of Four, Gamma, Johnson, Helm en Vlissides). Het begrip "design patterns" kwam al voorbij bij mijn studie Hogere Informatica, tussen 1997 en 2001. Dat ik er meer mee zou moeten doen, dat licht ik even toe.
Veel zaken in code volgen een bepaald systeem of patroon. Daardoor is je code robuust, is het vaak goed te testen en is ook het aanpassen makkelijker dan het geval zou zijn als er geen systeem in je code zit. Te vaak ziet een developer een probleem, bouwt er een oplossing voor, voegt mogelijk nog een testproject toe en denkt dat het dan klaar is. Totdat je in een situatie komt dat er een onverwachte waarde in het systeem komt en de boel crasht of een ongewenst resultaat geeft. Soms wordt dat opgelapt ("even een extra if-statement erbij") en na een half jaar treedt er een ander probleem op. En blijf je dus pleisters plakken.
Een aantal jaren geleden heeft Jan Julius bij de afdeling service van TRES stage gelopen. Inmiddels is hij back-end developer bij TRES. Bij het bouwen van een applicatie waarmee je sites kunt opzetten zag hij dat daar een flink aantal stappen in zaten. Zo maak je een map aan, maak je de website aan in IIS, voeg je een (aantal) applicaties in IIS toe, voeg je een applicatie-pool toe, bindings, FTP-toegang, IUSR die ook in active directory aangemaakt moet worden. Die hele flow is een transactie: of alles gaat goed, of als er ook maar 1 ding fout gaat, moet alles teruggedraaid worden. Ik zie hier alweer de spaghetti-code met allemaal if-statements ontstaan. Maar gelukkig niet bij JJ. Na wat onderzoek kwam hij met het Command-pattern. Daarbij heb je een Do/Execute-actie, maar ook een Undo-actie. In die Undo kun je dan de zaken die je in de Do/Execute aangemaakt hebt weer ongedaan maken. Met als resultaat een strak, overzichtelijk en goed testbaar resultaat.
Active Record en Repository zijn zaken die (volgens mij) na 2001 ontwikkeld zijn en deze patterns worden dus ook niet in de boeken behandeld. In de loop van de tijd zullen er steeds meer nieuwe design-patterns ontstaan. Het Repository-principe was ik al eens tegengekomen in een project wat volgens mij door een stagiair of ingehuurde kracht uitgevoerd is. In ieder geval, hierbij de uitleg van deze design-patterns, misschien is het iets waar jij ook wat mee kunt. Want hoe kun je design patterns toepassen? Pas op het moment dat je weet welke er bestaan en in welke gevallen ze jou een oplossing kunnen bieden voor het probleem wat jij hebt.
Active Record design pattern
Op mijn blog had ik een verwijzing naar een pagina op de site van Martin Fowler: link. Het staat daar erg beknopt, Martin verwijst naar een boek uit 2002 dat hij gepubliceerd heeft: Patterns of Enterprise Application Architecture. Active Record is als ik het zo zie een beetje het standaard ORM in C#/.NET. Via het Entity Framework maak je een representatie van je database als objecten in C#. Daar kun je dan eigen functies aan koppelen, de "domein logica". Ik denk dat dit nu toch wel de standaard is. In een enkele situatie voer ik nog wel eens hardcoded query's uit, maar dat zou je dus niet meer moeten doen. Via het ORM-model heb je op 1 locatie de weergave in code van de databasetabel. Als die wijzigt (andere naam van het veld, ander datatype, nieuwe velden, er worden velden verwijderd) dan druppelt dat door in je code. Met query's in strings heb je die relatie niet en vraag je dus eigenlijk om problemen.
Repository design pattern
In de uitzending kwam deze uitleg op de MSDN van Microsoft voorbij: link. Ik ga dit eerst behandelen, daarna de post van Rob Conery die het niet een goed voorbeeld vindt: link.
Met repository en het unit of work pattern zou je de code beter moeten kunnen scheiden van het databasemodel. Waardoor je jouw code beter kunt testen. De code die uitgevoerd wordt ontvangt een Interface-object. Hierdoor kan productiecode een concrete implementatie met database-connectie binnen krijgen en het test-project een eigen data-object waar je acties mee uit kunt voeren (bijvoorbeeld een in-memory object). Je ziet in de afbeelding het voorbeeld zonder repository, waarbij de Controller rechtstreeks met het DBContext-object werkt en de situatie waarbij wel met de repository gewerkt wordt.
Omdat elke repository een eigen database-context object heeft en je misschien met een transactie meerdere processen wilt laten slagen (of ongedaan wilt laten maken) heb je een Generiek Repository-object nodig. Een "Unit of Work" class gebruikt die Generieke Repository (met <classname1> en <classname2>) om een werk-item aan te maken en die uit te laten voeren. Meer informatie over het Repository-pattern is hier te vinden: link.
Rob Conery komt met het punt dat het een onoverzichtelijke zooi wordt. En ook dat hij niet echt de meerwaarde ervan ziet. Het voorbeeld van een order die binnen een transactie moet plaatsvinden, de klant moet aangemaakt worden, het id van die klant moet gekoppeld worden aan de order. Maar met een SubmitChanges() heb je de transactie al doorbroken. Terwijl je binnen een normale DBContext dit wel uit kunt voeren.
Er zullen vast wel situaties zijn waarbij dit design pattern wel toegevoegde waarde heeft. Het commentaar van Rob doet me denken aan code van een oud collega, die zaken steeds meer ging uitsplitsen en verder uitsplitsen, waardoor als je iets moet zoeken of aanpassen je eerst door 10 functies heen moest worstelen om bij de bron uit te komen. Abstractie en scheiden van zaken is goed, maar als je het te ver doorvoert zorgt het er alleen maar voor dat code slecht leesbaar en slecht te doorgronden is. Dat levert meer problemen op dan dat het problemen oplost.
Voor het komend kwartaal staat op mijn planning dat ik het boek "Design Patterns" uit ga lezen. Ik ga kijken of ik die stuk voor stuk op dit blog kan behandelen om zo te laten zien wat er is, wat je ermee kunt en wanneer het aan te raden is om er gebruik van te maken.