We zien steeds meer organisaties van monolithische applicaties naar modulaire systemen bewegen. Een uitdaging die binnen dergelijke distributed systemen komt kijken is de manier waarop de modules onderling communiceren. Een veelgebruikte oplossing voor asynchrone communicatie, is om een messaging service zoals RabbitMQ te gebruiken. Wat je dan in de praktijk bij veel teams ziet is een oplossing zoals in het onderstaande diagram vereenvoudigd weergegeven:

Een actie op service 1 waarop een andere service moet reageren wordt als event op de queue gezet. Service 2 implementeert een listener die het event van de queue haalt en vervolgens verwerkt. Zolang de applicatie relatief klein is, functioneert deze manier van werken prima. Het wordt echter een stuk lastiger naarmate de applicatie groeit en de services door verschillende teams beheerd worden:

  • Om de beschreven action te implementeren wordt het team dat service 1 beheert afhankelijk van een listener die door een ander team gebouwd moet worden.
  • Er ontstaat een wederzijdse afhankelijkheid van de beide services van 1 message queue, welk team is nu verantwoordelijk voor events die niet opgepakt kunnen worden?
  • Events afkomstig van service 1 komen niet via het publieke endpoint van service 2 binnen, maar via een achterdeur. Dit is een aanzienlijke verhoging van de complexiteit en kan grote problemen geven bij versie updates.
  • Het documenteren van publieke REST api’s is veel eenvoudiger dan queue berichten.

Al met al de nodige redenen om af te stappen van de bovenstaande implementatie van een message broker laag.

Een alternatieve implementatie
Met een aantal relatief eenvoudige aanpassingen is het mogelijk om de services beter te isoleren en teams onafhankelijk van elkaar te laten werken:

  • Services communiceren alleen met elkaar via de publieke endpoints, wat de complexiteit van de communicatie drastisch verlaagd.
  • Waar asynchrone communicatie nodig is wordt een message broker gebruikt, echter de service zelf pakt deze asynchrone events op en pushed het bericht door naar andere service endpoints.
  • De enige afspraak voor samenwerking tussen services is via hun REST contract. Teams hebben meer vrijheid om eigen tools en technologie te kiezen waardoor meer maatwerk ontstaat.

Welke winst hebben we nu geboekt:

  • Teams zijn niet afhankelijk van andere teams voor het opleveren van features (mits er geen volledig nieuwe endpoints nodig zijn, maar dat is meestal niet het geval).
  • Een team is volledig verantwoordelijk voor het afhandelen van een action inclusief het aftrappen van vervolgstappen.
  • Het is volstrekt helder welk team verantwoordelijk is voor de queue van service 1.
  • Het is relatief eenvoudig om service 1 inclusief de propagatie geïsoleerd automatisch te testen inclusief het afhandelen van foutmeldingen die van service 2 terugkomen.

Auteur: Menno Jongerius, Bergler Competence Center © 2018