Event-driven architectuur is overal. Elk conferentiegesprek, elke architectuurreview - vroeg of laat valt de term event-driven. Maar niet elk systeem heeft het nodig, en verkeerd toegepaste events maken je software complexer, niet beter. Na tientallen jaren ervaring met bedrijfskritische systemen delen we onze vuistregels.
Wat bedoelen we eigenlijk?
Bij event-driven architectuur reageren componenten op gebeurtenissen (events) in plaats van dat ze elkaar direct aanroepen. Een OrderPlaced-event wordt gepubliceerd, en abonnees - facturatie, voorraad, notificaties - reageren onafhankelijk. Klinkt elegant. Maar elegantie is geen architectuurdoel.
Wanneer events wél werken
Events zijn krachtig wanneer je te maken hebt met ontkoppeling tussen domeinen. Als de afzender niet hoeft te weten wie er luistert, en de ontvangers onafhankelijk kunnen falen en herstellen, dan is een event-driven aanpak zinvol. Concrete scenario's:
- Meerdere consumers die onafhankelijk reageren op dezelfde gebeurtenis
- Asynchrone verwerking waar de gebruiker niet op het resultaat hoeft te wachten
- Cross-domain communicatie in een modulaire of gedistribueerde architectuur
- Audit en traceerbaarheid - events vormen een natuurlijk logboek
Wanneer events problemen veroorzaken
Het wordt lastig wanneer je events gebruikt waar een simpele synchrone aanroep volstaat. We zien dit patroon regelmatig: een team introduceert een message broker voor communicatie tussen twee services die altijd samen deployen en altijd dezelfde database delen. Het resultaat? Extra infrastructuur, extra faalscenario's, en debugging wordt een nachtmerrie.
Rode vlaggen voor onnodig event-gebruik:
- Je hebt request-reply semantiek nodig (vraag-antwoord)
- De consumer moet direct reageren voor de operatie slaagt
- Er is maar één consumer en die verandert nooit
- Je introduceert events alleen om "modern" te zijn
Het grijze gebied: eventual consistency
De grootste valkuil is eventual consistency. Bij synchrone communicatie weet je: de operatie is geslaagd of gefaald. Bij events accepteer je dat het systeem tijdelijk inconsistent kan zijn. Dat is prima voor veel scenario's, ook op schaal. Maar het is riskant wanneer gebruikers op basis van stale data beslissingen nemen, bijvoorbeeld een voorraadsysteem dat iets als beschikbaar toont terwijl het al is verkocht, of een toegangscontrolesysteem waar ingetrokken rechten nog minutenlang actief blijven.
Vraag jezelf af: kan de business leven met een vertraging van seconden of minuten? Zo niet, dan is een synchrone aanpak waarschijnlijk beter - of je hebt een Saga-patroon nodig, wat weer eigen complexiteit introduceert.
Praktische richtlijnen
- Begin synchroon. Introduceer events pas wanneer je een concrete reden hebt: schaalbaarheid, ontkoppeling, of meerdere consumers.
- Kies bewust tussen commands en events. Een command is een verzoek ("MaakFactuur"), een event is een feit ("FactuurAangemaakt"). Verwar ze niet.
- Ontwerp je events als contract. Zodra consumers afhankelijk zijn van je event-schema, is het een publieke API. Versiebeheer is niet optioneel.
- Monitor je queues. Dead letter queues, consumer lag, retry-storms - als je dit niet monitort, weet je pas dat het misgaat als de business belt.
- Test het faalscenario. Wat gebeurt er als een consumer offline is? Als een event dubbel wordt afgeleverd? Idempotentie is geen nice-to-have.
De juiste balans
De beste architecturen die we bouwen combineren synchrone en asynchrone communicatie. Binnen een bounded context houden we het vaak synchroon en eenvoudig. Tussen bounded contexts gebruiken we events om domeinen te ontkoppelen. Die pragmatische middenweg levert systemen op die begrijpelijk, testbaar en operationeel beheersbaar zijn.
Bij NForza helpen we teams om dit soort architectuurkeuzes weloverwogen te maken - gebaseerd op decennia ervaring met software die er écht toe doet.