Meddelanden i en serviceorienterad arkitektur

Publicerad

(Joan Zapata) (8 dec 2020)

Serviceorienterad arkitektur (SOA) har visat sig dess styrkor i praktiskt taget alla branscher att producera skalbara och evolutiva system. SOA har många betydelser, men dess grundidé att bryta en komplex applikation i mindre, återanvändbara och löst kopplade tjänster har kommit långt sedan slutet av 1990-talet. Vi har anammat konceptet från första dagen och efter tre år har vi nu ett tjugo domänstyrda frikopplade tjänster som strukturerar våra backend-applikationer. Det tillät oss att använda de språk vi gillade – som Kotlin , Elixir – och verktygen vi ville – som Event Sourcing – för uppgiften.

Men att ha flera tjänster kommer med sina egna utmaningar, den första är hur får tjänster att kommunicera med varandra ? Det finns många strategier där ute, och den här artikeln är en avkastning på erfarenheterna från dem vi använder på Memo Bank.

Låt oss börja med två tjänster och detta enkla användningsfall som ett exempel:

VILA över HTTPS

Liksom många backend-applikationer började vi med REST samtal över HTTPS.

Cykliska beroenden mellan tjänster måste undvikas till varje pris, så kontrakt kan inte direkt be kunder att aktivera den nya kunden så snart avtalet undertecknas. Istället måste kunder skicka förfrågningar till kontrakt regelbundet för att veta när uppgiften är klar.

HTTPS är en bra start, och definitivt något att hålla i verktygslådan: det är enkelt att ställa in, stöds allmänt och används också för att exponera API: er för applikationer i frontend. Men det finns ett stort problem med detta schema, det är synkron (även om det inte nödvändigtvis blockerar). Det betyder att medan kontrakt fungerar, väntar kunder med en aktiv anslutning. När det används i stor skala betyder det att den långsammaste tjänsten kan sakta ner hela systemet. Det betyder också att när tjänsten kontrakt inte är tillgänglig är tjänsten kunder inte heller, vilket kallas kaskadfel.

Hur hanterade vi denna fråga? Vi använde två olika mönster: diskret och kontinuerlig meddelandehantering.

Diskreta meddelanden

Låt oss ta det första användningsfallet: kunder vill ha kontrakt för att skicka ett kontrakt.

För detta kan vi lösa problemet med en diskret meddelandemekanism: köer . Du kan se en kö som en hög tillgänglighet tredje parts postlåda. Även när huvudtjänsten inte är tillgänglig accepterar brevlådan meddelanden. Flera instanser av samma tjänst kan konsumera samma kö, i vilket fall kön fungerar som en belastningsutjämnare och säkerställer att varje meddelande hanteras minst en gång. När ett meddelande har hanterats kan brevlådan glömma bort det.

Vi använder köer för att skicka kommandon (t.ex. “SendContract”) från en tjänst till en annan. Det har flera fördelar:

  • Samtalstjänsten förlitar sig inte på tillgängligheten den mottagande tjänsten och kan återuppta sitt arbete så snart kommandot är i kön.
  • Mottagaren kan hantera kommandona i sin egen takt , och vi kan enkelt skala mottagartjänsten upp eller ner beroende på belastningen på dess köer;
  • Som en bonus, misslyckande kan enkelt isoleras och hanteras manuellt (vi hoppas kunna täcka ämnet döda bokstäver i en annan artikel).

Kontinuerlig meddelandehantering

Låt oss nu se det andra användningsfallet, när kontraktet undertecknas måste kontrakt meddela kunder hände så att det kan aktivera kunden.

Det är frestande att använda en annan kö här, men som vi sa tidigare vill vi inte ha ett cykliskt beroende, så kontrakt vet inte vad som helst om kunder . Det kan alltså inte skicka ett ”ActivateCustomer” -kommando, och det kan inte veta när eller var det ska skickas.

Vi löste problemet med en kontinuerlig meddelandemekanism: strömmar .Du kan se en ström som en ordnad sekvens av händelser som kan konsumeras i realtid men också görs tillgängliga över tiden. I motsats till köer som inte är relaterade kortvariga kommandon, berättar strömmar en bestående historia .

Varje tjänst på Memo Bank sänder en ström av händelser som beskriver livscykeln för dess resurser. Att bygga och underhålla dessa händelser är en integrerad del av någon utveckling , oavsett om denna händelse behövs omedelbart. Det är en del av rutinen, precis som automatiserade tester och mätvärden.

