Gå til indhold

Monolit vs microservices: hvorfor de fleste projekter bør starte som monolit

Monolit vs microservices uden hype: hvad opdelingen koster, hvorfor små teams bør starte med en monolit, og hvornår det betaler sig at dele systemet op.

Af

Freelance full-stack udvikler

Udgivet
Læsetid
11 min.
Indhold i indlægget9

Monolit vs microservices er valget mellem at bygge dit system som én samlet applikation eller som mange små tjenester der kører hver for sig og taler sammen over netværket. Til langt de fleste projekter med et lille udviklingshold er det rigtige svar en monolit fordi den er hurtigere at bygge, billigere at drive og lettere at ændre. Microservices løser primært et organisatorisk problem, nemlig at mange teams skal kunne arbejde og udgive uafhængigt af hinanden, og det problem har de færreste virksomheder.

Jeg anbefaler selv næsten altid at starte med én samlet applikation, så læs med det forbehold, men jeg er lige så konkret om hvornår en opdeling er det rigtige valg.

Den korte version

Monolit og microservices sammenlignet
MonolitMicroservices
Hvad det erÉn applikation og én kodebase, typisk med én databaseMange små tjenester med hver sin kode, udgivelse og ofte egen database
Passer bedst tilNye produkter, små og mellemstore teams, de fleste webappsStore organisationer med mange teams og dele med meget forskellig belastning
Tid til første versionKortest, du kan gå direkte til funktionerneLængere fordi infrastrukturen skal på plads først
DriftÉn applikation at overvåge, opdatere og sikkerhedskopiereMange tjenester, netværk mellem dem, køer og central logning
FejlfindingÉt sted at lede og én logEn fejl kan gå på tværs af flere tjenester
SkaleringHele applikationen skaleres samlet, og det rækker oftestHver del kan skaleres for sig
Ændringer på tværsÉn ændring og én udgivelseKræver koordinering mellem tjenester og deres API'er
Største risikoBliver uoverskuelig hvis den ikke holdes i ordenHøje faste omkostninger og et spredt rod hvis grænserne er trukket forkert

Min beslutningsregel er enkel: Start med en monolit, og hold den velorganiseret indefra. Del kun en bestemt del ud når du kan pege på et problem som opdelingen løser, og som ikke kan løses billigere inde i monolitten. "Det skal kunne skalere" er ikke et konkret problem. "Billedbehandlingen gør hele systemet langsomt hver mandag morgen" er.

Vil du se hvor arkitekturen passer ind i resten af projektet, har jeg skrevet en guide til hele forløbet i et softwareprojekt.

Hvad er forskellen på en monolit og microservices?

En monolit er én applikation med én kodebase. Brugerstyring, ordrer, fakturering og rapporter ligger i det samme program, deler typisk den samme database og sættes i drift samlet. Når du retter noget, udgiver du hele applikationen på én gang. Ordet lyder gammeldags, men sådan er de fleste webapps bygget, også mange store.

Microservices deler systemet op i mange små, selvstændige tjenester. Én tjeneste håndterer brugere, en anden ordrer, en tredje betalinger. Hver tjeneste har sin egen kode, sættes i drift for sig og har ofte sin egen database. Tjenesterne taler sammen via API'er (faste grænseflader som programmer bruger til at udveksle data) eller beskedkøer.

Tænk på det som ét værksted mod mange små værksteder i hver sin bygning. Med mange bygninger kan hvert værksted indrette sig som det vil, men alt der skal fra det ene til det andet, skal pakkes og sendes. Og en gang imellem bliver pakken væk.

Et kald inde i en monolit lykkes stort set altid. Et kald over netværket kan være langsomt, fejle eller lykkes halvt, og systemet skal være bygget til at tåle det. Det er dér de ekstra omkostninger ved microservices kommer fra.

Hvorfor de fleste projekter bør starte som monolit

Det tungeste argument er at du ikke kender de rigtige grænser på dag ét. For at dele et system op i tjenester skal du vide hvilke dele der hører sammen, og hvilke der kan leve hver for sig. Den viden får du først når rigtige brugere har brugt systemet et stykke tid. Gætter du forkert i en monolit, flytter du noget kode. Gætter du forkert med microservices, skal du ændre API'er, flytte data mellem databaser og koordinere udgivelser på tværs af tjenester.

Martin Fowler, en kendt stemme inden for softwarearkitektur, skrev i 2015 at næsten alle de succesfulde microservice-projekter han kendte til, startede som en monolit der blev for stor. Omvendt var stort set alle de systemer han havde hørt om, der var bygget som microservices fra bunden, endt i alvorlige problemer.

