De voordelen van het gebruik van EDA zijn legio. Maar hoe gebruik je nu eigenlijk EDA in de praktijk? Wat als je al een enorm ecosysteem van toepassingen hebt die enkel en alleen via synchrone communicatie worden geïntegreerd en je nog nagenoeg geen business Events publiceert? In deze blog-post stellen we een methode voor om een applicatie te laten evolueren om meer Events te gaan publiceren.
Binnen de instellingen van de Sociale Zekerheid groeit het besef dat Event Driven Architecture (EDA) erg nuttig kan zijn als onderdeel van de integratiestrategie (we hebben hierover reeds uitvoerig geblogd en de belangrijkste voordelen zijn ook samengevat in ons webinar van november). We hebben in ons IT ecosysteem echter al een enorm groot aantal systemen en applicaties die hier nog niet noodzakelijk van gebruik maken. Het zou zonde zijn als we ons voor EDA enkel zouden focussen op nieuwe toepassingen – de belangrijkste business Events in het ecosysteem worden waarschijnlijk al gecapteerd door reeds bestaande diensten. Maar hoe kunnen we er dan voor zorgen dat deze Events effectief via EDA technologie – als effectieve IT Events – worden gepubliceerd en beschikbaar worden in het ecosysteem voor subscribers?
In deze blog zullen we een mogelijke stap voor stap benadering bekijken, waarmee we reeds bestaande toepassingen meer Event-Driven kunnen maken. Niet alleen maar door ze stelselmatig meer Events te laten publiceren (al zal dit een logische eerste stap zijn), maar ook door ze hun eigen Events te laten gebruiken.
Een noodzakelijke disclaimer hier, is dat het Event-Driven maken van toepassingen niet het enige is dat zal moeten gebeuren om te evolueren naar een ecosysteem met een ruim en matuur gebruik van EDA. Wat dit allemaal zou moeten inhouden (b.v. het opzetten van een goede Event Broker), staat momenteel nog ter discussie in onze innovatiewerkgroep, al kunnen we daaromtrent uiteraard onze mening geven in een toekomstige blog-post…
Fase 1: Event-Publicatie, “Na de Feiten”
De eerste stap in het refactoren van een bestaande toepassing is meteen ook de belangrijkste voor het ecosysteem: het publiceren van Events. We moeten dit in eerste instantie als een zuivere toevoeging aan de functionaliteiten beschouwen, zonder enige impact op de reeds bestaande. Daarom zullen we een Event pas publiceren “na de feiten”, i.e. nadat alle zaken die normaal gezien door de toepassing worden gedaan, reeds zijn uitgevoerd.
Laten we kijken hoe we dit kunnen doen voor één Event (we kunnen dit uiteraard herhalen om de toepassing meerdere Events te laten publiceren). Beschouw het (informele) sequentiediagram in Fig. 1: een bepaald commando komt binnen in de toepassing (normaal gezien via een API), en er is besloten dat dit aanleiding geeft tot het voorkomen van een bepaald business Event; we willen dit Event dan ook publiceren als gevolg van het ontvangen commando. De toepassing doet reeds ander werk (“Internal Work”) als gevolg van het commando, en het commando moet worden beschouwd als een transactie (zoals we zien in “Command Transaction”).
Om het Event te publiceren met een minimale impact op de werking van de toepassing, zullen we het (via het “EDA Subsystem”) publiceren nadat we de transactie hebben vervolledigd. In de praktijk zal dit b.v. kunnen door (bínnen de transactie) een extra taak in de toepassing te creëren in een aparte thread, die pas wordt uitgevoerd nadat de transactie is afgewerkt (de “publicatietaak”). Het creëren van deze taak zal een zo eenvoudig mogelijke instructie zijn in de programmatie, met een veel kleinere kans op falen dan het effectieve uitvoeren van de taak, en kunnen we daarom dus veilig binnen de transactie doen.
Wanneer dan, achteraf, de publicatietaak effectief wordt uitgevoerd, zal dit door het EDA subsysteem gebeuren, en zullen we dan effectief het Event publiceren (in eerste instantie kan dit als “mock”: een operatie zonder enig effect, later zullen we het Event effectief aanbieden aan de Event Broker). Aldus komt het Event terecht in het ecosysteem en kan men het nuttig beginnen gebruiken.
Fase 2: Event-Publicatie, “In Transactie”
In fase 1 hebben we, om een zo min mogelijke impact te hebben op de performantie en kans op falen van de transactie, een nogal gekunstelde manier moeten gebruiken om het Event te publiceren búiten de transactie om. Een logische volgende stap is dan ook om het publiceren van het Event onderdeel te laten uitmaken van de transactie. Bijgevolg zullen we dan ook garanderen dat, indien de transactie volledig wordt uitgevoerd, het Event ook wordt gepubliceerd. Dit kan een garantie zijn van groot belang voor de Event subscribers verderop in het ecosysteem, vanaf nu kan men erop rekenen dat álle Events van dat type de revue zullen passeren.
Beschouw nu Fig. 2: het verschil met de vorige tekening is niet groot: we zien nu dat het EDA subsysteem wordt opgeroepen tijdens het uitvoeren van het normale werk als gevolg van het binnenkomende commando. Wanneer het Event is gepubliceerd én het andere werk is uitgevoerd, kan de transactie worden afgewerkt. Publicatie van het Event betekent dat we bevestiging hebben gekregen van de Event Broker dat het goed door deze is ontvangen, maar uiteraard niet noodzakelijk reeds naar de subscribers is gestuurd. Dit is net één van de voordelen van de asynchrone communicatie binnen EDA.
Bij deze fase moeten we er wel op letten dat we op een zeer betrouwbare Event Broker kunnen rekenen. Vermits deze online moet zijn om het Event in ontvangst te nemen tíjdens de transactie, zou de transactie worden geblokkeerd indien er daarbij een fout optreedt. Men zou dit als een nadeel kunnen beschouwen, maar we moeten er aan denken dat het Event kan worden gebruikt als plaatsvervanger van het eventuele rechtstreeks oproepen van alle subscribers, indien deze tijdig op de hoogte moeten worden gebracht van wat er wordt beoogd met het uitvoeren van de transactie. Indien men tijdens het verwerken van het commando normaal gezien beroep deed op achterliggende APIs van andere systemen, kan men deze andere systemen nu in plaats daarvan inschrijven op het Event, waardoor deze API calls niet meer hoeven gebeuren en onze toepassing dus niet langer afhankelijk is van de achterliggende. Op deze manier vermínderen we de kans op falen van de transactie indien de Event Broker betrouwbaarder wordt gemaakt dan de andere achterliggende systemen.
Fase 3: Event-First Programming, Transactioneel
Tot nu toe waren onze wijzigingen in de oorspronkelijke toepassing vrij conservatief: het doel was een reeds bestaand systeem Events te laten publiceren ten voordele van het grotere ecosysteem (we zagen daarbij dat dit in principe kan leiden tot het verminderen van het gebruik van APIs van andere systemen en op die manier tot minder afhankelijkheden).
Vanaf fase 3 zullen we echter ook de eigen toepassing gebruik laten maken van de eigen Events, alvorens, als gevolg van het ontvangen van deze Events, het normale werk te doen. Dit noem ik Event-First programming, en het laat toe om een standaard stramien te hebben om te reageren op binnenkomende informatie: alle belangrijke wijzigingen komen nu binnen via Events. Dat betekent dat we op dezelfde manier zullen reageren op de eigen Events, als op andere Events van het ecosysteem, komende van andere toepassingen.
Beschouw Fig. 3: om de transactie (voor het commando) op te volgen, voeren we een extra controller in. Als eerste stap wordt er nu een Event gepubliceerd als gevolg van het binnenkomende commando. Het EDA subsysteem zal dit Event publiceren, en óók de applicatie zelf (naast eventuele andere in het ecosysteem), zullen er zich voor inschrijven. Wanneer het Event dan opnieuw binnenkomt in de applicatie ten gevolge van de subscription, wordt het effectieve werk uitgevoerd dat de applicatie moet doen ten gevolge van het commando. Om de transactie, ten slotte, te kunnen beëindigen, moet de controller worden ingelicht wanneer het werk is gedaan. Hier zijn we vrij om dit opnieuw via een Event te doen, of op een andere manier (gebruik makend van een of ander bestaand mechanisme voor communicatie tussen threads dat wordt aangeboden door de programmeertaal).
Indien we deze fase voor een toepassing volledig implementeren (i.e. toepassen op álle binnenkomende commando’s), kunnen we de toepassing verder gaan modulariseren volgens het principe van CQRS (Command & Query Responsability Segregation). Dit wil zeggen dat modules die reageren op commando’s apart zullen staan van modules die reageren op queries.
Als u heeft opgemerkt dat het een beetje raar is dat we via een extra controller gaan wachten op een bericht dat het werk, als gevolg van ons Event, is gedaan en we onze transactie nu kunnen beëindigen, dan heeft u gelijk: we hebben deze fase eerder als illustratie ingevoegd om het contrast met de volgende fase te verduidelijken. Maar het lijkt ons eerder aangeraden om fase 3 over te slaan en onmiddellijk gebruik te maken van Eventual Consistency!
Fase 4: Eventual Consistency
Tot nu toe hielden we sterk vast aan de transactionaliteit van het uitvoeren van ons binnenkomende commando. Eigenlijk is dit een kunstmatig opgelegde beperking, die ons ervan weerhoudt maximaal in te zetten op de availability van het ecosysteem (zie: CAP theorema). In fase 4 zullen we gaan vertrouwen in het concept Eventual Consistency: we gaan het commando als uitgevoerd beschouwen, van zodra we zeker zijn dat ons Event is gepubliceerd. We gaan er dan van uit dat de toestand van de applicatie (en, per extensie, van het volledige ecosysteem), na verloop van tijd (“eventually”) consistent zal worden en alle nodige zaken zijn gebeurd als gevolg van het commando (ook in andere toepassingen!). Typisch zal deze tijd redelijk kort zijn; we laten enkel de mogelijkheid open dat het langer kan duren, omdat we het falen van een achterliggend systeem nu kunnen toelaten (zie ook het PACELC theorema).
Beschouw nu Fig. 4: eigenlijk doen we hier hetzelfde als in Fig. 3, maar dan met een vereenvoudiging: er hoeft geen feedback meer te komen van “Internal Work” naar de “Controller” voor het commando. Deze laatste zal nu simpelweg het commando als uitgevoerd beschouwen wanneer het Event is verstuurd (we hadden de extra controller eigenlijk terug weg kunnen laten). Met deze manier van werken verkrijgen we het meeste voordeel uit EDA voor de eigen applicatie (het voordeel voor het ecosysteem kregen we reeds in fase 1).
Besluit
In deze blog-post lieten we een mogelijke, stapsgewijze evolutie zien van een computerprogramma dat bij het uitvoeren van een commando een business Event zou moeten publiceren. In de eerste fases wordt de impact geminimaliseerd; in latere fases wordt de toepassing zelf meer Event Driven gemaakt. Het spreekt voor zich dat deze manier van werken niet de enige goede is, en dat men ook niet steeds álle stappen hoeft te zetten.
We beseffen dat de vier fases zeer verboos zijn gedocumenteerd in dit schrijven; voor programmeurs kan dit nogal overbodig lijken. Maar we richten deze blog ook op niet-programmeurs, zodat voor hen een tipje van de sluier wordt gelicht over hoe programmeurs moeten nadenken over het echte binnenwerk van een toepassing, en hoe dit in verband staat met de communicatie op schaal van het ecosysteem waarbinnen een toepassing opereert.
Het publiceren van herbruikbare business Events is cruciaal voor een groot IT ecosysteem, waarbij integraties, louter op basis van APIs, op langere termijn voor een verminderde resiliëntie en agility kunnen zorgen. Een goed evenwicht tussen integraties via zowel synchrone als asynchrone communicatie, waarbij EDA als evenwaardig wordt beschouwd aan API, leidt tot het meest gezonde ecosysteem van applicaties.
_________________________
Dit is een ingezonden bijdrage van Koen Vanderkimpen, IT consultant bij Smals Research. Dit artikel werd geschreven in eigen naam en neemt geen standpunt in namens Smals.
Lijkt mij dat vanuit business er al een nood aan event-verwerking bestaat. Ik denk bv aan mutaties zoals de INSZ mutatie die op dit moment op een andere manier doorgegeven worden.
Mogelijk is er vanuit EDA een antwoord op problemen die we daar al kennen:
Applicatie A produceert een event.
Applicatie B verwerkt dit event. Applicatie B wil nu informatie van Applicatie C en past in zijn vraag om die informatie al de context aan aan de verwerking van het event van Applicatie A.
Is er een garantie dat Applicatie C eerst het event van Applicatie A verwerkt vooraleer de vraag van Applicatie B te verwerken?
Ik veronderstel dat in het ideale geval de vraag van Applicatie B ook een event is en er een orde is in de events zodat we zeker zijn dat Applicatie C de events in de correcte orde verwerkt.
Complexer geval als het Applicatie D is die het event van Applicatie A moet verwerken (door bv iets aan te passen in een achterliggende database) en Applicatie C die het event van Applicatie B verwerkt (door een query uit te voeren op die achterliggende database).
Hoe gaat EDA met dergelijke gevallen om?
Dag Philippe,
Wat betreft het eerste probleem: er zijn natuurlijk altijd meerdere oplossingen en keuzes over waar de controle juist gebeurd, maar in dit geval lijkt een goede optie mij om app C een event te laten sturen nadat het de info van app A heeft verwerkt. App B kan dan hierop wachten alvorens de vraag te stellen aan C.
Het complexere geval met app D: Ook hier kan C eventueel wachten op een event notificatie van D (én uiteraard ook B). Ik zou echter wel vermijden dat meerdere applicaties gebruik maken van dezelfde achterliggende database. Voor mij is dat een anti-pattern 🙂 .
Dag Koen,
je beschrijf dit als 4 stappen, maar ik denk dat dit niet noodzakelijk “niveau’s” van EDA zijn maar misschien eerder patterns die in bepaalde context kunnen toegepast worden.
Wouter