Dessa händelser blir därmed en pålitlig tidsstämplad logg för oföränderlig fakta . Och eftersom de är en del av API: et för den emitterande tjänsten kan de konsumeras av vilken annan tjänst som helst, både i realtid (en efter en) och i framtiden. Dessa händelser har ett enormt värde för spårbarhet och dataanalys .

Sammanfattningsvis:

  • Köer används för att skicka kommandon till en specifik tjänst;
  • Strömmar används för att exponera fakta från en specifik tjänst.

Allt om beroenden

Diagrammet ovan kan få dig att undra om du tittar på pilarna, introducerar det inte en cykliskt beroende mellan kunder och kontrakt ?

Nej det gör det inte. Ett beroende definieras inte av datainriktningen utan av kunskapstjänsterna har av varandra. Här vet kunder om kontrakt, den berättar vad den ska göra och den lyssnar på dess historia. Men kontrakt vet ingenting om kunder, det behöver inte veta vem som skickar kommandona eller vem som lyssnar på dess historia.

Båda kön och strömmen är en del av kontrakt API, och kunder är beroende av detta API.

Har en som namnger konvention för kommandon och fakta är mycket viktigt för att förmedla denna idé. Vi använder alltid ett basformulär för kommandon, som “SendContract”, och en form för tidigare particip för fakta, som “ContractSent”.

Observera att det passar bekvämt med vår Core Banking System-arkitektur baserat på CQRS / ES . I denna terminologi är kommandon desamma och händelser är fakta.

Hur man väljer beroendets riktning

Med tanke på de principer som förklarats tidigare skulle den här lösningen vara lika giltig:

Men om båda lösningarna är giltiga, hur ska man välja varandra? Tja, det är upp till dig.

Allt kommer ner till riktningen för beroendet du vill ha att ställa in.

A: kunder beror på kontrakt . B: kontrakt beror på kunder .

Här är några frågor vi vanligtvis ställer oss:

  • Kan en tjänst enkelt vara agnostisk för andra?
    Här till exempel så länge vi ger innehållet till kontrakt , kontrakt kan vara helt agnostiska för vilken tjänst som använder den. Det är svårare att föreställa sig att kunderna är agnostiska när det gäller ett kontrakt. Det är för A.
  • Vad händer om en tjänst var en tredje part?
    För Det vore till exempel inte meningsfullt för Memo Bank att lägga ut kunder men det kunde för kontrakt . Således är detta också till förmån för A.
  • Orkestrerar en tjänst andra tjänster?
    Implicit orkestrering är dålig, du kan hitta mer om det i det här samtalet av Bernd Ruecker. Att skapa en kund är ett komplext arbetsflöde som involverar många tjänster (skicka e-post, aviseringar, skapa ett bankkonto, etc.), så kunder är förmodligen en orkestrator här.Att göra orchestrator beroende av andra tjänster – och inte tvärtom – gör koden mycket lättare att förstå, eftersom hela arbetsflödet kan hittas på en enda plats. Det är också till förmån för A.
  • Skapar det en cykel i den övergripande arkitekturen?
    Även om det inte finns någon länk mellan de två tjänsterna är de båda beroende av andra tjänster. Låt oss säga att kunder beror på användare och användare beror på avtal redan. Om vi ​​valde lösning B skulle det skapa en cykel med de tre tjänsterna. Det är också för A.

Slutsats

Meddelanden är en av de första frågorna vi behöver besvara när vi skapar en tjänstorienterad arkitektur. Att använda HTTPS och REST verkar som den mest enkla lösningen till en början, men det har sina begränsningar. Vi slutförde vår arsenal med köer och strömmar , och vi ställer huvudsakligen två riktlinjer.

Först bör varje tjänst strömma händelser när fakta händer inom denna tjänst, även om vi inte behöver dessa händelser ännu. Dessa händelser bör berätta en historia om vad som händer i tjänsten, som ”ContractSent”, ”ContractSigned”. Det här är bra för spårbarhet – vilket krävs som en bank – men också för att konsolidera varje tjänsts API och för att göra systemet lättare att arbeta med för alla team.

För det andra handlar det om beroenden . Beroenden formar systemet och cykliska beroenden är fiendens främsta. När beroendeförhållandena är korrekt inställda är meddelandeverktygen bara här för att låta data flöda i vilken riktning som helst.

Ursprungligen publicerad på https://memo.bank/en/magazine .

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *