vrijdag 4 juli 2008

Ik rest mijn case

In de zgn. REST-SOA-discussie lopen allerlei vragen door elkaar heen: hoe makkelijk moet een protocol zijn? hoe erg is het om ingewikkelde tools, zoals ESBs, nodig te hebben? heb je bijvoorbeeld transaction awareness nodig? enzovoort.

Dat zijn allemaal interessante en ook relevante vragen, maar ik denk dat het toch vooral om iets anders gaat, namelijk de manier waarop je objecten (en daarmee semantische modellen) kunt gebruiken op het Web.

Toen zo'n 20 jaar geleden OO doorbrak, werd hoog opgegeven van de voordelen: je maakte een model in objecten van het relevante domein, je implementeerde dat in een daartoe geëigende omgeving, en het werkte. Schaalbaarheid, onderhoudbaarheid, en wat niet al waren je deel.

En dat kon dankzij het feit dat goed ontworpen objecten een unieke identiteit hadden en vindbaar waren op een unieke lokatie, ze stelden functionaliteit beschikbaar via een interface, en de interne implementatie en gegevens waren afgeschermd van de buitenwereld. Objecten waren eigenlijk zelfstandige entiteiten die elkaar kennen en met elkaar samenwerken.

Natuurlijk bleek de werkelijkheid wat prozaïscher, en kon er erg veel misgaan. Maar desondanks: als eea goed werd aangepakt, konden de beloften worden ingelost. Althans: binnen de run-time omgeving van een OO-omgeving, bijvoorbeeld een SmallTalk-image. En dus: eigenlijk slechts binnen het geheugen van een applicatie en zolangs als die applicatie loopt.

Persistentie en distributie verstoorden dus de OO-droom, en maakten het nodig om ingewikkelde oplosssinngen te bedenken, waarmee verzekerd werd dat objecten bewaard en geditribueerd konden worden, en wel op een "transparante" wijze. En dus werd er heel wat afgemapt.

Het persistentie-probleem is min of meer opgelost. Er kwamen OO-databases die het applicatiegeheugen en de "echte" database naadloos ("transparant", jazeker) in elkaar over lieten lopen. Erg mooi, en vrijwel niemand gebruikte het. Maar ook de wat meer aardse oplossing via een ORM (object relational mapping) bleek na wat vijven en zessen goed werkend te krijgen.

Het distributie-probleem bleek persistenter. :) Hoe moest de "ruimte" van objecten in één applicatie op één machine worden uitgebreid naar andere ruimtes van andere applicaties op andere machines? Met andere woorden: hoe maken we van al die ruimtes één grote ruimte?

De meest voor de hand liggende oplossingsrichting was: maak bovenop het netwerk een protocol wat ervoor zorgt dat objecten war dan ook elkaar kunnen kennen en direct boodschappen sturen. Java RMI is van deze route een voorbeeld.

Dat werkte, althans tot op zekere hoogte. Binnen een closely-knitted LAN was het netwerk stabiel genoeg om van het netwerk te kunnen abstraheren. Maar daarbuiten bleek dat een fictie: het netwerk was niet stabiel genoeg, en dan kun je het ook niet transparant maken. Bovendien waren de RMI-objecten niet echt persistent, omdat ze in feite afhankelijk waren van de applicatie waarbinnen ze bestonden. En tenslotte was de bovenop het netwerk vereiste laag dusdanig ingewikkeld dat het nooit een algemene standaard kon worden. En dat is ook niet gebeurd: RMI kun je alleen binnen de Java-architectuur gebruiken, en die is niet universeel.

Men kon er niet onderuit: het was niet mogelijk om een werkend object-netwerk te maken volledig geabstraheerd van het netwerk. Dus werd het begrip "component" (opnieuw) ingevoerd. Componenten waren entiteiten die op het netwerk draaiden, en als zodanig bekend waren, en via een functioneel interface aangeroepen konden worden.

Maar hoe verhielden de componenten zich tot de objecten?

