Hva ports and adapters løser
To spørsmål avgjør om arkitekturen holder: kan du teste forretningsreglene uten en database, og kan du bytte ut en avhengighet uten å røre reglene?
Domenelaget eier reglene og deklarerer portene det trenger, som RegistrationCaseRepository og VinValidator, uten å nevne PostgreSQL, MongoDB eller REST. Applikasjonslaget orkestrerer domenet og håndterer transaksjoner og sikkerhet. Infrastrukturlaget er det eneste som får kjenne konkret teknologi, og implementerer portene med Hibernate, JDBC, en REST-controller eller en GraphQL-resolver.
Eksempelet i artikkelen er et kjøretøyregister der entiteten RegistrationCase håndhever at en sak ikke kan godkjennes før VIN er validert. Regelen bor i domenet, og de fire adapterne bytter fritt uten at den røres.
ArchUnit gjør reglene kjørbare
Disiplin forvitrer. En controller som griper rett ned i et repository sniker seg inn i en travel uke, og da er grensen borte. ArchUnit lar deg skrive lagreglene som tester i stedet:
- en lagregel som verifiserer avhengighetsretningen: domenet avhenger av ingenting, applikasjonen kun av domenet, infrastrukturen av begge
- en renhetsregel som feiler hvis domenet importerer Spring eller et persistensrammeverk
- en allowlist for applikasjonslaget som kun tillater annotasjoner som
@Transactionalog@PreAuthorize
Testene kjører i CI, og et brudd stopper før merge. ArchUnit er et Java-verktøy, men tilsvarende finnes for Python med import-linter og for Node med dependency-cruiser eller eslint-plugin-boundaries.
Hvorfor dette betyr mer nå
En agent som skriver kode leser strukturen den ser og kopierer mønsteret. Ligger arkitekturgrensene i et wiki-dokument, finnes de ikke for agenten. Ligger de i en test som feiler, blir de en del av tilbakemeldingssløyfen den faktisk får. Det gjør arkitekturtester til noe av det billigste du kan gi et agentoppsett.
Kilde: Hexagonal Architecture: Keeping Business Logic Honest