Naar de inhoud
maarten.
Alle posts

Monoliet of microservices: architectuur en beheer

Maarten Soetens 11 min lezen

De keuze tussen een monoliet en microservices bepaalt meer dan hoe code is ingedeeld: ze beïnvloedt releases, gegevensbeheer, foutafhandeling en samenwerking tussen teams. Lees welke afwegingen daarbij spelen en waarom een opgesplitste architectuur niet vanzelf eenvoudiger te schalen of te onderhouden is.

Deployment bij een monoliet en bij microservices

In een monoliet worden onderdelen doorgaans samen gebouwd en als één applicatie uitgerold. Een wijziging in een kleine functie kan daardoor een release van het volledige systeem vereisen, ook wanneer andere modules niet zijn aangepast. Dat vraagt om zorgvuldige regressietests en een releaseproces dat rekening houdt met de samenhang tussen onderdelen. Tegelijkertijd is de route van code naar productie vaak overzichtelijk: één build, één artefact en een beperkt aantal runtimecomponenten.

Microservices kunnen onafhankelijk worden gedeployed, maar alleen als die onafhankelijkheid ook technisch en organisatorisch is ingericht. Elke service heeft eigen builds, configuratie, infrastructuur en monitoring nodig. Versies van services moeten gedurende releases met elkaar kunnen samenwerken; anders kan een deployment die op zichzelf geslaagd is, toch een ketenbrede storing veroorzaken. Compatibele API-wijzigingen, feature flags en gecontroleerde uitrolvormen zoals canary releases worden dan belangrijk.

Een veelvoorkomende misrekening is om het aantal deploybare componenten te verwarren met deploymentvrijheid. Als services bij iedere release tegelijk moeten veranderen, bestaat de onafhankelijkheid vooral op papier. De relevante maatstaf is daarom niet hoeveel services er zijn, maar hoe vaak een team een wijziging veilig kan uitrollen zonder andere teams of componenten te blokkeren.

Gegevensbeheer en consistentie tussen services

Een monoliet kan verschillende domeinen in één database onderbrengen. Dat maakt transacties over meerdere onderdelen relatief eenvoudig en biedt ruimte voor constraints die de integriteit van gegevens afdwingen. De keerzijde ontstaat wanneer modules elkaars tabellen rechtstreeks gebruiken. Een wijziging in het gegevensmodel kan dan onverwachte gevolgen hebben buiten de module waarvoor ze bedoeld was, waardoor interne afhankelijkheden steeds moeilijker te veranderen zijn.

Bij microservices hoort gegevensbezit in principe bij een service. Andere services vragen gegevens op via een API of ontvangen gebeurtenissen, in plaats van rechtstreeks tabellen te lezen. Dat beperkt koppeling, maar maakt gegevensbeheer complexer. Een proces dat in één database met een transactie kan worden uitgevoerd, kan over services heen uit meerdere stappen bestaan. Als een vervolgstap faalt, zijn compensatieacties, herhaalbare berichten en duidelijke processtatus nodig.

Die inrichting introduceert vaak eventual consistency: gegevens zijn niet op ieder moment overal bijgewerkt. Producten en processen moeten daarmee om kunnen gaan. Een scherm kan bijvoorbeeld tijdelijk een oudere status tonen, of een rapportage moet wachten op gebeurtenissen uit meerdere bronnen. Een aparte database per service voorkomt op zichzelf geen duplicatie of inconsistentie. Het vraagt om expliciet eigenaarschap, afspraken over brongegevens en controle op berichten die vertraagd, dubbel of in een andere volgorde aankomen.

Foutisolatie en storingen die zich verspreiden

Een monolithische applicatie draait vaak als één proces of als meerdere identieke instanties van dezelfde applicatie. Een fout in één onderdeel kan daardoor beschikbare resources van andere onderdelen opslokken. Een langdurige databasequery, geheugenlek of onverwachte piek kan het hele proces beïnvloeden. Interne aanroepen zijn daarentegen meestal eenvoudiger te volgen dan netwerkverkeer: ze voegen geen time-outs, verbindingsproblemen of onbereikbare tussenlagen toe.

Microservices creëren grenzen waarbinnen een storing geïsoleerd kan blijven, maar die isolatie ontstaat niet automatisch door code op te splitsen. Een service die wacht op een trage afhankelijkheid kan bijvoorbeeld alle beschikbare verbindingen bezet houden. Wanneer meerdere services elkaar synchroon aanroepen, kan één probleem zich door de keten verspreiden. Time-outs, circuit breakers, begrensde wachtrijen en limieten op gelijktijdige verzoeken helpen zulke kettingreacties te beperken. Ze moeten wel passen bij het gedrag van de applicatie; een circuit breaker kan bijvoorbeeld tijdelijk functionaliteit uitschakelen.

Foutisolatie heeft ook een operationele kant. Teams moeten onderscheid kunnen maken tussen een storing in de service zelf en een probleem bij een afhankelijkheid. Dat vereist consistente logs, metrics en traces met voldoende context. Zonder die signalen kan een verdeeld systeem juist moeilijker te diagnosticeren zijn dan een monoliet. De architectuur bepaalt de mogelijke grenzen; configuratie, capaciteitsbeheer en foutafhandeling bepalen of die grenzen in productie werkelijk effect hebben.

Schaalbaarheid per onderdeel en de kosten van distributie

Een veelgenoemd voordeel van microservices is dat onderdelen afzonderlijk opgeschaald kunnen worden. Dat is nuttig wanneer één domein aantoonbaar meer capaciteit vraagt dan de rest, bijvoorbeeld een zoekfunctie met sterk wisselende belasting. De service kan dan meer instanties krijgen zonder ook alle andere onderdelen mee te schalen. Daarvoor moeten belasting, resourcegebruik en afhankelijkheden wel per service inzichtelijk zijn. Anders is onduidelijk waar extra capaciteit nodig is en welke component de werkelijke begrenzing vormt.

Een monoliet kan eveneens horizontaal worden opgeschaald door meerdere applicatie-instanties te draaien. Voor veel workloads is dat een werkbaar model, zolang sessies, bestanden en andere gedeelde toestand niet aan één instantie vastzitten. De database kan daarbij de beperkende factor worden, maar dat probleem verdwijnt niet door de applicatie in services te verdelen. Een netwerk van services kan de database zelfs extra belasten wanneer dezelfde gegevens herhaaldelijk worden opgevraagd of wanneer één gebruikersactie veel interne verzoeken veroorzaakt.

Microservices brengen bovendien infrastructuurverkeer, service discovery, beveiligingsbeleid en resourceverbruik per component mee. De schaalbaarheid moet daarom worden afgewogen tegen de extra operationele onderdelen. Splitsing is vooral relevant wanneer verschillende domeinen werkelijk verschillende schaalprofielen, beschikbaarheidseisen of releasebehoeften hebben. Een theoretisch kleinere service die niet los kan worden opgeschaald of aangepast, levert weinig praktisch voordeel op.

Onderhoudbaarheid en de operationele oppervlakte

Een monoliet is niet per definitie één ongedifferentieerde codebasis. Met duidelijke modules, beperkte afhankelijkheden en expliciete interfaces kan een monolithische applicatie intern sterke grenzen hebben. Die structuur maakt het mogelijk wijzigingen lokaal te houden zonder meteen netwerkcommunicatie en een aparte infrastructuurlaag te introduceren. Wanneer grenzen ontbreken, kan de codebasis echter uitgroeien tot een systeem waarin iedere wijziging meerdere domeinen raakt en teams elkaars interne implementaties gebruiken.

Microservices verplaatsen een deel van die complexiteit naar de omgeving van de applicatie. Naast de code moeten services worden gebouwd, uitgerold, beveiligd en bewaakt. Versiebeheer van API’s, secrets, certificaten, dashboards, incidentprocedures en lokale ontwikkelomgevingen vormen onderdeel van het dagelijks onderhoud. Een service die weinig functionaliteit bevat, kan toch een aanzienlijke beheerlast veroorzaken als ze een eigen pipeline en runtime nodig heeft.

Ook testen verandert. In een monoliet kunnen integratietests meerdere modules in één proces controleren, hoewel een grote testset traag kan worden. Bij services zijn contracttests nuttig om afspraken tussen producenten en afnemers te bewaken, maar ze vervangen niet alle ketentests. Een wijziging kan lokaal correct zijn en alsnog falen door configuratie, een afwijkende API-versie of gedrag van een externe afhankelijkheid. Onderhoudbaarheid hangt dus af van de totale wijzigingslast, niet alleen van de omvang van afzonderlijke codebases.

Teamafhankelijkheden en eigenaarschap van services

De structuur van software beïnvloedt hoe teams werk kunnen verdelen. Een monoliet kan samenwerking vereenvoudigen wanneer één team verantwoordelijk is voor het geheel en modules goed afgebakend zijn. Bij meerdere teams ontstaan sneller gedeelde bestanden, conflicterende wijzigingen of afstemming over releases. Dat is niet alleen een technisch probleem: onduidelijk eigenaarschap maakt het lastig om te bepalen wie een module mag aanpassen, wie incidenten oppakt en wie verantwoordelijk is voor de kwaliteit ervan.

Microservices kunnen teams een eigen domein en deploymentverantwoordelijkheid geven. Dat werkt alleen wanneer de servicegrens aansluit op een betekenisvolle bedrijfsfunctie en een team de service daadwerkelijk zelfstandig kan beheren. Als een wijziging in één gebruikersproces vijf teams vereist, is de architectuur wel verdeeld, maar blijft het werk sterk onderling afhankelijk. Ook gedeelde platformteams kunnen een knelpunt worden wanneer elk team voor deployments, logging of infrastructuur op handmatige ondersteuning moet wachten.

Heldere eigenaarschap betekent meer dan een teamnaam in een catalogus. Het omvat verantwoordelijkheid voor API-contracten, gegevenskwaliteit, beveiligingsupdates, capaciteitsgedrag en incidentanalyse. Teams hebben daarbij gemeenschappelijke standaarden nodig voor observability en deployment, zonder dat elke service dezelfde interne technologie moet gebruiken. Te veel standaardisatie beperkt passende oplossingen; te veel vrijheid maakt beheer en kennisdeling moeilijker. De praktische keuze is welke zaken teams zelfstandig mogen bepalen en welke organisatiebrede afspraken nodig zijn.

Wanneer een monoliet opsplitsen in microservices

Een monoliet opsplitsen is het overwegen waard wanneer concrete beperkingen terugkeren: releases lopen vast doordat domeinen elkaar beïnvloeden, verschillende onderdelen hebben sterk uiteenlopende beschikbaarheidseisen, of één functie vraagt structureel om een andere schaalstrategie. Ook kan een domein zo helder afgebakend zijn dat een team het onafhankelijk kan ontwikkelen en beheren. Zulke signalen zijn sterker dan algemene voorkeuren voor een bepaalde architectuurstijl, omdat ze een aantoonbare frictie in het bestaande systeem aanwijzen.

Voordat code wordt verplaatst, is het nodig de huidige afhankelijkheden zichtbaar te maken. Tabellen die door meerdere modules worden aangepast, gedeelde bibliotheken met domeinlogica en impliciete aannames over transacties vormen vaak de lastigste grenzen. Een service uit een module halen zonder die afhankelijkheden te begrijpen, verplaatst de koppeling naar API’s, gebeurtenissen of gedeelde databronnen. De complexiteit verandert dan van vorm, maar neemt niet noodzakelijk af.

Een stapsgewijze aanpak kan risico’s beheersbaar maken. Een goed begrensde functionaliteit kan eerst achter een interface worden geplaatst, zodat implementatie en aanroep losser van elkaar komen te staan. Daarna kan worden beoordeeld of een afzonderlijke service waarde toevoegt. Daarbij horen vragen over gegevensbezit, foutgedrag, deployment en teamverantwoordelijkheid. Als die antwoorden ontbreken, kan het versterken van interne modulaire grenzen een betere stap zijn dan directe distributie over meerdere processen.

Observability en testen in een verdeeld systeem

Bij een monoliet kan een request vaak binnen één proces worden gevolgd, al vraagt ook daar productiegedrag om goede logging en metrics. In een microservicesysteem loopt een gebruikersactie doorgaans door meerdere processen en mogelijk via een berichtenplatform. Zonder correlatie-ID’s en distributed tracing is het lastig te reconstrueren waar vertraging ontstond of waarom een proces onvolledig bleef. Logs moeten voldoende context bevatten om een gebeurtenis te koppelen aan een verzoek, maar mogen geen gevoelige gegevens onnodig vastleggen.

Metrics maken operationele grenzen meetbaar. Denk aan responstijdpercentielen, foutpercentages, wachtrijlengte, verzadiging van verbindingen en het aantal retries. Alleen beschikbaarheid per service meten geeft geen volledig beeld: alle services kunnen technisch bereikbaar zijn terwijl een gebruikersproces faalt door een onjuiste volgorde van gebeurtenissen. Daarom zijn signalen op zowel componentniveau als op het niveau van belangrijke bedrijfsprocessen nodig. Alerting moet bovendien onderscheid maken tussen een symptoom en een onderliggende oorzaak.

