De afgelopen periode is er intensief gewerkt aan de basis van een nieuw TrueCare, ons communicatiesysteem voor alle services en support die True biedt. We hadden namelijk een flinke uitdaging: legacy moest samengevoegd worden tot één nieuwe interface. Al snel wisten we dat een microservice architectuur ons zou helpen in de transitie naar één TrueCare. In dit artikel deel ik, Peter van Kleef (Project Manager & Product Owner van TrueCare) de inzichten van het Development-team.
In de vorige blogpost kon je lezen over de aanleiding van de microservice-architectuur van True. De uitdagingen waren bekend. Hoe maak je vervolgens een begin ombouwen van bestaande applicaties en functionaliteiten naar microservices?
Dit artikel gaat in op het technisch perspectief van microservices. Ben je benieuwd naar het business perspectief? Lees dan 'De businessuitdagingen van een microservice-architectuur'.
Een plan
Vanaf het begin wisten we dat het herschrijven van alle bestaande functionaliteit een gigantische klus zou zijn. Dat gaat niet over een nacht ijs en doe je niet even tussen de bedrijven door. Om tot een werkbare oplossing te komen hebben we een set basisregels gedefinieerd.
Een vaste stack van frameworks maakt het eenvoudiger voor (nieuwe) developers om thuis te raken in de code. Het helpt ook om niet elke keer het wiel opnieuw uit te vinden. In de besprekingen is gekozen voor CakePHP voor de back-end en Angular (2+) voor de front-end. Dit omdat oudere versies van deze frameworks al in gebruik waren binnen True en omdat we daarin de meeste kennis en ervaring hadden.
Een AppSkeleton ontwikkelen maakt het makkelijker om een nieuwe microservice te starten. De AppSkeleton bevat randzaken zoals Event Sourcing, basis API-structuur, basis boilerplate en vooraf bepaalde CI/CD-parameters. Daardoor kunnen we nieuwe microservices in rap tempo opzetten.
Daarnaast is Event Sourcing belangrijk. Door een (gemodificeerde) vorm van Event Sourcing toe te passen hebben we een audit trail van alles wat er gebeurt en daarmee een volledige geschiedenis.
In pure vorm betekent Event Sourcing dat alle wijzigingen die gedaan worden aan een entiteit als losse ‘events’ opgeslagen worden. Deze events kun je vervolgens in chronologische volgorde ‘afspelen’ (replay) om tot de huidige staat van een entiteit te komen. In de pure vorm wordt de huidige staat niet apart bijgehouden.
De aanpak
Dat we zaken op een andere manier aan wilden pakken was duidelijk – maar waar start je?
Medio 2017 kwamen er een aantal wensen voorbij; klanten wilden graag gebruik maken van API’s; vooral voor DNS en SSL. Daarnaast waren er wensen voor meer automatisering en verbeterde functionaliteiten in de interfaces van onder andere DNS en SSL.
Dat klinkt als twee separate projecten – en na kort overleg is er destijds gekozen om deze twee projecten als eerste microservices te ontwikkelen, in combinatie met Domains (het beheren van domeinnamen).
Een van de eerste stappen was om samen met het team tot een architectuur te komen. Omdat het de eerste microservices betrof is er veel overleg geweest en heeft het flink wat voeten in de aarde gehad om tot een vast omlijnd plan te komen. Uiteindelijk heeft dat overleg geleid tot de schematische architectuur die je hieronder ziet:
Zoals je ziet zijn microservices gelaagd opgebouwd. Specifieke logica voor bijvoorbeeld de communicatie met een externe partij wordt daarmee afgesplitst van onze eigen toepassing. Dat maakt, bijvoorbeeld, het wijzigen van samenwerking met een externe partij eenvoudiger – met veel minder impact.
Vooral de AppSkeleton en de toepassing van Event Sourcing zijn langzaamaan gegroeid. Tijdens de ontwikkeling van de eerste microservices kom je er gaandeweg achter dat bepaalde zaken net niet helemaal werken zoals je wil, en worden er continu kleine aanpassingen gedaan.
In eerste instantie was het team voornemens om de huidige front-end intact te houden en de backend te vervangen met de nieuw te bouwen microservices.
Uitdagingen en afwegingen
Het opbouwen van microservices brengt uiteraard ook een aantal uitdagingen met zich mee.
Uitdaging #1: Event sourcing is een mooi principe: maar kost dat niet enorm veel resources als je iedere keer een volledige replay moet doen om tot de huidige staat te komen? En hoe kun je dan makkelijk queries uitvoeren op de database?
Oplossing: We kozen ervoor om een hybride vorm toe te passen. Het voordeel hiervan is dat je zowel de events als de huidige staat opslaat. Hierdoor is er een volledige audit-trail en kunnen onze Data Scientists veel meer bruikbare informatie uit de verschillende systemen halen. Door ook de huidige staat op te slaan kunnen we informatie sneller tonen, ontstaan er geen performanceproblemen en kunnen queries makkelijker op de database losgelaten worden.
Oplossing: We kozen voor een combinatie van API's, Message Brokers (RabbitMQ) en Webhooks. Daarmee zorg je ervoor dat er altijd communicatie plaats kan vinden - ook als een specifieke microservice tijdelijk niet bereikbaar is. In de onderstaande afbeelding is schematisch weergegeven hoe deze combinatie zich verhoud tot de eerder genoemde microservices: 1. Een command is een opdracht. Een voorbeeld: 'Registreer een nieuw SSL certificaat' 2. Vervolgens wordt er, in sommige gevallen, met een externe partij geschakeld middels API communicatie 3. Via een webhook event wordt aangegeven dat de entiteit een update behoeft. 4. Een event is een omschrijving van wat er gebeurd is. Een voorbeeld: 'Een nieuw SSL certificaat is geregistreerd'.
Door dat onderscheid kunnen andere microservices iets met de events doen – denk bijvoorbeeld aan facturatie, notificatie of inventarisatie van en over de certificaten.
De gekozen oplossingen zijn gaandeweg het eerste project opgekomen en zijn het resultaat van veel overleg tussen onze super slimme developers.
Toch maar een nieuwe front-end?
Het behouden van de huidige front-end was destijds een valide optie. Het hele team heeft daar in eerste instantie unaniem ook voor gekozen. Uiteraard moeten er, voor ingebruikname van de nieuwe API’s, wat aanpassingen gemaakt worden maar die zijn te overzien.
Een aantal Developers – en ik stiekem ook als (ex) front-end developer en enthousiasteling – vonden het toch leuk om een Proof of Concept front-end voor de nieuwe API’s op te zetten. Daarom is in november 2017 een kleine start gemaakt aan de nieuwe front-end. Dat is vooral als hobbyproject in eigen tijd geweest, toen nog in Angular 2 – inmiddels in Angular 6.
De huidige front-end kon worden omgebouwd, maar was in een oude versie van het framework opgezet en was heel erg nauw gekoppeld aan de backend (met veel automagische rendering).
De voordelen van de nieuwe front-end
Al snel werd duidelijk dat een aparte, nieuwe, front-end veel voordelen met zich meebracht:
Functionaliteit hergebruiken – de nieuwe front-end biedt ons de mogelijkheid om functionaliteiten (componenten) op verschillende plekken in de applicatie te hergebruiken.
Betere beveiliging – De nieuwe front-end biedt ons de mogelijkheid om veel betere beveiliging in te bouwen (via Guards).
Betere structurering – De nieuwe versie van Angular biedt een betere structurering (en performance) door de indeling in modules met aparte services en componenten.
Drie applicaties in één – De nieuwste Angular-versie biedt ons de mogelijkheid om drie verschillende applicaties (TrueCare, TrueAccounts en ons administratiepaneel) binnen één project te bouwen. Daardoor kan een heel stuk basislogica gedeeld worden binnen het project – waardoor duplicatie van code gereduceerd wordt en functionaliteiten toch apart in de verschillende applicaties ingebouwd kunnen worden.
Door verder onderscheid te maken reduceren we verdere duplicatie en voorkomen we onnodige complexiteit. We hebben het volgende onderscheid gemaakt:
- Core Componenten, deze componenten vormen de basis van alle apps
- App-specifieke Componenten, componenten die voor één specifieke app gelden
- Base Componenten, abstracte componenten die toegepast kunnen worden in elke app middels een Extend
- Shared Componenten, componenten die in iedere app toegepast kunnen worden en gelijke functionaliteiten hebben in de verschillende apps.
Een portaal voor alle klanten – Het meest belangrijke argument is echter, zoals in het eerste deel van de blogreeks reeds aangehaald is, dat een nieuwe front-end een veel eenvoudigere weg biedt om de nieuwe functionaliteiten voor al onze klanten, Webspace & Workspace, uit te rollen.
Zodoende is het hobbymatig opgezette project ingelijfd bij het developmentteam en is er – uiteraard in overleg – ook op managementniveau gekozen voor het uitbouwen van deze nieuwe front-end.
Authenticatie en autorisatie
Beveiliging van alle nieuwe microservices, API’s en front-end(s) is een van de belangrijkste onderdelen van het traject. Ook daarover is intensief en extensief gebrainstormd en vergaderd voordat er een duidelijk plan van aanpak was.
Relatief snel werd duidelijk dat we het OAuth2-principe wilden toepassen voor authenticatie, gecombineerd met een uitgebreid RBAC (Role Based Access Control) systeem voor autorisatie.
Daar is een nieuwe microservice voor ontwikkeld; Accounts. Deze microservice wordt vanuit verschillende bronnen gevoed, zodat alle gebruikers van de verschillende systemen daarin bekend zijn.
Authenticatie tokens worden via deze microservice aangemaakt, gecontroleerd en vernieuwd. Bij ieder request dat gedaan wordt bij een microservice wordt een extra check uitgevoerd via de Accounts-microservice om er zo zeker van te zijn dat de gebruiker een geldige sessie heeft.
Benodigde rollen voor het uitvoeren van een actie worden ook via Accounts gecontroleerd. Iedere gebruiker heeft rollen, en voor iedere functionaliteit (zowel in de front-end als de back-end) zijn specifieke rollen vereist. Om ervoor te zorgen dat er geen ongeldige acties uitgevoerd kunnen worden (via de API’s) worden ook de rollen per actie gecontroleerd bij de Accounts Microservice.
Een nieuw loginformulier waarmee alle huidige formulieren komen te vervallen. De introductie van deze nieuwe microservice biedt ons tevens de mogelijkheid om een nieuw loginformulier te introduceren en de oude applicaties (zonder al te veel bijkomend werk) ook om te zetten naar de nieuwe OAuth2-standaard. Daardoor kan iedere gebruiker vanaf de introductie inloggen via hetzelfde formulier.
Eén sessie voor de verschillende apps zorgt ervoor dat een gebruiker ongehinderd gebruik kan maken van alle functionaliteiten. Door de nieuwe OAuth2-setup worden sessies meegenomen tussen de verschillende (oude en nieuwe) systemen om. Hierdoor ondervinden gebruikers zo min mogelijk hinder van de onderverdeling in systemen tijden de transitieperiode.
Technische uitdagingen
In dit artikel heb je meer gelezen over de technische uitdagingen van onze legacy-infrastructuur, hoe we deze hebben aangepakt met microservices en welke afwegingen we daarbij hebben gemaakt. In het volgende artikel vertellen wat wat meer over het resultaat. Wat heeft deze microservice-architectuur ons opgeleverd, wat hebben we geleerd en hoe zien we de toekomst?