Databases vs Content repositories

13 October 2009 15:37 Nico Klasens Databases, Java

De afgelopen tijd verzeil ik regelmatig in een discussie waar de data van een project het beste in bewaard kan worden. De discussie gaat tussen een relationeel systeem of een content repository. In de rest van dit verhaal zal ik een aantal redenen noemen om wel of juist niet voor een repository te kiezen.

Het relationele systeem is bij de meesten wel bekend. Dit wordt gebruikt door de meeste databases en bestaat uit tabellen met kolommen en rijen. Een tabel heeft een primary key en optioneel een aantal foreign keys naar andere tabellen.

Een content repository, waarvan JSR-170 een beschrijving is, is een hiërarchisch systeem van nodes en properties. In elke workspace van de repository is één node de root. Elke node heeft een namespaced pad in de workspace gerekend vanaf de root. Een node heeft een primair nodetype. Mixins kunnen toegewezen worden aan een node om de properties uit te breiden. Elke property van een node bestaat uit een namespaced naam, waarde en datatype. Binnen een content repository worden optionele services zoals search, notification (events), versioning en import/export geleverd.

Features

Een manier om te bepalen of een systeem gebruikt kan worden, is door te kijken naar de features die geïmplementeerd moeten worden. Een content repository is een laag boven op een data store. Dit kan een RDBMS zijn maar ook een filesysteem. Een repository bestaat uit een aantal componenten: een repository engine, een repository information model en een repository api. De JCR (jsr-170) heeft als information model de repository, workspace, node, property en mixin objecten en deze komen ook weer voor in de api. Met Jackrabbit, de reference implementatie, bestaan de statements uit workspace.getRootNode().getNode(“my:folder/my:file”).getProperty(“jcr:name”). Jackrabbit, heeft een aantal built-in mixin’s in de engine waaronder mix:versionable. Door het toewijzen van de mix:versionable krijgt een node extra properties zoals jcr:versionHistory en jcr:predecessors.

De volgende features zijn bij alle repositories terug te vinden.

Object management. Het opslaan van een repository object state, de property waarden, en attributen zoals type en mixins.

Dynamic extensibility. Elk object in een repository heeft een type. Via de repository engine kunnen types toegevoegd of uitgebreid worden. De types in een repository zijn vaak first-class repository objecten in plaats van metadata van de repository. Een repository kent niet zoiets als de tabel en kolom informatie van een relationele database. De manier van types is ruw weg te vergelijken met objecten in de Java Virtual Machine. De types worden in de JVM bewaard in Class objecten. De niet-Class objecten worden gekoppeld aan Class objecten die de object types definiëren.

Notification. Wanneer een repository object veranderd kan een repository notificaties sturen naar geïnteresseerde partijen. Met triggers in een relationele database kan je een beetje hetzelfde doen, maar triggers liggen vast in de metadata van de database. In een repository kunnen de geïnteresseerde partijen zich dynamisch aanmelden.

Version management. Bijhouden van versies is één van de belangrijkste features van een repository. Bij relationele databases wordt dit niet standaard meegeleverd. Elke applicatie moet hierdoor de versiebeheer functies zelf oplossen. Een repository heeft geen problemen om alle versies van een object op te leveren.

Search and indexing. Steeds meer repositories bieden een full text search faciliteit over de properties van nodes. Samen met een xpath of sql-like syntax ontstaat er een krachtig zoeksysteem over de repository. Dit is gedeeltelijk wat hibernate-search bied voor een relationele database.

Remote interfaces. Veel repositories bieden de api ook aan via externe interfaces. Protocollen die via een hiërarchische structuur werken, zoals webdav en rest, zijn redelijk te vertalen naar het repository model.

Als één van deze features nodig is in een project kan een content repository een goede optie zijn.

Modelleren

Een andere manier om te bepalen welk systeem het beste past is door een domein model te maken en de eigenschappen van de systemen te vergelijken met het domein model. Het modelleren van de data in een domein model en het model waarin het wordt opgeslagen in een systeem is verschillend. Tussen het domein model en een systeem is altijd een vertaalslag nodig. Mijn definitie van een domein model is terug te vinden in het Domain Driven Design boek van Eric Evans. De kleinste bouwstenen in dat boek zijn entities, value objects, services en modules. In de discussie over data opslag zijn entities en value objects alleen van belang.

Dus wanneer kies je wat? Simpel, kijk naar het patroon van entities en value objects in het domein model en bepaal of het hiërarchisch is of niet. Als dit zo is kan het zeer goed in een content repository en anders is een relationeel systeem geschikter. Maar net zoals alle andere dingen in het leven is dit niet zo zwart-wit. De diversiteit in domein modellen is groot en gedeeltes van een model zijn wel hiërarchisch en andere niet.

