Etikettarkiv: arkitektur

En arkitekturell insikt, del 2

Idag tänkte jag följa upp gårdagens inlägg med en vidareutveckling som också är inspirerad av boken jag just nu läser (Implementing Domain-Driven Design). Mitt förra inlägg illustrerade att vi behöver revidera vår syn på den traditionella layered architecture när vi använder oss av Dependency Injection (DI). Författaren i boken gör en tänkvärd om än aningen krystad övergång under sitt resonemang till en annan intressant arkitektur som är värd att prata om. Jag tänker här försöka mig på att ge en liknande förklaring men på mitt sätt.

Den vänstra sidan i Figur 1 nedan illustrerar den ändring av arkitekturen som mitt inlägg igår landade i. Anledningen till att vi ser Infrastructure layer överst är som vi behandlade igår att vi med hjälp av Dependency Inversion Principle (DIP) har vänt på beroenderiktningen. Författaren menar att en konsekvens av att vi använder oss av DIP är att vi hamnar i ett läge där vi inte behöver tänka på arkitekturen som en uppsättning lager. Hela applikationen är i och med DIP utformad att ha beroende mot abstraktioner (interfaces) som sedan kan implementeras på lite olika sätt. Om vi nu ändå lite krystat vill pressa in den tanken i en form av lagerarkitektur kan vi se resultatet till höger i Figur 1.

Figur 1
Figur 1: En krystad övergång

I ärlighetens namn är den högra delen av figuren inte särskilt intressant. Det borde väl ändå finnas ett bättre sätt att förhålla sig till denna nya insikt? Säg hej till Hexagonal architecture. Kärt barn har många namn; arkitekturen benämns även som Onion och Ports and Adapters. Jag plockade fram min konstnärliga sida och ritade upp ett exempel på hur denna arkitektur ser ut. I Figur 2 ser vi två hexagoner. Den inre innehåller själva applikationen och dess innehåll kan mer eller mindre likställas med det nedre lagret till höger i Figur 1.

Figure 2
Figur 2: Hexagonal architecture

Den yttre hexagonen är det som gör skäl för namnet Ports and Adapters. Här kan man i stort sett peta in hur många adaptrar som helst för att på så sätt kunna erbjuda olika former av kommunikation med systemet och olika implementationer av applikationens abstraktioner. Den vänstra delen av hexagonen ger exempel på två olika adaptrar för kommunikation med systemet. Vid anrop delegerar respektive adapter sina anrop till metoder i den inre hexagonen där domänmodellen lever. Den högra delen av hexagonen ger exempel på adaptrar som implementerar abstraktioner för persistering. Värt att understryka är att applikationen i den inre hexagonen inte har en aning om anropet kommer via REST eller huruvida resultatet av en operation persisteras i en minnesdatabas, detta avgör aktuell adapter.

Av Figur 2 går det att utläsa att avståndet mellan klientens anrop och själva applikationen är väldigt liten – det är bara en adapter emellan. Detta medför också flexibilitet i att det enkelt går att lägga till nya och byta ut adaptrar. Vad vi också kan se till höger i bilden är att adaptertänket även gäller för saker som persistering, ja alla infrastrukturella saker egentligen. Eftersom DDD inte främst handlar om arkitektur utan om lingvistik och domänmodeller känns arkitekturen för mig väldigt tilltalande just för DDD. Vi kan se att den inre hexagonen huserar domänmodellen och att den där kan leva i en skyddad verkstad.

Via den flexibilitet som arkitekturen erbjuder behöver vi inte lägga så mycket vikt vid teknologival. Var det en dålig idé att satsa på teknologi X? Ja kanske, vi petar in en ny adapter med teknologi Y, det kanske funkar bättre? I min hjärna ger en sådan flexibilitet mer utrymme till det som borde vara det viktigaste av allt: att vårda, förbättra och vidareutveckla domänmodellen.

En arkitekturell insikt

Den lilla tid jag har fått över de senaste dagarna har i stort sett gått till att läsa i den alldeles eminenta boken Implementing Domain-Driven Design. Boken bygger på sätt och vis vidare på Evans tidlösa bok genom att ta upp nya insikter som har framkommit samt hur DDD kan implementeras med modern teknologi. Den är en rejäl bok att sätta tänderna i och det vore att skryta om jag sa att jag har kommit halvvägs. Hittills har den bjudit på många handfasta tips på hur man ska förhålla sig till DDD rent praktiskt. Redan såhär långt har flera av mina frågetecken kring DDD rätats ut och fler lär det bli om boken fortsätter att leverera. Tidigare idag när jag bläddrade i ett kapitel om arkitekturer sprang jag på något som inte direkt är relaterat till DDD men som ändå var värdefullt för mig. Detta tänker jag skriva av mig om här och nu.

När jag sitter och hackar i mina hemmaprojekt brukar jag nog tänka på strukturen som en layered architecture (i brist på en vettig svensk benämning). I denna ”klassiker” ser man arkitekturellt på systemet i flera lager där varje lager endast känner till det lager som finns direkt under lagret i fråga (Figur 1). Alltså känner bara Domain layer i Figur 1 till Infrastructure layer. Nu brukar man inte vara så bokstavstrogen – som jag har förstått det är det vanligast att ett lager känner till samtliga underliggande lager. Denna form av layered architecture kallar man för relaxed.

Figur 1
Figur 1: Layered architecture

Samtidigt har jag som förmodligen alla andra Javautvecklare under många år ägnat mig åt Dependency Injection (DI), i mitt fall via Spring. Att utveckla mot abstraktioner när det gäller saker som till exempel persistering sitter så i märgen på mig att jag knappt inte skulle kunna tänka mig att utveckla på något annat vis. DI är ett av flera sätt att realisera något som kallas för Dependency Inversion Principle (DIP). Väldigt förenklat kan man säga att principen innebär att man vänder på beroenderiktningen, att högnivåkomponenter inte är beroende av infrastrukturella saker. Detta rimmar illa med vad vi ser i Figur 1; enligt den traditionella layered architecture känner alla lager till Infrastructure layer. I alla fall om man tänker på varianten relaxed.

Som boken föreslår behöver vi om vi praktiserar DIP revidera Figur 1 för att bilden ska stämma med verkligheten. Jag låter mig här bli skamlöst inspirerad och min version av denna reviderade layered architecture kan ses i Figur 2. Den enda egentliga skillnaden mot innan är att Infrastructure layer nu finns att finna överst. Rent grafiskt kan man tycka att skillnaden är liten men konsekvensen av förändringen är stor. Figuren säger att det är okej för infrastrukturkomponenter att känna till abstraktioner (dvs gränssnitt) i till exempel applikations- och domänlagret. I verkligheten är det ju så vi hela tiden har arbetat med DI, att vi implementerar gränssnitt och dessa implementationer injectas in i programmet med hjälp av ramverk såsom Spring. Men den verkligheten stämmer inte överens med Figur 1.

Figur 2
Figur 2: DIP layered architecture

För mig var det en ögonöppnare. Nu förstår jag varför jag har haft svårt att hitta en naturlig plats för infrastrukturklasser (tänk till exempel en HibernateAnimalRepository) i mina projekt. Den faktiska arkitekturen (Figur 2) matchade inte min mentala bild (Figur 1).