3.0 Introduktion til del 3: Systemer
Arkitektur er det sæt beslutninger, der er dyre at gøre om. Hvordan deler du et system i dele? Hvordan taler delene med hinanden? Hvordan modelleres dataene? Og hvordan opfører det hele sig under belastning og under fejl? Del 3 handler om at træffe de valg med vilje. I et lille team kan arkitektur bo i nogle få hoveder og udvikle sig undervejs. I en stor organisation (hundredvis af ingeniører, dusinvis af teams, systemer der vil overleve karrieren hos dem, der bygger dem) bliver arkitektur det, der holder alle koordinerede. Når den er klar, bevæger teams sig uafhængigt uden at støde sammen. Når den er vag, bliver hver afhængighed på tværs af teams en forhandling, og hver hændelse et arkæologiprojekt.
Indsatsen er højest i virksomheder og det offentlige. En skattemotor, en ydelsesplatform, en national sundhedsjournal eller en banks kernehovedbog er langlivet, stærkt reguleret, delt på tværs af afdelinger og ansvarlig over for offentligheden. De beslutninger, du træffer i dag om kobling, dataejerskab og kvalitetsegenskaber, vil begrænse, hvad der er muligt i et årti eller mere. Tilsynsmyndigheder og revisorer forventer i stigende grad dokumenteret, forsvarlig arkitektur, med reel dokumentation for, at pålidelighed, sikkerhed, privatliv og holdbarhed var designet ind og ikke skruet på bagefter. De fejl, der gør overskrifter, er arkitektoniske fejl: portaler der bøjer sig på lanceringsdagen, indberetningssystemer der får timeout ved fristen, migreringer der taber eller dobbelttæller poster.
Denne del begynder med de varige grundlag, bevæger sig gennem konkrete strukturelle valg, derefter ind i realiteterne ved distribution, data og skala, og slutter med det sværeste problem, de fleste store organisationer faktisk står over for: at modernisere de systemer, de allerede kører. Den røde tråd: god arkitektur er en række bevidste afvejninger, ikke en modeagtig standard.
Kapitler i denne del
3.1 Grundlæggende arkitektur. De varige værktøjer, der overlever teknologimoder: kvalitetsegenskaber (“-iliteterne”), arkitektonisk signifikante krav, fitness functions (automatiserede tests, der vogter en valgt arkitektonisk kvalitet) og evolutionær arkitektur, let dokumentation med C4-modellen (indlejrede arkitekturdiagrammer i fire zoomniveauer) og arc42 (en skabelon til arkitekturdokumentation) samt struktureret afvejningsanalyse.
3.2 Arkitekturstilarter og mønstre. En oversigt over de store systemformer, fra monolit over mikrotjenester, hændelsesdrevne arkitekturer med CQRS (Command Query Responsibility Segregation, der adskiller læsemodeller fra skrivemodeller) og event sourcing (at gemme tilstand som en kun-tilføjelses-log af hændelser), service mesh (et dedikeret infrastrukturlag til kommunikation mellem tjenester) og gateways, serverless og hexagonal og ren arkitektur, med vejledning i, hvornår hver passer, rammet ind af Conways lov (systemer har tendens til at spejle kommunikationsstrukturen i den organisation, der bygger dem).
3.3 Distribuerede systemer. De hårde sandheder, der dukker op i det øjeblik, du krydser en netværksgrænse (upålidelige netværk, delvis fejl, intet fælles ur), og de standardforsvar, der findes: konsistensræsonnement, idempotens (at gøre en operation sikker at gentage), genforsøg med backoff, circuit breakers (som stopper med at kalde en fejlende afhængighed), sagaer (sekvenser af lokale transaktioner med kompenserende fortrydelsestrin) og distribueret observerbarhed.
3.4 Dataarkitektur og lagring. Hvordan data modelleres, gemmes, holdes konsistente og serveres hurtigt i stor skala: de store lagringsparadigmer og hvornår man bruger hvert, polyglot persistence (at bruge flere specialiserede datalagre i ét system), skemaudvikling og migrering, caching og CDN’er (content delivery networks) samt transaktioner og samtidighed under belastning.
3.5 Skalerbarhed, ydeevne og robusthed. Tre adskilte kvaliteter designet ind frem for eftermonteret: horisontal og vertikal skalering, tilstandsløshed og sharding (at opdele data på tværs af maskiner efter en nøgle), belastningsfordeling og autoskalering, ydeevnebudgetter, robusthedsmønstre og kaosingeniørkunst samt katastrofegenopretning på tværs af regioner rammet ind af RTO (recovery time objective) og RPO (recovery point objective).
3.6 Modernisering af ældre systemer. De inkrementelle mønstre, der faktisk virker, strangler fig (at lade et nyt system vokse rundt om det gamle, til det gamle kan pensioneres) og branch-by-abstraction (at erstatte en komponent bag en grænseflade på udviklingens hovedlinje), plus risikovurdering af legacy, forvaltning af mainframe og COBOL (Common Business-Oriented Language), datamigrering og dobbeltkørsel, og hvordan man modstår fristelsen til den store omskrivning, der producerer feltets dyreste fiaskoer.
3.7 Softwarevedligeholdelse. Den dominerende fase i softwarens levetid: korrigerende, adaptiv, perfektiv og forebyggende vedligeholdelse, programforståelse og reengineering samt design til vedligeholdelighed, så langlivede systemer forbliver til at betale at ændre.
3.8 Interoperabilitet og åbne standarder. At designe systemer til at interoperere gennem åbne standarder frem for skræddersyede integrationer: teknisk, syntaktisk og semantisk interoperabilitet, domænestandarder som FHIR i sundhed og omkostningen ved proprietær indlåsning.
3.9 Systemteknik. At konstruere komplekse systemer fra ende til anden, ofte i kombination af software, hardware, mennesker og processer: livscyklus, kravallokering og sporbarhed, grænseflader, integration samt verifikation og validering.
3.10 Indlejrede systemer og realtidssystemer. Software til enheder under stramme begrænsninger: realtidsadfærd og determinisme, et RTOS eller bare-metal firmware, begrænset hukommelse og strøm, hardwareinteraktion og sikkerhedskritiske standarder.
3.11 Cloudarkitektur. At designe til skyen frem for at løfte-og-flytte et datacenter: servicemodeller og serverless, regioner og tilgængelighedszoner som fejldomæner, modellen med delt ansvar, afvejninger ved administrerede tjenester og indlåsning, multicloud og hybrid, når de fortjener deres kompleksitet, og omkostningsbevidst, velarkitekteret design.
3.12 Hændelsesdrevet arkitektur og beskedudveksling. At bygge systemer, der kommunikerer ved at producere og reagere på hændelser: køer versus holdbare strømme, koreografi versus orkestrering, event sourcing og CQRS, hvor de tjener deres brød, sagaer til distribuerede transaktioner, leveringsgarantier og idempotens samt de mønstre, der holder asynkrone flows pålidelige.
3.13 Netværk og konnektivitet. Det netværk, en applikationsingeniør faktisk har med at gøre: DNS, TCP og HTTP’s udvikling, TLS-terminering, belastningsfordeling og reverse proxies, indholdslevering og edge, tjenesteopdagelse og service mesh samt de timeouts, genforsøg og circuit breakers, der gør netværkets upålidelighed overlevelig.
3.14 Multitenancy og SaaS-arkitektur. At betjene mange kunder fra én softwareinstans uden at lade dem se eller sulte hinanden: spektret mellem isolation og effektivitet, datapartitionering, kvoter pr. tenant mod støjende naboer, tenantens livscyklus og omkostningsfordeling.
3.15 Caching og indholdslevering. At bytte lidt forældelse for store gevinster i latens, belastning og omkostning på tværs af cachehierarkiet (klient, edge og CDN, reverse proxy, applikation og datalager), mens cache-invalidering og beskyttelse mod stampede behandles som førsteklasses design.
3.16 API-gateways og service mesh. At håndtere nord-syd-trafik ved en API-gateway (routing, autentificering, hastighedsbegrænsning, sammensætning) og øst-vest-trafik gennem et service mesh (gensidig TLS, trafikflytning, genforsøg, observerbarhed) og at afgøre, hvornår et mesh fortjener sin kompleksitet.
3.17 Søgning og informationssøgning. At behandle søgning som et førsteklasses system, fra det inverterede indeks og relevansrangering til forespørgselsforståelse, facettering og vektor- og hybridsøgning, med reel relevansevaluering frem for gætteri.
Sådan hænger kapitlerne sammen
Kapitlerne bygger på hinanden i rækkefølge. Kapitel 3.1 giver dig ordforrådet (kvalitetsegenskaber og afvejningsanalyse), som hvert senere kapitel bruger; de “-iliteter”, det navngiver, er netop det, kapitel 3.4 og 3.5 gør konkrete. Kapitel 3.2 omsætter disse grundlag til strukturelle valg, og de mere distribuerede stilarter, det anbefaler (mikrotjenester, hændelsesdrevet, service mesh), bringer de omkostninger, som kapitel 3.3 lærer dig at håndtere. Kapitel 3.3, 3.4 og 3.5 læner sig tungt op ad hinanden: distribution tvinger de konsistens- og holdbarhedsbeslutninger frem, som dataarkitektur handler om, og sammen former distribution og data, hvilken skalerbarhed og robusthed du faktisk kan nå. Kapitel 3.6 lukker løkken, for de fleste store organisationer bygger ikke på et blankt ark: de udvikler registersystemer, der begrænser hvert valg, de tidligere kapitler beskriver.
Del 3 rækker også udad. Kapitel 3.2 trækker på designprincipperne og Domain-Driven Design fra kapitel 2.2 for at finde gode tjenestegrænser. De driftsdiscipliner, der holder disse systemer kørende, bor i del 9: site reliability engineering (kapitel 9.1) og observerbarhed (kapitel 9.2) er, hvor arkitektonisk robusthed bevises i produktion. De sikkerheds- og privatlivsegenskaber, tilsynsmyndigheder kræver, designes her, men detaljeres i del 4, og platforms- og leveringspraksis i del 8 afgør, om en arkitektur faktisk kan udgives og drives af mange teams på én gang.