Mapparkontexter Superkontexter: Koppla från domänspecifika och domängeneriska avgränsade sammanhang

(1 augusti 2019)

Du bygger ett nytt system och två medlemmar i ditt team föreslår alternativa arkitekturer för att skicka meddelanden. Vilken är korrekt?

Den första utvecklaren föreslår en push-modell: begränsade sammanhang bör instruera meddelandekontexten att skicka ett meddelande. Meddelandekontexten (Meddelanden) ska helt enkelt följa kommandon från andra sammanhang och skicka meddelanden när de blir instruerade till.

Den andra utvecklaren ogillar push-modellen och föreslår en koreograferad lösning: när avgränsade sammanhang höjer händelser Meddelanden bör lyssna och avgöra när en avisering ska skickas.

Hur skulle du bygga lösningen? Ännu viktigare, hur skulle du lösa detta beslut i teamet? Hur ska du utforma den mest effektiva arkitekturen som stöder kortsiktiga mål och långsiktig utveckling?

Att ha DDD-färdigheter i teamet är en stor fördel. Att kunna analysera din domän för att förstå grundläggande, stödjande och generiska funktioner gör det möjligt för dig att göra ljudtekniska avvägningar.

Låt oss utforska detta scenario vidare med ett DDD-perspektiv.

Domän Kapacitetsanalys

Argumentet för alternativ 1 (push-kommandon) är att generic (Notifications) inte ska bero på specifika. Om något är generiskt över många domäner, är det logiskt sett fel om det är kopplat till något i en enda domän.

Ser vi bortom abstrakt resonemang, varnar den här heuristiken oss om? Vilka är de tekniska konsekvenserna vi behöver vara medvetna om?

Det finns två scenarier som vi vill undvika:

  1. Undvika domänspecifik logik som läcker in i generiska sammanhang vilket resulterar i samarbete -byte
  2. Oförmåga att återanvända eller ersätta generiska sammanhang med en hylla-lösning på grund av att de är för tätt kopplade till domänspecifika begränsade sammanhang

Kopplingsdomän och allmänt ansvar

Med alternativ 1 sker ingen samväxling mellan domänspecifika och domängeneriska API: er. Om en ny anmälan behövs ändras endast de domänspecifika sammanhangen. Men så är inte fallet med alternativ 2.

Med alternativ 2 (koreograferat), om en ny händelse introduceras som kräver aviseringar, måste ett domänspecifikt API publicera den nya händelsen och aviseringstjänsten måste prenumerera på evenemanget och skicka ett meddelande. Detta känns inte rätt – kunskap om domänen gömmer sig i det generiska sammanhanget.

Isolering av generiska möjligheter

Om aviseringstjänsten verkligen är generisk och återanvänds i många lag eller till och med en organisation, kommer det att behöva veta om hundratals domänhändelser. Och med så många team beroende på meddelandekontexten kommer det säkert att bli en flaskhals som minskar potentialen för återanvändning över en organisation.

En annan designfeedback är att vi inte kan ersätta det generiska sammanhanget med en hylla-lösning. Om det verkligen är generiskt över många domäner kan vi inte ersätta det med en hylla som har mer funktionalitet och kostar mindre att köra, designen ger feedback om att något är fel.

Så är kommandon från specifikt till generiskt en bästa praxis?

Alla bevis tyder på att alternativ 1 är korrekt: domänspecifika sammanhang bör skicka kommandon till domängeneriska sammanhang för att koppla från domänlogik. Vår analys har dock varit grund. Vi måste analysera domänen ytterligare för att ge oss förtroendet för att vi gör bra tekniska val.

Djupare domänanalys

När vi tittar närmare på alternativ 1 (specifika instruktioner generiska), varje domän -specifikt sammanhang som behöver skicka meddelanden har tagit ett ytterligare ansvar. Det behöver veta när meddelanden ska skickas och vilken typ av meddelande som ska skickas.

