Datum: 18-09-2014
Auteur:
Pieter van der Spek, Software Architect

Object relational mappers (ORM) bestaan al een flink aantal jaren. Bekende tools als Hibernate zijn al sinds het begin van de eeuw beschikbaar. In de wereld van Microsoft-technologieën heeft het gebruik van dit soort tools echter pas echt een vlucht genomen met de komst van Entity Framework in 2008. Deze tools vereenvoudigen de communicatie met de database voor de programmeur doordat de meeste “boiler-plate” code, zoals het maken van een verbinding met de database, beschikbaar is als kant-en-klare functionaliteit binnen de ORM.

Design pattern

Tot de ORMs gemeengoed werden, paste men allerlei design patterns toe om de applicatie dusdanig te structureren dat de code die nodig was om de verbinding met de database op te zetten en de queries op te bouwen, gescheiden was van de rest van de applicatie. Door de gelaagde opzet van de applicatie werden (en worden) in ieder geval drie belangrijke punten gerealiseerd die het onderhoud van de applicatie moeten vereenvoudigen:

  • Testbaarheid: door het scheiden van de database- en business-/applicatie-logica is het relatief eenvoudig om de beide lagen los van elkaar te testen.
  • Herbruikbaarheid: de code voor het opzetten van de verbinding met de database en de individuele queries zijn makkelijk herbruikbaar door de applicatie heen.
  • Agnostisch: doordat de functionaliteit voor het communiceren met de onderliggende database beperkt is tot de database-laag, is het eenvoudiger om van database te wisselen.

Repository pattern

Een belangrijk design pattern om dit voor elkaar te krijgen is het “repository pattern”. Dit wordt door Martin Fowler als volgt omschreven:

(Een repository) bemiddeld tussen de domein-laag en de data-laag door gebruik te maken van een collectie-achtige interface voor het benaderen van domein-objecten

In de praktijk betekend dit dat de programmeur repositories maakt die de queries richting de database groeperen. De repositories vormen daarmee een aparte data-toegangslaag (DAL) in de veelgebruikte gelaagde software architectuur. Deze laag is (relatief) makkelijk aan te passen voor een andere database en de repositories zijn voor het testen te vervangen door alternatieve implementaties zodat de code los van de database kan worden getest.

Is een repository nog wel nodig is als je een ORM gebruikt?

Na de introductie van ORMs zijn veel programmeurs vast blijven houden aan dit design pattern (met als enige verschil dat queries nu worden geschreven met behulp van de query-taal van de gebruikte ORM) zonder zich af te vragen of dit nog altijd nodig of zelfs verstandig is. Toch is het niet zo gek om je af te vragen of een repository nog wel nodig is als je een ORM gebruikt. Immers, door queries in verschillende repositories te stoppen bestaat de kans dat sommige van de voordelen van ORMs niet meer benut kunnen worden. Een voorbeeld zijn NHibernate (NHibernate is de .NET-implementatie van het bekende Hibernate dat ontwikkeld is voor Java) Futures die het mogelijk maken om verschillende losse queries in één keer naar de database te versturen wat kan zorgen voor een betere performance. Zodra de losse queries echter in verschillende repositories staan, wordt dit lastig te gebruiken. Bij het schrijven van de losse queries weet de programmeur namelijk niet dat de queries samen gebruikt zullen worden. Als deze kennis wel beschikbaar is en de implementatie hierop wordt aangepast, introduceer je een ongewenste koppeling tussen verder ongerelateerde repositories.

Niet alleen beperkt het repository pattern de ORM, maar in feite is het gebruik van dit design pattern in combinatie met een ORM dubbel op. Hoe zit het dan met de eerder genoemde voordelen van het repository pattern? Ten eerste ondersteunen de meeste ORMs een grote verscheidenheid aan databases. Kortom, wisselen van database is een peulenschil als je een ORM gebruikt. Het repository pattern voegt daar niets aan toe.

Herbruikbaarheid

Verder zorgt het repository pattern niet alleen voor de herbruikbaarheid van het opzetten van een verbinding, maar ook van de standaard queries en dan met name het opvragen van een object aan de hand van zijn ID. Juist deze standaard functionaliteit is ook beschikbaar in iedere ORM. De meeste andere queries zijn redelijk uniek voor de service, webpagina of scherm waar hij gebruikt wordt. Herbruikbaarheid is hier dus geen issue en door de query ver weg te halen van de plek waar hij gebruikt wordt, zorg je er juist voor dat je de query niet goed kan optimaliseren voor waar hij voor gebruikt wordt. Een query als “selecteer al de te laat ingeleverde huurauto’s” kan gebruikt worden in het scherm om een lijstje te tonen en als onderdeel van de business logica om de betreffende personen een boete in rekening te brengen. Alhoewel de benodigde query op het oog hetzelfde is, is de werkelijke betekenis en de manier waarop de resultaten gebruikt worden heel verschillend. De kans is dan ook groot dat eventuele wijzigingen in het gebruik aan de ene kant, zorgen voor ongewenste bijeffecten aan de andere kant. Met het achterwege laten van het repository pattern lever je dus uiteindelijk niets in aan herbruikbaarheid en behoud je bovendien de extra kracht die een ORM kan leveren zoals de eerder genoemde futures in NHibernate. Anderzijds kun je veel gebruikte filters (bijv. voor de beveiliging) in aparte query-objecten, command-objecten of in extensie-methodes zetten (afhankelijk van de programmeertaal zijn er verschillende oplossing voor handen), waardoor die alsnog op meerdere plekken te gebruiken zijn.

Testbaarheid

Dan blijft de testbaarheid van het repository pattern over. Dit is misschien wel het belangrijkste voordeel van het repository pattern. Door de data access-laag achterwege te laten, zo is het veel gehoorde argument ten faveure van repositories in combinatie met een ORM, zou het niet langer mogelijk zijn om de applicatie los van de database te testen. Alhoewel het met een ORM inderdaad wat meer werk vereist, is het verre van onmogelijk om dit voor elkaar te krijgen en zeker geen reden om vast te blijven houden aan dit design pattern als je ook al een ORM gebruikt. Twee voor de hand liggende manier die Microsoft adviseert in combinatie met het Entity Framework zijn het creëren van in-memory dubbelgangers voor het testen of met behulp van een mocking framework. Beide methodes zijn afdoende voor het doen van unit test, maar ontslaan je net als bij het gebruik van het repository pattern niet van het doen van integraties tests.

ORM of repository pattern

Al met al snijd je je met het loslaten van het repository pattern dus niet in de vingers. Sterker nog, je behoud niet alleen de voordelen van het repository pattern, je krijgt ook de volledige beschikking over de krachtige functies die het ORM biedt. Bovendien wordt het veel makkelijker om enkele veel voorkomende problemen bij het gebruik van ORMs, zoals het bekende (beruchte) SELECT N+1-probleem, in de hand te houden. Kortom, het is ORM of repository pattern.

Meer weten?

Heb je vragen of wil je meer informatie naar aanleiding van deze blog?
Neem dan contact met ons op via info.nl@axians.com of door te bellen naar +31 88 988 96 00.