Elk domein model is met wat creativiteit wel om te zetten naar een relationeel model. Er is genoeg informatie en stapsgewijze regels te vinden hoe dit te doen. Een content repository legt regels op aan het content model waardoor er soms te veel concessies gedaan moeten worden in de vertaalslag vanuit het domein model. Een hele goede manier om te bepalen of het domein model past in een content model is om te kijken of het model voldoet aan de volgende regels.

1) Gebruik de hiërarchie. Voor een blogging site gebruik dan een structuur zoals

/content/myblog
/content/myblog/posts
/content/myblog/posts/what_i_learned_today
/content/myblog/posts/content_modelling_explained
/content/myblog/comments/ content_modelling_explained/i_like_it
/content/myblog/comments/ content_modelling_explained/i_like_it /i_dont_agree
/content/myblog/comments/ content_modelling_explained/

Zonder verder de structuur uit te leggen snapt iedereen hoe de content wordt gebruikt. Zelfs met deze structuur kun je aparte rechten uitdelen voor de posts en de comments. Anonieme gebruikers kunnen comments maken, maar niet de post aanpassen. Nadeel is wel dat er geen referentiële integriteit is tussen posts en comments en moet de applicatie zelf de comments weggooien als de post wordt verwijderd.

2) ID’s zijn overbodig. Zie 1 bij een goed gebruik van de hiërarchie en afspraken over paden heb je geen *Id velden nodig zoals in een relationeel model. In een relationeel model heb je altijd een KEY nodig om relaties naar anders records te maken. In hele exceptionele gevallen heb je een identifier nodig voor een node om daar naar te verwijzen. Dit kan in een JCR (jsr-170) met de mix:referenceable. De mix: referenceable is een mixin en kan aan elke node worden gekoppeld (dynamisch) als dat nodig is. Als er niet naar verwezen wordt hoeft een node het dus ook niet te hebben. In het content model hoef je dus niet hard ergens van te erfen om deze eigenschap te krijgen.

3) Gebruik references in mate. Referential integrity is kostbaar in een hiërarchisch model. Opeens kun je niet meer exporteren en importeren omdat de andere node misschien niet aanwezig is. Ook operaties op de repository en workspaces zoals merge, update, restore, clone die veel worden gebruikt voor versioning geven problemen. Als je niet de hiërarchie kunt gebruiken kijk dan eerst naar een natuurlijke identifier in de gerefereerde node. Voor een auteur property in een blog kan je bijvoorbeeld de gebruikersnaam gebruiken. Is er niet een natuurlijke identifier te vinden dan is het verstandig om de references, maar naar één kant in de hiërarchie te laten gaan. Dus alleen van tak A naar tak B en niet andersom.

Conclusie

Als je services zoals versioning, notification en search nodig hebt kan een content repository heel wat werk schelen. Als je deze services niet nodig hebt dan is een content repository wel een zware oplossing met nutteloze overhead voor je probleem. Een repository zal altijd zijn read performance proberen te verbeteren door caching te stoppen in de hiërarchie. Writes op de reppsitory zijnop eens zware operaties door cache invalidaties.

Als je meer wil dan een relationele database levert en het model binnen het hiërarchische model past dan is een Content Repository een goede keus. In alle andere gevallen zal een content repository alleen maar tegen werken. Nu klinkt het domein van repositories wel klein, maar veel enterprise systemen zijn ook hiërarchisch. De meeste applicaties gebruiken alleen maar 1-N relaties en als daar geen terugverwijzingen in zitten is het al hiërarchisch. Als binnen het domein model N-N relaties bestaan dan gaat dat moeilijk passen in een hiërarchisch model. Ook de gevallen waarbij een entity ontstaan is uit een N-N relaties doordat er velden aan toegevoegd zijn maakt het een moeilijke model om in een content repository te krijgen.