Een eerste schot was: laat elk object, althans elk zelfstandig domeinobject, een component zijn. Maar er zijn verschrikkelijk veel domeinobjecten. Die allemaal een echte zelfstandige component maken zou veel te veel resources vergen. Immers, de functionaliteit die nodig is om iets een zelfstandige, vindbare, persistente eintiteit op een netwerk (een component dus) te maken, is fors.Dus moest er een oplossing worden bedacht waarin de object-componenten weliswaar algemeen bekend en bereikbaar waren, maar voor de standaard-component functionaliteit gebruik maakten van een soort component voedingsbodem.

De J2EE-EntityBeans waren een poging in deze richting, maar al ras bleek dat zelfs in deze aanpak veel te veel componenten ontstonden om een acceptabele performance te halen. Bovendien konden J2EE-componenten alleen goed met J2EE-componenten praten, dus van een universele ruimte was sowieso geen sprake. En vergelijkbare nadelen golden voor andere pogingen in deze richting zoals COM/DCOM.

Goed, objecten konden dus niet allemaal componenten worden. Dan maar een hybride benadering: we hebben componenten, en daarbinnen leven objecten. En we gaan een standaard ontwikkelen volgens welke componenten met elkaar, in via die route gedistribueerde objecten met elkaar, kunnen praten. En dat niet gebonden aan één talomgeving (zoals bij RMI). Dat was Corba.

Ingewikkeld? Ja, en ook nog eens slechts een standaard, nog geen implementatie. En je had er meestal dure en in ieder geval moeilijk te begrijpen tools voor nodig.