Teststrategieën moeten dezelfde grenzen weerspiegelen. Unit- en moduletests controleren lokale regels, contracttests bewaken API- en berichtafspraken en integratietests toetsen kritieke combinaties. End-to-endtests blijven nuttig voor representatieve gebruikersstromen, maar zijn kwetsbaar wanneer ze te veel interne details vastleggen. Bij asynchrone verwerking moeten tests ook dubbele berichten, vertragingen en herverwerking meenemen. Dat maakt zichtbaar of een service idempotent is en of herstelgedrag klopt wanneer onderdelen tijdelijk niet beschikbaar zijn.

Veelgestelde vragen

Wat is een modulaire monoliet en wanneer is die een goede keuze?

Een modulaire monoliet is één applicatie die intern is opgebouwd uit duidelijk afgebakende modules met beperkte, expliciete afhankelijkheden. Het is een goede keuze wanneer je domeinen gescheiden wilt houden, maar nog geen overtuigende reden hebt om ze als afzonderlijke services uit te voeren. Je behoudt één deployment en vermijdt een deel van de netwerk- en beheercomplexiteit van microservices. Deze aanpak kan ook een praktische tussenstap zijn: sterke modulegrenzen maken het later makkelijker om een onderdeel eventueel zelfstandig uit te nemen.

Kunnen een monoliet en microservices naast elkaar bestaan?

Ja, een monoliet en microservices kunnen naast elkaar bestaan in één systeem. Een organisatie kan bijvoorbeeld nieuwe of duidelijk afgebakende functionaliteit als service ontwikkelen, terwijl bestaande onderdelen in de monoliet blijven. Dit wordt vaak een hybride architectuur genoemd. Spreek wel af waar gegevens eigendom zijn en hoe onderdelen met elkaar communiceren; anders ontstaan onduidelijke verantwoordelijkheden en extra koppelingen. Beoordeel per onderdeel of de zelfstandige deployment of schaalbaarheid de extra operationele lasten rechtvaardigt, in plaats van alles volgens één patroon te bouwen.

Hoe migreer je een monoliet naar microservices zonder een big bang?

Je migreert een monoliet zonder big bang door één afgebakende functionaliteit tegelijk los te maken en de bestaande applicatie voorlopig te laten draaien. Breng eerst in kaart welke code, gegevens en processen die functionaliteit gebruikt. Plaats vervolgens een duidelijke interface tussen de functie en de rest van de applicatie, zodat verkeer gecontroleerd naar de nieuwe service kan worden geleid. Vergelijk het gedrag van beide routes, houd een terugvalmogelijkheid beschikbaar en verwijder oude code pas wanneer de nieuwe route betrouwbaar werkt.

Hoe bereken je de totale kosten van microservices?

Bereken de totale kosten van microservices door niet alleen infrastructuur, maar ook bouw, beheer en dagelijkse samenwerking mee te nemen. Maak een schatting van de extra pipelines, runtimeomgevingen, monitoring, beveiligingsvoorzieningen en ondersteuning die elke service nodig heeft. Tel daar de tijd bij op voor incidenten, ketentests, API-beheer en afstemming tussen teams. Vergelijk die lasten met de concrete besparing of opbrengst, zoals minder releaseblokkades of gerichtere capaciteit. Gebruik waar mogelijk bestaande cijfers uit het huidige systeem en toets aannames met een beperkte proef.

Hoe ontwikkel en test je microservices lokaal op een laptop?

Je ontwikkelt microservices lokaal door ontwikkelaars een reproduceerbare omgeving te geven waarin de benodigde services en afhankelijkheden eenvoudig te starten zijn. Dat kan met containers, testvervangingen voor externe systemen of een gedeelde ontwikkelomgeving; de beste keuze hangt af van de benodigde middelen en de afhankelijkheden tussen services. Leg configuratie en startinstructies vast in de repository en gebruik veilige testgegevens. Test lokaal vooral het gedrag van de service en haar contracten, en laat geautomatiseerde omgevingen daarnaast controleren of meerdere services samen correct werken.

Portret van Maarten

Maarten

Freelance developer in Nijmegen

Even kennismaken?

Vertel kort wat er speelt. Dan hoor je wat er kan, wat ik anders zou doen en waar AI bij jou wél en niet iets toevoegt. Vrijblijvend.

[email protected]
Het kantoor in Nijmegen
© 2026 maarten.online Sitemap Privacy Algemene voorwaarden
Het kantoor in Nijmegen

Maarten.

Freelance developer in Nijmegen. Liever direct contact? Dat kan ook.

Kennismaken

Laat je gegevens achter, dan kijken we of het klikt. Vrijblijvend en zonder verkooppraat.

Maarten

Stuur een bericht via WhatsApp

Hoi! Waar kan ik je mee helpen?

nu