Det andet argument handler om organisation. Conways lov fra 1968 siger at et system kommer til at afspejle den måde organisationen bag det kommunikerer på. Microservices passer til virksomheder med mange teams, hvor hvert team ejer sin del og vil kunne udgive uden at vente på de andre. Er I to, fem eller otte udviklere, er I ét team. Så får I alle omkostningerne ved opdelingen og ingen af gevinsterne.

Min tommelfingerregel: Så længe alle der udvikler på produktet, kan være med i det samme ugentlige møde, er der sjældent grund til mere end én applikation.

Det tredje argument er tempo. Et nyt produkt skal først finde ud af om nogen vil bruge det, og hver uge der går med infrastruktur, er en uge hvor du ikke lærer noget om dine kunder.

Det koster microservices i praksis

Forslag om microservices bliver tit præsenteret som det moderne valg, uden at regningen følger med. Her er det du betaler for, også før systemet har en eneste bruger:

  • Hver tjeneste skal bygges, testes, udgives, overvåges og opdateres. Ti tjenester er ti gange det arbejde, plus det der binder dem sammen.
  • I en monolit kan en ordre og dens betaling gemmes i samme transaktion, så enten bliver begge gemt, eller også bliver ingen af dem. Ligger de i hver sin tjeneste, skal du selv håndtere tilfældet hvor betalingen gik igennem, men ordren aldrig blev oprettet.
  • En fejl hos en kunde kan gå gennem fire tjenester. Uden central logning og sporing af hvert kald leder du i blinde.
  • Ændrer én tjeneste sit API, skal alle de tjenester der bruger det, følge med. Ellers går noget i stykker i drift.

Det er ikke teori. Twilio Segment beskrev i 2018 hvordan de havde over 140 små tjenester til at sende data videre til andre systemer, og hvordan arbejdet med driften voksede for hver ny tjeneste. Til sidst samlede de det hele i én tjeneste igen og fik et system der var lettere at udvikle på. Og Segment havde vel at mærke rigtig meget trafik.

For et mindre projekt betyder det flere timer til det samme resultat, en større hostingregning og færre udviklere der kan overtage systemet hvis den oprindelige leverandør forsvinder.

Den modulære monolit: mellemvejen de fleste bør vælge

En monolit behøver ikke være én stor bunke kode hvor alt hænger sammen med alt. Man taler om en modulær monolit når applikationen er delt op i tydelige moduler efter forretningsområder, fx kunder, ordrer, fakturering og lager, men stadig sættes i drift som én enhed.

Reglerne er lette at forklare, men kræver disciplin at holde:

  • Hvert modul har sit eget ansvar og sine egne tabeller i databasen.
  • Moduler taler sammen gennem få, tydelige indgange og roder ikke i hinandens data.
  • Tunge opgaver som PDF-generering, billedbehandling og kald til eksterne systemer kører som baggrundsjob via en kø, så de ikke gør resten af systemet langsomt.

Shopify valgte netop den vej. I stedet for at dele deres store Ruby on Rails-applikation op i microservices omorganiserede de den i moduler efter forretningsområder og byggede værktøjer der håndhæver grænserne. Kan det holde for en virksomhed i Shopifys størrelse, kan det også holde for din kundeportal.

Du får det meste af overskueligheden fra microservices uden netværkskald og data spredt over flere databaser. Og skal et modul en dag skilles ud, er grænsen allerede trukket. Laravel har køer, cache og planlagte job indbygget, så meget af det man ellers ville bygge separate tjenester til, kan løses inde i applikationen. Det er en af grundene til at Laravel er mit standardvalg til webapps.

En monolit kan også skaleres. Du kan køre flere kopier af den samme applikation bag en load balancer (en server der fordeler trafikken), give databasen flere ressourcer og cache det der bliver læst ofte. Er systemet langsomt, skyldes det sjældent arkitekturen. Oftest er det databaseforespørgsler, manglende cache eller tunge job der kører mens brugeren venter.

Hvornår hver løsning ikke passer

Hvornår en monolit ikke passer

  • Når mange teams arbejder på det samme produkt og konstant står i vejen for hinanden ved udgivelser. Så er det organisationen der er vokset ud af én kodebase.
  • Når én del har en helt anden belastning end resten, fx videokonvertering eller tunge beregninger, mens resten af systemet har få brugere ad gangen.
  • Når en del skal holdes helt adskilt af hensyn til sikkerhed eller lovkrav, fx betalingsdata.
  • Når en del skal skrives i et andet programmeringssprog end resten, fx en model til billedgenkendelse i Python ved siden af en webapp i PHP.

Ingen af punkterne kræver at hele systemet bliver til microservices. Ofte er svaret én eller to selvstændige tjenester ved siden af monolitten. Skal du designe en hel platform af tjenester til mange teams, har du mere brug for en intern arkitekt eller et platformsteam end for en freelancer som mig.