Een andere variant was: laten we het niet te ingewikkeld maken. We zien de componenten gewoon als objecten (ze hebben tenslotte al een unieke lokatie, en een interface met mooie functies, en we proberen zo'n beetje de OO-ontwerp principes voor het ontwerp van die componenten te gebruiken. Daar is weinig op tegen, en als er goede standaarden zijn of komen voor de communicatie tussen de componenten, dan heeft dit een goede kans van slagen. O ja, en heel belangrijk: we noemen het anders, dan lijkt het nieuw.

Ziedaar SOA, en dankzij XML, SOAP en de WS-* standaarden is het inderdaad een behoorlijk succes. En dankzij het Web en HTTP is de ruimte van componenten nu ook universeel.

Maar het lost het oorspronkelijke probleem niet op. Want SOA-services of -componenten zijn geen domeinobjecten, al lijken ze er een beetje op. Eigenlijk zijn we weer terug bij af: we hebben componenten (services) die elkaar kunnen vinden en met elkaar kunnen praten. Objecten bestaan wel, maar zitten binnenin de componenten. En als ee object een boodschap wil sturen aan een object in een andere component, moet eerst worden uitgezocht waar het doelobject zich eigenlijk bevindt, moet de boodschap vervolgens worden vertaald naar een serviceaanroep, moet daarbij worden aangegeven voor welk object de boodschap eigenlijk is bedoeld, en kan daarna de service-aanroep worden gedaan. En aan de andere kant moet de booschap dan weer worden uitgepakt en ontcijferd, en worden doorgestuurd naar het doelobject. Het kan wel, maar het is erg omslachtig. (Al is dat bij gebruik van MDD-technieken midner bezwaarlijk.)

Stand van zaken: we hebben eindelijk een universele ruimte, maar alleen voor componenten. Die gebruiken om echte objecten direct aan elkaar te hangen is niet gelukt, vanwege gebrek aan eenvoud, gebrek aan standaardisatie, en gebrek aan persistentie.

En toen kwam REST.

De resources van REST vormen een universele ruimte, ze zijn (uiteraard) persistent, ze zijn vindbaar en uniek dankzij de URI, het is een bestaande en zeer wijdverbreide standaard (HTTP in feite), en hij is bewezen makkelijk te gebruiken: het hele Web is erop gebaseerd.

Zouden objecten dan te mappen zijn op HTTP-resources?

Op het eerste gezicht is de mapping lastiger dan op RPC-style componenten. Objecten hebben een unieke identificatie ne locatie nodig, en dat biedt de resource inderdaad dankzij de URI. Maar niet elke via een URI te identificeren resource is een echt object, sommige zijn een "representation" van een object, in OO-termen een returnwaarde van een method.

En de REST-methods passen ook niet zonder meer op de methods in een objectmodel. Immers, REST kent slechts een beperkt aantal methods (GET, PUT, POST, DELETE zijn relevant in dit verband). De eerste drie hebben een heel strikte betekenis, en de consequentie is dat extra informatie (parameters in een method van een object) in de URI wordt gestopt. En methods die niet kunnen worden gezien als een GET, PUT of DELETE moeten via POST worden "getruct".

Vanuit OO ziet het er dus op het eerste gezicht niet zo heel goed uit. Maar als je wat beter kijkt, lijkt dat erg mee te vallen. De object-methods uit een goed ontworpen OO-domeinmodel blijken vrijwel alle zonder veel problemen te kunnen worden vertaald naar HTTP-methods.

Maar dan nog steeds: het wringt. HTTP-resources zijn brokken informatie die aan elkaar zij gerelateerd, met verscheidene representaties. Het zijn geen objecten die potentieel een zeer specifiek hebben. Resources hebben juist zoveel mogelijk een standaard-interface.

De claim van REST-voorstanders is dat dat standaard-interface juist een voordeel is. Doordat bijvoorbeeld de betekenis van GET is gedefineerd als: een GET mag een resource niet wijzigen, kunnen HTTP-servers GET-resultaten cachen. En door de simpele method + URI structuur zouden hergerbuikers makkelijker in staat zijn te snappen wat er gebeurt dan als ze bv een WSDL-file moeten lezen voor een SOA-service. Bij REST is de structuur van de resources en de informatie binnen de resources (te verkrijgen als representatie) primair.

In OO is dat van oudsher anders: daar is het functionele interface primair, en de informatie is juist geëncapsuleerd. Althans: volgens de OO-puristen. Als je kijkt naar een goed domeinmodel, dan blijkt dat veel meer een "semantic web" achtig ding te zijn, waarbij het interface helemaal niet zo'n leidende rol speelt.

(Voor de duidelijkheid: voor OO-toepassingen op een ander nivo dan de implementatie van domeinmodellen ligt dat anders, maar die worden ook niet geraakt door deze problematiek.)

En dus is het verschil met de REST/resource-oriented denkwijze helemaal niet zo groot als het op het eerste gezicht lijkt. En het voordeel van REST, namelijk dat je een universele ruimte met dingen (resources, objecten) krijgt, met unieke locaties, met de mogelijkheid daar via een standaard protocol boodschappen aan te sturen, en dat zonder dat je last hebt van het begrip component, dat voordeel is immens.

Is REST dus de toekomst? Ja, ik denk het wel. Maar we moeten nog wel uitzoeken hoe goed die mapping van OO op REST nou echt werkt, en ik denk dat er toch soort REST-standaard bovenop HTTP (bij via een nadere uitwerking van POST) nodig blijkt. Ook al is dat tegen het zere been van de REST-puristen.

Betekent dit dat "gewone SOA" ten dode is opgeschreven? Dat weet ik niet zo zeker. Voor "semantic web" achtige webservices is REST de eerste keus, maar er zijn ook webservices die andere eisen stellen. In de beruchte WS-* stack zitten een aantal zaken (bijvoorbeeld op het gebied van reliability en security) die wat lastig zijn vorm te geven in een REST-context. Het zou kunnen dat we die veel minder vaak nodig hebben dat we nu denken, maar of dat echt waar is moet nog maar blijken.

Maar er zijn zeker een hoop SOA/SOAP-etc interfaces die makkelijk via REST zouden kunnen werken, en ik denk dat we REST-facades bovenop SOA-interfaces gaan zien. Dat zal de migratie ook makkelijker maken.

3 opmerkingen:

Anoniem zei

Hmm, wordt wel de Francis Fukuyama van de automatisering.

Unknown zei

Wat is een "representation" van een object? Werkt het als een referentie naar dat object, kun je het bijvoorbeeld op die manier veranderen, of is het alleen data en niet het object zelf?

Anoniem zei

Mat, een geweldig goed verhaal! Ik snap niet alles wat je schrijft maar het verschaft een prima overzicht van zaken in de tijd die allemaal aan elkaar hangen, opvolgers van elkaar zijn, etc.

Hulde!

Groet, Rik L.