Är det förnuftigt att ha aviseringslogik spridd över alla begränsade sammanhang? Tillsammans med trasslig kod kan detta innebära en storskalig samordning mellan många team om aviseringar närmar sig förändringar.

Det finns också en ökad risk eller inkonsekvenser och duplicering / avfall om varje team antar sin egen inställning till aviseringar. Delade bibliotek är en möjlighet men de löser inte alla problem och de ger också kompromisser.

Vissa domänbegrepp partitionerar tydligt (röda, grå, gula trianglar; blå cirklar) men vissa har överlappar olika dimensioner och kan kategoriseras på flera sätt (blå trianglar)

När det är svårt att avgöra om ett ansvar ska tillhöra i ett eller annat sammanhang, zooma in och sönderdela ansvaret ytterligare. Leta efter underansvar som kan brytas isär – kanske finns det ett dolt domänkoncept.

När ett koncept inte passar rent in i ett enda begränsat sammanhang, analysera det vidare för att identifiera underansvar. Kanske finns det ett dolt domänkoncept som kan ge en ren partitionerad modell.

Zooma in i det omtvistade ansvaret för att skicka meddelanden, kan det finnas ett saknat koncept ? Kanske finns det ett tredje koncept som länkar domänspecifikt till domängeneriskt för att ge en mer elegant modell.

Domain Mapper Contexts

Ett mönster som kan användas för att koppla från domänspecifikt och domängenerisk är Domain Mapper Context. När ett sammanhang antar denna roll lyssnar det på specifika domänhändelser och mappar dem på kommandon som skickas till ett generiskt sammanhang.

Lägg märke till hur mönstrets avvägningar jämförs med fördelar och nackdelar med de första två alternativen. Det ger fördelarna med båda – friheten att ändra hur aviseringar skickas utan att förstöra varje domänspecifikt sammanhang med anmälningsrelaterad komplexitet.

Tänk på scenariot: ett internt aviseringssystem ska ersättas med en hyllalösning. Domänavbildaren skulle dirigera alla kommandon till den nya aviseringstjänsten utan att några domänspecifika sammanhang påverkas.

Överväg ett annat scenario: ett nytt meddelande ska läggas till. En registrering skulle konfigureras inom mapparen: när {domänhändelse} utlöser {avisering}.

Aviseringar är en domängenerisk funktion – många domäner utnyttjar e-post- och push-aviseringar
Meddelandeinställningar på Twitter – mappning från domänspecifik händelse till domängeneriska åtgärder (e-post)

Varför kallas Mapper Contexts?

The namngivning av detta mönster är betydelsefullt. Åtgärder som sker inom en domän mappas på åtgärder som sker i en annan domän (ett generiskt sammanhang är generiskt över många domäner, så är det delvis i en annan domän).

Du kan likheter med andra mönster som meddelandeöversättare , men översättning innebär dock viss ekvivalens; det översatta värdet är en annan representation av det ursprungliga värdet. Med en mapper är detta inte fallet.

En mapper är mer lyssnare och observerar vad som händer inom domänen. Det fattar ett beslut om hur man ska svara på händelser i domänen med en åtgärd i en annan domän.

Gateway Domain Mapper Contexts

Om du bestämmer dig för att ersätta din specialbyggda generiskt sammanhang med en SaaS-lösning blir din Domain Mapper Context en Gateway Domain Mapper Context.

A Gateway sitter vid kanten av ett system som hanterar inflöde och utflöde av information

En Gateway Domain Mapper Context utför i princip samma funktion, men den sitter nu vid kanten av ditt system och kommunicerar över systemgränser. Det är en väg för information som flyter in och ut ur systemet.

Implementeringen kan se likadan ut, men att ha en mer exakt terminologi är användbar för att du ska kunna kommunicera din arkitektur tydligare.

Avvägningar för teknik för domänmapper

Det associerade med mönstret för domänmapparkontexter är inte obetydligt. Det extra kartläggningsskiktet innebär att det nu finns tre medarbetare inblandade. Fler saker som kan misslyckas.