Der er også en situation der ligner, men ikke er det: en monolit der aldrig er blevet holdt i orden, hvor hver ændring føles farlig. Her er løsningen sjældent at skære den i stykker, for rod i én kodebase bliver til rod i ti. Start med at kortlægge den tekniske gæld og hvad den koster dig, og ryd op før du overvejer at dele noget ud.

Hvornår microservices ikke passer

  • Når produktet er nyt, og du stadig lærer hvad kunderne vil have.
  • Når udviklingsholdet er lille, fx en freelancer, et lille bureau eller et internt team på få personer.
  • Når argumentet er at systemet skal kunne skalere, uden at nogen kan sige hvad der skal skaleres, og hvor meget.
  • Når budgettet ikke rækker til den løbende drift: overvågning, logning og en person der forstår hele opsætningen.
  • Når ingen hos dig kan vurdere arkitekturen, og du derfor ikke kan se om den er valgt for dit projekts skyld eller for udviklerens.

Sådan deler du en monolit op når tiden kommer

Står du med et konkret problem som en opdeling løser, så skil én del ud ad gangen mens resten kører videre som før. Metoden kaldes ofte strangler fig-mønsteret, opkaldt efter kvælerfigen, en figenart der langsomt vokser op omkring et værtstræ og til sidst erstatter det.

  1. Find den del med det tydeligste problem og den klareste grænse. Typisk er det noget med egen belastning eller egne data, fx billedbehandling eller en integration.
  2. Gør grænsen tydelig inde i monolitten først, så resten af koden kun bruger delen gennem én indgang.
  3. Flyt delen ud i en selvstændig tjeneste bag den samme indgang. Resten af systemet mærker ikke forskel.
  4. Sæt overvågning og logning op for den nye tjeneste før den får rigtig trafik.
  5. Mål om problemet faktisk er løst før du overvejer at skille den næste del ud.

De fleste stopper efter en eller to dele fordi det var præcis dét problem der skulle løses. Det er et godt tegn og ikke et halvfærdigt projekt. Er det din SaaS der vokser, har jeg skrevet mere om hvad der typisk skal til når en SaaS skal skaleres.

Næste skridt

Har du fået foreslået microservices til et nyt projekt, så stil tre spørgsmål før du siger ja:

  • Hvilket konkret problem løser opdelingen, som en velorganiseret monolit ikke kan løse?
  • Hvad koster driften om måneden, og hvem skal stå for den når projektet er afleveret?
  • Hvor mange udviklere på markedet kan overtage systemet hvis den nuværende leverandør stopper?

Er svarene vage, er arkitekturen sandsynligvis valgt fordi den er spændende, ikke fordi dit projekt har brug for den. Står du derimod med et eksisterende system der driller, er tegnene på en sund kodebase et bedre sted at starte end en diskussion om arkitektur.

Mit udgangspunkt til nye webapps og platforme er en velstruktureret monolit med plads til at vokse, og jeg siger det ærligt hvis dit projekt har brug for noget andet. Du kan se hvordan jeg griber den slags projekter an på siden om webapps og platforme.

Ofte stillede spørgsmål

Er microservices hurtigere end en monolit?

Nej, ikke i sig selv. Et kald over netværket er langsommere end et kald inde i samme program, så en simpel handling kan faktisk blive langsommere når den skal gennem flere tjenester. Fordelen ved microservices er at du kan give en enkelt tung del flere ressourcer uden at skalere resten.

Er serverless det samme som microservices?

Nej, men de har mange af de samme udfordringer. Serverless betyder at koden kører som små funktioner som hostingudbyderen starter efter behov, og at du betaler for hver kørsel. Det kan være praktisk til enkeltstående opgaver, fx at behandle uploadede billeder. Bygger du hele systemet af mange små funktioner, får du til gengæld de samme problemer med fejlfinding, data på tværs og koordinering.

Hvad er en distribueret monolit?

En distribueret monolit er et system der er delt op i tjenester, men hvor tjenesterne er så afhængige af hinanden at de skal ændres og udgives samtidig. Du betaler for netværk, flere databaser og en kompliceret drift, men får ikke den uafhængighed som var hele formålet. Det sker typisk når grænserne bliver trukket før nogen kender systemet godt nok.

Gør en monolit mig afhængig af én udvikler?

Nej, snarere tværtimod. En velstruktureret monolit i et udbredt framework er det lettest at overtage fordi mange udviklere kender opbygningen. Afhængighed skyldes manglende dokumentation, manglende tests og kode der ikke ligger hos dig. Kræv derfor at koden ligger i dit eget repository (kodearkiv) fra første dag.