4 reacties »

  1. Wat is er nodig om 1-N en N-N relaties in een domein samen te kunnen laten gaan? Ik denk nu aan een situatie waarin de meeste relaties 1-N zijn, maar een beperkt deel van het domein uit N-N relaties bestaat. Hoe weeg je af of het toch nog mogelijk is om een content repository te gebruiken?

    Kost het laatste geval enkel complexere xpath queries en een performance verlaging? In dat geval is de verhoogde complexiteit misschien voor lief te nemen wanneer het grootste deel van het domein wel 1-N is en kan de implementatie van de content repository misschien een zodanige configuratie van caching op relaties mogelijk maken dat de performance wordt verbeterd.

    Kun je meer zeggen over de prijs die moet worden betaald voor het toch gebruiken van een content repository voor een domein model waarin “moeilijk” te passen N-N relaties zitten?

    Cees Roele October 20, 2009 11:26

  2. Een N-N relatie kun je alleen implementeren met references in een content repository. De objecten aan beide kanten van de relaties staan dan in een andere tak van de hiërarchie. Je moet dan alle problemen die ik hierboven over references al noemde zelf oplossen. Je moet al de problematiek van een N-N relatie, bi-directionaliteit en circulaire afhankelijkheid, en persistentie oplossen en dan krijg je de content repository services overhead er ook nog bij. Wat moet er gearchiveerd worden, wat geïndexeerd en welke events negeer je? Je maakt het jezelf alleen maar moeilijker dan het al is. Meer complexiteit betekent makkelijker fouten door onwetendhied op de lange termijn en dus hogere onderhoudskosten.

    Ik kan niet heel veel situatie bedenken waarbij je een N-N relatie echt nodig hebt. In heel veel gevallen kun je die reduceren naar een 1-N relatie in de scope van het domein probleem.

    Het meest voorkomende voorbeeld dat gebruikt wordt voor een N-N relatie is de klant-adres relatie. Naar mijn mening niet helemaal een sterk voorbeeld. Je kunt zeggen dat klant en adres allebei een entiteit (DDD) zijn, maar ik zou eerder kiezen voor adres als value object. Een value object is immutable. Als iemand verhuist maak je een nieuw adres aan en voeg je dat toe aan de klant. Dit betekent ook dat als de data van twee adressen hetzelfde zijn dan zijn ze semantisch gelijk. Als dezelfde data in twee entiteiten zit, twee klanten met dezelfde naam, dan zijn ze nog niet hetzelfde object. Deze N-N relatie (klant-adres) kun je dus ook zien als een 1-N relatie.

    Een andere manier van reduceren is door te bekijken of beide entiteiten ook aggregate roots zijn volgens de use cases. Een aggregate root is een domein object waarop je een methode aanroept vanuit de buiten wereld (m.a.w. de service laag). Er zijn maar weinig gevallen waarbij je op beide entiteiten methodes aanroept. Vaak ga je maar vanaf één kant de relatie af. En als je wel de andere kant op gaat dan kun je misschien bekijken of de use case ook nog werkt als je de andere kant langs gaat. Een belangrijke vraag die je kunt stellen bij de entiteiten is of ze ook zonder de andere ook nog betekenis hebben in jouw domein, of in DDD-termen in de bounded context waartoe de entiteiten behoren. In het klant-adres voorbeeld van hierboven heeft een adres niet veel betekenis zonder klant in een Customer Relation Management domein. Dit hele verhaal zit verpakt in mijn zin dat je de references maar één kant op laat gaan tussen takken.

    Mijn pleidooi is dat als je al “moeilijk” te passen N-N relaties hebt dat je het jezelf niet nog moeilijker moet maken. Maar laten we eerlijk zijn hoeveel “moeilijk” te passen N-N relaties bestaan er?

    Nico Klasens October 21, 2009 23:23

  3. you might want to look at vtd-xml for best possible xpath query perfomrance

    vtd-xml

    anon_anon November 26, 2009 0:37

  4. Sowieso een heel geschikt model voor websites en weblogs, maar dan weer niet echt geschikt voor een wiki bijvoorbeeld.

    Het voorbeeld van een adres van een bedrijf of persoon kan je in een content repository wellicht ‘modelleren’ met een zgn. mixin. De mixin is dan ook nog te gebruiken voor bijvoorbeeld afleveradressen van bestellingen.

    Wat ik mij dan weer afvraag, gegeven een weblog in een conten repository, waarbij elke post tot meerdere categorieen (tags) kan behoren:

    1) is de categorie een eigenschap van de post, of is de categorie een node met daaronder referenties naar de posts?
    2) Als de categorie een eigenschap van de post is, kan een content repository dan wel efficient een query naar alle posts in een bepaalde categorie uitvoeren?

    Omdat ik RDB’s gewend ben, heb ik de neiging ieder model in termen van tabellen en relaties te zien, waardoor een ogenschijnlijk simpel model als een weblog toch nog veel vragen oproept in de context van een content repository.

    Toch jammer dat er in .NET nog weinig aandacht voor lijkt te zijn…

    Michiel January 13, 2010 0:44

Reageer

RSS feed for comments on this post · TrackBack URI