Det finns också samförändring. När nya händelser introduceras eller befintliga händelser ändras måste både de domänspecifika kontexterna som äger händelserna och mapparkontexten ändras.

Det finns en allmänt använd lösning för att minimera dessa kostnader.

Kontext för självbetjäningsdomänmappare

Ett mönster som ofta förekommer i naturen är det självbetjäningsgränsade sammanhanget. Ett begränsat sammanhang som spelar den här rollen gör det möjligt för konsumenter att utnyttja kontextens möjligheter utan att blockeras av teamet som äger sammanhanget.

Den första varianten är en kompileringstidsmekanism via källkontroll och den andra är en runtime-mekanism API-samtal.

I aviseringsscenariot kan självbetjäningskontexten ge en DSL. Team som äger domänspecifika sammanhang skulle skapa en pull-begäran som innehåller ändringar i en konfigurationsfil (ännu bättre – kod som kompilerar och testas), som använder DSL, konfigurerar en mappning mellan en domänhändelse att prenumerera på och en avisering till skickas.

Med den dynamiska versionen skulle motsvarande konfiguration utföras via ett API-samtal eller användargränssnitt. När dynamisk konfiguration är möjlig och det inte finns några beroendeperioder beroende av domänen förskjuts sammanhanget mot att bli helt generiskt. Detta är ett vanligt evolutionärt mönster ().

Publicerat språk

Ett annat DDD-mönster att tänka på i denna typ av scenario är det publicerade språket. Varje händelse som utlöser en avisering bör ingå i ett publicerat språk.

Ett publicerat språk är ett kontrakt eller ett avtal om formatet för meddelanden som produceras av ett begränsat sammanhang. Att se till att händelser som kräver en anmälan är en del av ett publicerat språk innebär att större försiktighet bör iakttas för att säkerställa bakåtkompatibilitet och meddela konsumenter om framtida förändringar.

Ska jag alltid använda Domain Mapper-sammanhang?

Definitivt inte. Varje mönster som presenteras i inlägget är giltigt och används framgångsrikt i en mängd olika system. Domain Mapper Contexts är ett annat mönster med tydliga avvägningar som du kan använda för att utforska alternativa modelleringsmöjligheter.

Sökningen efter Supercontexts

Det är lätt att modellera domäner på en naiv eller ytlig nivå . När du hör ett ord som ”aviseringar” är det lätt att falla för det sammanhängande namnet fallacy, förutsatt att eftersom ett ord låter som ett enda koncept måste det representeras av ett enda begränsat sammanhang i vårt system.

När vi använder DDD för att analysera på en djupare nivå och vi ser på separata domänspecifika och domängeneriska begrepp, kommer vi ofta att upptäcka att det finns flera sätt att modellera domänen, inklusive att ha flera begränsade sammanhang – avgränsa domän som är specifik från generisk – inom en enda superkontext.

Att koppla från domän-specifikt från domän-generiskt handlar inte om att skapa vackra abstrakta modeller som följer gamla DDD-regler, det handlar om att koppla bort begrepp som ändras tillsammans av olika skäl så att vi kan konstruera handel -off som gör det möjligt för system att utvecklas lättare.

Försök att undvika felaktig klassificering av enskild domän: förutsatt att en högnivåfunktion, meddelanden, måste ha en enda domänklassificering (t.ex. generisk).

I orde För att få djupare insikter i domänen rekommenderar jag att du tar med DDD-färdigheter och DDD-aktiviteter i ditt team. Experimentera med EventStorming och (The Bounded Context Design Canvas) som utgångspunkt för att lära dig att modellera bättre begränsade sammanhang.

Utbildning och rådgivning

Om du söker hjälp med att utforska din domän, utforma ditt system eller träna dina team, kontakta mig för mer information. Jag arbetar med ett nätverk av mycket erfarna DDD-utövare som brinner för att utforma effektiva sociotekniska system anpassade till affärsmålen och domänen.