Få klarhet i din monolit med begränsade sammanhang
kolla in videon av detta samtal från ElixirConf 2017 nedan
monolitiska applikationer är bra när du börjar bygga ditt företag, men när tiden går blir de svåra att underhålla. Dessa kodbaser, när de växer, blir lätt stora bollar av lera.
när man bygger stora applikationer i ramar som Rails, börjar de mycket convention-over-configuration designprinciperna som gjorde Rails en sådan glädje att använda komma i vägen när applikationen växer i omfattning. Du kan uppleva samma smärtor också om:
- Refactoring är svårt och tråkigt, eftersom metoder och klasser beror på för många andra klasser
- du har en ständigt växande lista över affärsobjekt som är svåra att hålla i ditt huvud. Faktum är att ingen verkar kunna förstå systemet som en sammanhängande helhet
- att ändra kod i ett område av koden leder till oväntade och oavsiktliga biverkningar i andra delar av koden, eftersom det är lätt att ringa till globala tjänster och objekt
i vår senaste chatt tillsammans diskuterade vi att utveckla ett allestädes närvarande språk tillsammans med affärsexperterna och ditt utvecklingsteam för att hjälpa ditt team att arbeta närmare tillsammans. Idag kommer vi att bygga vidare på det genom att introducera nya Domändrivna designverktyg (DDD). Sedan introducerar vi en ny mappstruktur för dina Rails-appar och förbereder dem för en framtid där din applikation är mindre kopplad och mer sammanhängande. Låt oss komma igång!
- Låt oss prata domäner
- avgränsade sammanhang i lösningsutrymmet
- the big idea: organisera Rails-kod i moduler av affärsdomän
- invertera mappstrukturer i en platt domänorienterad gruppering
- modulera klasser
- referera associerade modeller med fullständigt klassnamn
- Håll controllers uppdaterade om var de hittar sina nyligen modulerade vyer
- Vad fungerar bra med detta tillvägagångssätt?
- Vad har vi lärt oss?
Låt oss prata domäner
en nyckelprincip i DDD är att programvaran du bygger måste spegla (affärs) domänen för organisationen som bygger den. Därför måste vi göra några läxor för att förstå affärsdomänen för din programvara.
en domän är vad verksamheten gör och sammanhanget för hur den gör det.
Låt oss se över vårt Delorean-exempel från föregående inlägg. I det marknadsförs företaget som Uber för tidsresor. Således är dess “domän” (“vad den gör”) tidsresa Ridesharing. I domänen ingår också ” hur ” hur det gör det – genom att samarbeta förare som äger tidsresande Delorean-fordon med passagerare som vill göra tidsresor.
för att få mer nyanser i affärsdomänen introducerar DDD ett annat koncept, kallat underdomänen:
en underdomän representerar de mindre grupperna eller enheterna i verksamheten som samarbetar i det dagliga för att uppnå företagets mål.
Delorean är uppdelat i flera team inom företaget. Låt oss titta på två av dem, och se vad de är ansvariga för:
Trip Platform team | Finance Operations team | |
---|---|---|
uppdrag | designa och stödja systemen som leder resor och kopplar förare till passagerare | hantera systemen som involverar finansinstitut och kreditkortsbehandlare |
ansvar |
|
|
var och en av dessa två grupper animerar ett företagsansvar eller underdomän. Låt oss namnge dem Ridesharing erfarenhet och e-handel, respektive.
nu har vi en allmän illustration av verksamheten och två av dess enheter som hjälper den att fungera i det dagliga. Domänen och underdomänen är sätt att modellera problemutrymmet i ditt företag – och hur det fungerar för att uppfylla dessa roller. Chansen är stor att ditt företags organisationsschema kommer att återspegla underdomänerna för ditt företag. I den verkliga världen kan avgränsningarna vara mindre tydliga-lag kan vara ansvariga för flera överlappande underdomäner.
låt oss fylla i detta diagram med några fler underdomäner i Delorean-verksamheten:
- kundsupport underdomän: lösa kundsupport biljetter kommer in via e-post
- marknadsföring underdomän: hantera marknadsföring e-postkampanjer och marknadsföring kupongkoder
- identitet underdomän: hur systemet spårar varje användare och hans / hennes identifierande information
avgränsade sammanhang i lösningsutrymmet
detta diagram framför oss återspeglar nu företagets affärsmål, uppdelat i logiska enheter som (förhoppningsvis) uppnår sina mål i den verkliga världen. Nu ska vi lägga över mjukvarusystemen som uppnår dessa mål över detta diagram. Dessa mjukvarusystem beskrivs som begränsade sammanhang:
ett avgränsat sammanhang är ett system som uppfyller målen för verksamheten i den verkliga världen.
alla våra mjukvarusystem (som en webbtjänst eller webbapp) som fungerar som konkreta instanser i den verkliga världen betraktas som begränsade sammanhang.
tekniskt sett är det begränsade sammanhanget i DDD-speak en specifik gräns inom din domän som din ordlista från ditt allestädes närvarande språk bara kan gälla – tanken är att olika underdomäner kan ha konkurrerande eller motstridiga definitioner av termer. Det här inlägget kommer inte att utarbeta de språkliga nyanserna i det begränsade sammanhanget. För vidare läsning, se Martin Fowlers förklaring om begränsade sammanhang.
nu händer det så att på Delorean implementeras alla dessa underdomäner i ett system – en stor boll av Lerskenor monolit. Vi ritar en blå ruta runt underdomänerna vars funktioner implementeras av mjukvarusystemet. I det här fallet börjar vi med vår ovannämnda Rails monolith:
eftersom det är monoliten, gör det i princip allt-och så här äter det alla andra underdomäner i diagrammet.
låt oss inte glömma – vi har några andra mjukvarusystem som vi inte har modellerat här. Vad sägs om alla trevliga tredjepartsintegrationer som företaget använder? Det är också mjukvarusystem. Vi ritar dem som blå lådor.
förresten – det vi har ritat här är en Kontextkarta – ett diagram som blandar affärsmål och konkreta implementeringar av mjukvarusystem. Det är användbart för att bedöma läget för dina mjukvarusystems land och visualisera beroenden mellan lag.
nu är detta rimligt och rent, men vi lever i den verkliga världen, och verklig världsprogramvara kommer sällan ut och ser konsekvent och sammanhängande ut. Om du har byggt din Rails-app efter dess Out-of-the-box-konventioner saknar din app internt de grupperingar som krävs för att visualisera din app i dess beståndsdelar. I verkligheten ser DeLorean-kodbasen något mer ut så här:
poängen är-Rails verkställer inte några organisatoriska begränsningar på våra mjukvarusystem-vilket innebär att logiska affärsenheter (våra underdomäner) som föreslår frikopplade gränssnitt – inte materialiseras i koden, vilket leder till förvirring och ökad komplexitet när åren går.
the big idea: organisera Rails-kod i moduler av affärsdomän
även om dina Ruby-klasser i din ansökan förmodligen bor i det globala namnområdet, kan de enkelt plockas i moduler. Vårt mål är att skapa logiska grupper av domänkod som kan isoleras i fristående komponenter.
ett av målen med Domändrivna mönster är faktiskt att ha en en-till-en-mappning från en underdomän till ett avgränsat sammanhang.
OK, vad betyder detta? Låt oss gå in på några rekommendationer, tillsammans med exempel.
invertera mappstrukturer i en platt domänorienterad gruppering
du kanske kommer ihåg att följande Rails-konventioner leder oss till mapphierarkier som grupperar klasser efter Roller:
Låt oss flytta allt ut till en ny katalogstruktur: låt oss gruppera som funktionalitet efter domän istället. Vi börjar med en första variant, som jag kallar en platt domänorienterad gruppering.
modulera klasser
därefter vill du modulera klasserna från vad de var tidigare. Eftersom Förarklassen faller under Ridesharing-domänen lägger vi till den i en Ridesharing-modul:
du vill göra detta för varje klass du flyttar in i app/domains
platt katalogstruktur.
referera associerade modeller med fullständigt klassnamn
dessutom måste du ändra dina ActiveRecord-modellföreningar för att hänvisa till klassen med sin fulla modulerade sökväg:
Håll controllers uppdaterade om var de hittar sina nyligen modulerade vyer
du måste också infoga den här lilla biten för att låta Rutter från styrenheten veta var de ska leta efter vyerna:
här är det coola: du behöver inte flytta all din kod på en gång. Du kan välja en liten domän i din ansökan, det mest mogna området i din kod eller det område som du har den bästa förståelsen runt, och börja flytta sina problem till en enda domänmapp, samtidigt som du lämnar befintlig kod i vila tills den är redo att flytta.
nu har vi gjort några små steg för att uppnå arkitektonisk klarhet i vår ansökan. Om vi tittar nu har våra modulära mappstrukturer hjälpt oss att gruppera vår kod så:
under huven kan vår app se mer ut så här:
Vad fungerar bra med detta tillvägagångssätt?
- det finns mindre brus i varje filkatalog – genom att gruppera som filer efter domänspecificitet hittar vi en naturlig organisationspunkt
- de enheter som finns kvar i varje domänmapp är mycket sammanhängande-de tenderar sannolikt naturligtvis att kommunicera med varandra och visas naturligt med varandra
- enheter som inte hör ihop är nu separerade (lösare kopplade)
- om du har ingenjörskonst och team som arbetar längs underdomänansvar, dessa ingenjörer kan nu arbeta på ett mer strömlinjeformat, isolerat sätt. Looser koppling tillåter dessa lag att göra ändringar med tillförsikt att de inte kommer att införa regressioner eller slå samman konflikter tillbaka till kodbasen
- scenen är nu inställd på lång sikt för att börja flytta var och en av dessa domänmappar till en oberoende programvarutjänst (mer om det i ett framtida blogginlägg)
om du vill ha ytterligare vägledning i den här mappstrukturen har jag utvecklat en exempelapp som visar den här domänorienterade mappstrukturen: http://github.com/andrewhao/delorean. Ta en titt och låt mig veta vad du tycker.
Vad har vi lärt oss?
i vår tid tillsammans lärde vi oss om domändrivna designkoncept kring domäner och underdomäner. Vi lärde oss att visualisera våra mjukvarusystem som begränsade sammanhang på en Kontextkarta, som visade oss de områden i systemet som hör samman som sammanhängande delar.
slutar på en praktisk anteckning, Vi illustrerade hur Rails-filer och mappar kan “inverteras” och reimagined som domän-första grupperingar.
i mitt nästa inlägg fortsätter vi vår diskussion i ett kommande blogginlägg om hur vi ytterligare kan koppla bort vår domänorienterade Rails-kod med domänhändelser och så småningom ta oss in i mikroservices land.