Building Domain Driven Microservices

Chandra Ramalingam

Follow

Jul 1, 2020 – 18 min olvasni

Image credits:

A “mikro” kifejezés a mikroszolgáltatásokban, bár jelzi a szolgáltatás méretét, nem az egyetlen kritérium, amely egy alkalmazást mikroszolgáltatássá tesz. Amikor a csapatok mikroszolgáltatás-alapú architektúrára állnak át, céljuk az agilitás növelése – a funkciók autonóm és gyakori telepítése. Nehéz egyetlen tömör meghatározást adni erre az architektúrális stílusra. Nekem ez a rövid definíció tetszett Adrian Cockcroft-tól: “szolgáltatásorientált architektúra, amely lazán összekapcsolt elemekből áll, amelyek korlátozott kontextusokkal rendelkeznek.”

Bár ez egy magas szintű tervezési heurisztikát határoz meg, a mikroszolgáltatások architektúrája rendelkezik néhány egyedi jellemzővel, amelyek megkülönböztetik az egykori szolgáltatásorientált architektúrától. Az alábbiakban néhány ilyen jellemzőt mutatunk be. Ezek és néhány további jól dokumentáltak – Martin Fowler cikke és Sam Newman Building Microservices című könyve, hogy csak néhányat említsünk.

  1. A szolgáltatások jól meghatározott határokkal rendelkeznek, amelyek középpontjában az üzleti kontextus áll, nem pedig önkényes technikai absztrakciók
  2. A megvalósítás részleteit elrejtik, a funkciókat pedig szándékot felfedő interfészeken keresztül tárják fel
  3. A szolgáltatások nem osztják meg belső struktúrájukat a határaikon túl. Például nem osztják meg az adatbázisokat.
  4. A szolgáltatások ellenállóak a hibákkal szemben.
  5. A csapatok önállóan birtokolják funkcióikat, és képesek önállóan kiadni a változásokat
  6. A csapatok elfogadják az automatizálás kultúráját. Például automatizált tesztelés, folyamatos integráció és folyamatos szállítás

Röviden így foglalhatjuk össze ezt az architektúra-stílust:

Lazán kapcsolt szolgáltatásorientált architektúra, ahol minden egyes szolgáltatás egy jól meghatározott, körülhatárolt kontextusba van zárva, ami lehetővé teszi az alkalmazások gyors, gyakori és megbízható szállítását.

Tartományvezérelt tervezés és lehatárolt kontextusok

A mikroszolgáltatások ereje a felelősségük egyértelmű meghatározásából és a köztük lévő határvonal elhatárolásából ered. A cél itt az, hogy a határon belül magas kohéziót, azon kívül pedig alacsony csatolást alakítsunk ki. Vagyis az olyan dolgoknak, amelyek hajlamosak együtt változni, összetartozniuk kell. Mint sok valós életbeli probléma esetében, ezt könnyebb mondani, mint megtenni – a vállalkozások fejlődnek, és a feltételezések változnak. Ezért a refaktorálás képessége egy másik kritikus dolog, amit figyelembe kell venni a rendszerek tervezésekor.

A területvezérelt tervezés (DDD) kulcsfontosságú, és véleményünk szerint szükséges eszköz a mikroszolgáltatások tervezésekor, legyen szó egy monolit megtöréséről vagy egy zöldmezős projekt megvalósításáról. A területvezérelt tervezés, amelyet Eric Evans tett híressé könyvével , olyan ötletek, elvek és minták összessége, amelyek segítenek a szoftverrendszerek tervezésében az üzleti terület mögöttes modellje alapján. A fejlesztők és a szakterületi szakértők együtt dolgoznak az üzleti modellek létrehozásán egy Ubiquitous közös nyelven. Ezután ezeket a modelleket olyan rendszerekhez kötik, ahol van értelme, együttműködési protokollokat hoznak létre e rendszerek és az ezeken a szolgáltatásokon dolgozó csapatok között. Ami még fontosabb, megtervezik a rendszerek közötti fogalmi kontúrokat vagy határokat.

A mikroszolgáltatás-tervezés ezekből a koncepciókból merít ihletet, mivel mindezek az elvek segítenek olyan moduláris rendszerek létrehozásában, amelyek egymástól függetlenül változhatnak és fejlődhetnek.

Mielőtt továbbmennénk, nézzük át gyorsan a DDD néhány alapvető terminológiáját. A Domain-Driven Design teljes áttekintése nem tartozik ennek a blognak a keretébe. Eric Evans könyvét ajánljuk mindenkinek, aki mikroszolgáltatásokat próbál építeni

Domain: Azt képviseli, amit egy szervezet csinál. Az alábbi példában ez a kiskereskedelem vagy az e-kereskedelem lenne.

Aldomain: Egy szervezeten belüli szervezet vagy üzleti egység. Egy domain több aldomainből áll.

Általános nyelv: A modellek kifejezésére használt nyelv. Az alábbi példában az Item egy Model, amely az Ubiquitous nyelvhez tartozik minden egyes altartományban. A fejlesztők, a termékmenedzserek, a szakterületi szakértők és az üzleti érdekeltek ugyanabban a nyelvben állapodnak meg, és ezt használják az artefaktumokban – kód, termékdokumentáció stb.

1. ábra. Aldomainek és korlátozott kontextusok az e-kereskedelem területén

Bounded Contexts: A tartományvezérelt tervezés a Bounded contexts-t a következőképpen definiálja: “Az a környezet, amelyben egy szó vagy egy kijelentés megjelenik, és amely meghatározza annak jelentését”. Röviden ez azt a határt jelenti, amelyen belül egy modell értelmet nyer. A fenti példában az “Item” minden egyes kontextusban más jelentést kap. A Katalógus kontextusban az Item egy eladható terméket jelent, míg a Kosár kontextusban azt a terméket, amelyet a vásárló a kosarába tett. A Teljesítés kontextusban egy raktári tételt jelent, amelyet elküldenek a vásárlónak. Mindegyik modell más és más, és mindegyiknek más jelentése van, és esetleg más attribútumokat tartalmaz. Azáltal, hogy ezeket a modelleket elkülönítjük és elkülönítjük a saját határaikon belül, szabadon és félreérthetetlenül fejezhetjük ki a modelleket.

Megjegyzés: Lényeges megérteni a különbséget az altartományok és a határolt kontextusok között. Egy szubdomain a problématérbe tartozik, vagyis abba, ahogyan a vállalkozásunk látja a problémát, míg a Bounded contexts a megoldástérbe tartozik, vagyis abba, ahogyan a probléma megoldását megvalósítjuk. Elméletileg minden altartománynak több kötött kontextusa is lehet, bár mi arra törekszünk, hogy altartományonként egy kötött kontextus legyen.

Hogyan kapcsolódnak a mikroszolgáltatások a kötött kontextusokhoz

Most, hová illeszkednek a mikroszolgáltatások? Jogos az a kijelentés, hogy minden egyes kötött kontextus egy mikroszolgáltatásnak felel meg? Igen és nem. Meglátjuk, hogy miért. Előfordulhatnak olyan esetek, amikor a határolt kontextus határa vagy kontúrja meglehetősen nagy.

2. ábra. Korlátozott kontextus és mikroszolgáltatások

Mondjuk a fenti példát. Az Árképzés lehatárolt kontextus három különböző modellel rendelkezik: Ár, Árazott tételek és Kedvezmények, amelyek mindegyike egy-egy katalógustétel áráért, egy tétellista teljes árának kiszámításáért, illetve a kedvezmények alkalmazásáért felelős. Létrehozhatnánk egyetlen rendszert, amely az összes fenti modellt magában foglalja, de ez egy indokolatlanul nagy alkalmazássá válhatna. Az egyes adatmodelleknek, mint korábban említettük, megvannak a maguk invarianciái és üzleti szabályai. Idővel, ha nem vigyázunk, a rendszer egy Nagy Sárgolyóvá válhat, homályos határokkal, átfedő felelősségi körökkel, és valószínűleg visszatérhetünk oda, ahonnan indultunk – egy monolitba.

Egy másik módja a rendszer modellezésének az, hogy a kapcsolódó modelleket elkülönítjük, vagy külön mikroszolgáltatásokká csoportosítjuk. A DDD-ben ezeket a modelleket – Ár, árazott tételek és kedvezmények – aggregátumoknak nevezzük. Az aggregátum egy önálló modell, amely a kapcsolódó modelleket állítja össze. Egy aggregátum állapotát csak egy közzétett interfészen keresztül lehet megváltoztatni, és az aggregátum biztosítja a konzisztenciát és azt, hogy az invarianciák érvényesek legyenek.

Formálisan az Aggregátum a kapcsolódó objektumok egy egységként kezelt csoportja az adatváltozásokhoz. A külső hivatkozások az AGGREGÁTUM egy tagjára korlátozódnak, amelyet gyökérnek nevezünk. Az AGGREGÁT határain belül egy sor konzisztencia szabály érvényes.

3. ábra. Mikroszolgáltatások az árképzés kontextusában

Mégis nem szükséges minden aggregátumot külön mikroszolgáltatásként modellezni. A 3. ábrán látható szolgáltatások (aggregátumok) esetében ez így alakult, de ez nem feltétlenül szabály. Bizonyos esetekben lehet értelme több aggregátumot egyetlen szolgáltatásban elhelyezni, különösen akkor, ha nem értjük teljesen az üzleti tartományt. Fontos megjegyezni, hogy a konzisztencia csak egyetlen aggregátumon belül garantálható, és az aggregátumokat csak a közzétett felületen keresztül lehet módosítani. Ezek bármilyen megszegése annak a kockázatát hordozza, hogy egy nagy sárgolyóvá válik.

Kontextustérképek – Egy mód a pontos mikroszolgáltatás-határok kijelölésére

Egy másik elengedhetetlen eszköztár az arzenálban a kontextustérképek fogalma – ismét a Domain Driven Designból. Egy monolit általában különböző, többnyire szorosan összekapcsolt modellekből áll – a modellek talán ismerik egymás intim részleteit, az egyik módosítása mellékhatásokat okozhat egy másikra, és így tovább. A monolit lebontása során létfontosságú, hogy azonosítsuk ezeket a modelleket – ebben az esetben aggregátumokat – és kapcsolataikat. A kontextustérképek éppen ebben segítenek. A különböző körülhatárolt kontextusok és aggregátumok közötti kapcsolatok azonosítására és meghatározására szolgálnak. Míg a határolt kontextusok egy modell – a fenti példában az ár, a kedvezmények stb. – határait határozzák meg, addig a kontextustérképek a modellek és a különböző kontextusok közötti kapcsolatokat határozzák meg. Miután azonosítottuk ezeket a függőségeket, meghatározhatjuk a megfelelő együttműködési modellt az ezeket a szolgáltatásokat megvalósító csapatok között.

A Kontextustérképek teljes feltárása meghaladja ennek a blognak a kereteit, de egy példával szemléltetjük. Az alábbi ábra egy e-kereskedelmi megrendelés kifizetéseit kezelő különböző alkalmazásokat ábrázol.

  1. A kosár-kontextus gondoskodik a megrendelés online engedélyezéséről; a megrendelés-kontextus a teljesítés utáni fizetési folyamatokat, például az elszámolásokat dolgozza fel; a kapcsolattartó központ kezeli az olyan kivételeket, mint a fizetések újbóli próbálkozása és a megrendeléshez használt fizetési mód megváltoztatása
  2. Az egyszerűség kedvéért tegyük fel, hogy mindezen kontextusok különálló szolgáltatásként vannak megvalósítva
  3. Mindezen kontextusok ugyanazt a modellt kapszulázzák.
  4. Megjegyezzük, hogy ezek a modellek logikailag azonosak. Vagyis mind ugyanazt az Ubiquitous domain nyelvet követik – fizetési módok, engedélyek és elszámolások. Csak éppen különböző kontextusok részét képezik.

A másik jele annak, hogy ugyanaz a modell különböző kontextusokban van elterjedve, hogy ezek mindegyike közvetlenül integrálódik egyetlen fizetési átjáróba, és ugyanazokat a műveleteket végzik, mint egymás

4. ábra. Egy helytelenül definiált kontextustérkép

A szolgáltatáshatárok újradefiniálása – Az aggregátumok hozzárendelése a megfelelő kontextusokhoz

A fenti kialakításban (4. ábra) néhány probléma nagyon is nyilvánvaló. A kifizetések aggregátuma több kontextus része. Lehetetlen az invariánsokat és a konzisztenciát érvényesíteni a különböző szolgáltatások között, nem is beszélve a szolgáltatások közötti párhuzamossági problémákról. Mi történik például, ha a kapcsolattartó központ megváltoztatja a megrendeléshez kapcsolódó fizetési módot, miközben a Rendelések szolgáltatás egy korábban benyújtott fizetési mód elszámolását próbálja elküldeni. Azt is vegye figyelembe, hogy a fizetési átjáróban bekövetkező bármilyen változás több szolgáltatás és potenciálisan számos csapat módosítását kényszerítené ki, mivel különböző csoportok birtokolhatják ezeket a kontextusokat.

Néhány módosítással és az aggregátumok megfelelő kontextusokhoz igazításával sokkal jobb képet kapunk ezekről az aldomainekről – 5. ábra. Sok minden megváltozott. Tekintsük át a változásokat:

  1. A Fizetések aggregátum új otthont kapott – Fizetési szolgáltatás. Ez a szolgáltatás absztrahálja a Fizetési átjárót is a többi, fizetési szolgáltatásokat igénylő szolgáltatástól. Mivel az aggregátumot mostantól egyetlen lehatárolt kontextus birtokolja, az invariánsok könnyen kezelhetők; minden tranzakció ugyanazon a szolgáltatáshatáron belül történik, ami segít elkerülni az esetleges párhuzamossági problémákat.
  2. A fizetési aggregátum egy antikorrupciós réteget (ACL) használ az alapvető tartománymodell elszigetelésére a fizetési átjáró adatmodelljétől, amely általában egy harmadik féltől származó szolgáltató, és esetleg változni kényszerül. Egy későbbi bejegyzésben mélyebben belemerülünk az ilyen szolgáltatás alkalmazás-tervezésébe a Ports and Adapters mintát használva. Az ACL réteg általában azokat az adaptereket tartalmazza, amelyek a fizetési átjáró adatmodelljét átalakítják a Fizetések összesített adatmodelljévé.
  3. A Kosár szolgáltatás közvetlen API hívásokon keresztül hívja a Fizetések szolgáltatást, mivel a Kosár szolgáltatásnak esetleg a fizetési engedélyezést kell elvégeznie, miközben az ügyfelek a weboldalon tartózkodnak
  4. Figyeljünk a Megrendelések és a Fizetések szolgáltatás közötti interakcióra. A Megrendelések szolgáltatás egy domain eseményt bocsát ki (erről bővebben később ebben a blogban). A Fizetési szolgáltatás meghallgatja ezt az eseményt, és befejezi a megrendelés kiegyenlítését
  5. A kapcsolattartó központ szolgáltatásának számos aggregátuma lehet, de minket ebben a felhasználási esetben csak a Megrendelések aggregátuma érdekel. Ez a szolgáltatás eseményt bocsát ki, amikor a fizetési mód megváltozik, és a Payments szolgáltatás erre úgy reagál, hogy a korábban használt hitelkártyát visszafordítja, és az új hitelkártyát dolgozza fel.

5. ábra. Újradefiniált kontextustérkép

Egy monolitikus vagy örökölt alkalmazás általában sok aggregátummal rendelkezik, gyakran egymást átfedő határokkal. Ezen aggregátumok és függőségeik kontextustérképének elkészítése segít megérteni a monolitokból kinyerhető új mikroszolgáltatások körvonalait. Ne feledjük, hogy a mikroszolgáltatási architektúra sikere vagy sikertelensége az aggregátumok közötti alacsony csatoláson és az aggregátumokon belüli magas kohézión múlik.

Azt is fontos megjegyezni, hogy a határolt kontextusok maguk is megfelelő kohéziós egységek. Még ha egy kontextusnak több aggregátuma is van, a teljes kontextus az aggregátumaival együtt egyetlen mikroszolgáltatássá állítható össze. Ezt a heurisztikát különösen hasznosnak találjuk olyan tartományok esetében, amelyek kissé homályosak – gondoljunk csak egy új üzletágra, amelybe a szervezet belevág. Lehet, hogy nincs elegendő rálátásunk a megfelelő szétválasztási határokat illetően, és az aggregátumok bármilyen idő előtti szétbontása költséges refaktoráláshoz vezethet. Képzeljük el, hogy két adatbázist kell egyesítenünk egybe, adatmigrációval együtt, mert véletlenül rájöttünk, hogy két aggregátum összetartozik. De gondoskodjunk arról, hogy ezek az aggregátumok interfészek révén kellőképpen el legyenek szigetelve, hogy ne ismerjék egymás bonyolult részleteit.

Event Storming – Egy másik technika a szolgáltatáshatárok azonosítására

Az eseményvihar egy másik alapvető technika az aggregátumok (és így a mikroszolgáltatások) azonosítására egy rendszerben. Hasznos eszköz mind a monolitok megbontásához, mind a mikroszolgáltatások összetett ökoszisztémájának megtervezésekor. Ezt a technikát használtuk az egyik összetett alkalmazásunk lebontására, és az Event Storminggal kapcsolatos tapasztalatainkról egy külön blogban kívánunk beszámolni. E blog keretein belül egy gyors, magas szintű áttekintést szeretnénk adni. Kérjük, nézze meg Alberto Brandelloni videóját a témáról, ha tovább szeretne elmélyülni.

Az Event Storming dióhéjban az egy alkalmazáson – esetünkben egy monoliton – dolgozó csapatok közötti ötletbörze, amelynek célja a rendszerben zajló különböző domainesemények és folyamatok azonosítása. A csapatok azonosítják azokat az aggregátumokat vagy modelleket is, amelyeket ezek az események érintenek, valamint azok későbbi hatásait. Miközben a csapatok ezt a feladatot végzik, azonosítják a különböző átfedő fogalmakat, a nem egyértelmű tartományi nyelvet és az egymásnak ellentmondó üzleti folyamatokat. Csoportosítják a kapcsolódó modelleket, újradefiniálják az aggregátumokat és azonosítják a duplikált folyamatokat. Ahogy haladnak előre ebben a feladatban, világossá válik, hogy ezek az aggregátumok milyen korlátozott kontextusba tartoznak. Az Event Storming workshopok akkor hasznosak, ha az összes csapat egyetlen – fizikai vagy virtuális – helyiségben van, és elkezdik feltérképezni az eseményeket, parancsokat és folyamatokat egy scrum-stílusú táblán. A gyakorlat végén a következők a szokásos eredmények:

  1. Az aggregátumok újradefiniált listája. Ezekből potenciálisan új mikroszolgáltatások lesznek
  2. Tartománybeli események, amelyeknek ezek között a mikroszolgáltatások között kell áramolniuk
  3. Kommandók, amelyek más alkalmazások vagy felhasználók közvetlen meghívásai

Az alábbiakban egy Event Storming workshop végén készült mintatáblát mutatunk. Ez egy nagyszerű együttműködési gyakorlat a csapatok számára, hogy megegyezzenek a megfelelő aggregátumokról és kötött kontextusokról. Amellett, hogy ez egy nagyszerű csapatépítő gyakorlat, a csapatok úgy kerülnek ki erről a foglalkozásról, hogy közösen megértik a tartományt, az ubiquitást és a pontos szolgáltatáshatárokat.

6. ábra. Event Storming tábla

Kommunikáció a mikroszolgáltatások között

Gyorsan összefoglalva, egy monolit több aggregátumot fogad egyetlen folyamathatáron belül. Ennélfogva az aggregátumok konzisztenciájának kezelése ezen a határon belül lehetséges. Például, ha egy Vevő lead egy megrendelést, csökkenthetjük a tételek leltárát, küldhetünk egy e-mailt a Vevőnek – mindezt egyetlen tranzakción belül. Minden művelet sikeres vagy sikertelen lenne. De ahogy megbontjuk a monolitot, és az aggregátumokat különböző kontextusokba szórjuk, több tíz vagy akár több száz mikroszolgáltatásunk lesz. A folyamatok, amelyek eddig egy monolit egyetlen határán belül léteztek, most több elosztott rendszerben oszlanak el. A tranzakciós integritás és konzisztencia elérése mindezen elosztott rendszerekben nagyon nehéz, és ennek ára van – a rendszerek rendelkezésre állása.

A mikroszolgáltatások is elosztott rendszerek. Ezért a CAP-tétel rájuk is vonatkozik – “egy elosztott rendszer három kívánt tulajdonságból csak kettőt tud teljesíteni: konzisztencia, rendelkezésre állás és partíciótűrés (a “C”, “A” és “P” a CAP-ban)”. A valós rendszerekben a partíciótűrés nem alku tárgya – a hálózat megbízhatatlan, a virtuális gépek leállhatnak, a régiók közötti késleltetés rosszabbá válhat, és így tovább.

Így marad a választásunk a rendelkezésre állás vagy a konzisztencia között. Nos, tudjuk, hogy bármely modern alkalmazásban a rendelkezésre állás feláldozása sem jó ötlet.

7. ábra. CAP-tétel

Az alkalmazásokat az esetleges konzisztencia köré tervezzük

Ha több elosztott rendszerre próbálunk tranzakciókat építeni, akkor megint a monolitok földjén kötünk ki. Csak ezúttal a legrosszabb fajta lesz, egy elosztott monolit. Ha bármelyik rendszer elérhetetlenné válik, az egész folyamat elérhetetlenné válik, ami gyakran frusztráló ügyfélélményhez, meghiúsult ígéretekhez stb. vezet. Emellett az egyik szolgáltatás módosítása általában egy másik szolgáltatás módosítását is maga után vonhatja, ami összetett és költséges telepítésekhez vezet. Ezért jobban járunk, ha a felhasználási eseteinkre szabott alkalmazásokat úgy tervezzük meg, hogy a rendelkezésre állás javára elviseljünk egy kis következetlenséget. A fenti példa esetében az összes folyamatot aszinkronizálhatjuk, és így végül konzisztensekké tehetjük. Küldhetünk e-maileket aszinkron módon, a többi folyamattól függetlenül; Ha egy beígért cikk később nem áll rendelkezésre a raktárban, akkor a cikket visszarendelhetjük, vagy egy bizonyos küszöbérték felett leállíthatjuk a cikkre vonatkozó megrendelések fogadását.
Egyszer előfordulhat olyan forgatókönyv, amely erős ACID-stílusú tranzakciókat igényelhet két aggregátumon keresztül, különböző folyamathatárok között. Ez kiváló jel arra, hogy felülvizsgáljuk ezeket az aggregátumokat, és esetleg egyesítsük őket egybe. Az eseményvihar és a kontextustérképek segítenek az ilyen függőségek korai felismerésében, mielőtt elkezdenénk ezeket az aggregátumokat különböző folyamathatárokon belül szétbontani. Két mikroszolgáltatás egybeolvasztása költséges, és ezt igyekeznünk kell elkerülni.

Előnyben részesítsük az eseményvezérelt architektúrát

A mikroszolgáltatások lényeges változásokat sugározhatnak, amelyek az aggregátumaikkal történnek. Ezeket tartományi eseményeknek nevezzük, és minden olyan szolgáltatás, amely érdekelt ezekben a változásokban, figyelhet ezekre az eseményekre, és a saját tartományán belül megteheti a megfelelő lépéseket. Ezzel a módszerrel elkerülhető a viselkedési csatolás – az egyik tartomány nem írja elő, hogy a többi tartománynak mit kell tennie – és az időbeli csatolás – egy folyamat sikeres befejezése nem függ attól, hogy az összes rendszer egyszerre legyen elérhető. Ez természetesen azt jelenti, hogy a rendszerek végül konzisztensek lesznek.

8. ábra. Eseményvezérelt architektúra

A fenti példában a Megrendelések szolgáltatás közzétesz egy eseményt – Megrendelés törlése. A többi szolgáltatás, amely feliratkozott az eseményre, feldolgozza a saját tartományi funkcióit: A fizetési szolgáltatás visszatéríti a pénzt, a leltárszolgáltatás kiigazítja a tételek leltárát stb. Néhány megjegyzendő dolog az integráció megbízhatóságának és rugalmasságának biztosítása érdekében:

  1. A termelőknek biztosítaniuk kell, hogy legalább egyszer létrehozzanak egy eseményt. Ha ez meghiúsul, biztosítaniuk kell, hogy legyen egy tartalék mechanizmus az események újraindítására
  2. A fogyasztóknak biztosítaniuk kell, hogy az eseményeket idempotens módon fogyasztják. Ha ugyanaz az esemény újra bekövetkezik, nem szabad, hogy bármilyen mellékhatás legyen a fogyasztó oldalán. Az események soron kívül is érkezhetnek. A fogyasztók használhatnak időbélyegző vagy verziószám mezőt az események egyediségének garantálására.

Az egyes felhasználási esetek jellege miatt nem mindig lehetséges az eseményalapú integráció használata. Kérjük, tekintse meg a Kosár szolgáltatás és a Fizetési szolgáltatás közötti integrációt. Ez egy szinkron integráció, és ezért van néhány dolog, amire figyelnünk kell. Ez egy példa a viselkedési csatolásra – a Kosár szolgáltatás talán meghív egy REST API-t a Fizetési szolgáltatásból, és utasítja azt, hogy engedélyezze a fizetést egy megrendeléshez, valamint az időbeli csatolásra – a Fizetési szolgáltatásnak elérhetőnek kell lennie ahhoz, hogy a Kosár szolgáltatás elfogadja a megrendelést. Ez a fajta csatolás csökkenti ezeknek a kontextusoknak az autonómiáját, és talán nemkívánatos függőséget eredményez. Van néhány mód arra, hogy elkerüljük ezt a csatolást, de mindezekkel a lehetőségekkel elveszítjük azt a képességet, hogy azonnali visszajelzést adjunk a vásárlóknak.

  1. A REST API-t eseményalapú integrációvá alakítjuk át. De ez a lehetőség nem biztos, hogy elérhető, ha a fizetési szolgáltatás csak egy REST API-t tár fel
  2. A kosár szolgáltatás azonnal elfogad egy megrendelést, és van egy kötegelt munka, amely felveszi a megrendeléseket, és meghívja a fizetési szolgáltatás API-ját
  3. A kosár szolgáltatás egy helyi eseményt hoz létre, amely aztán meghívja a fizetési szolgáltatás API-ját

A fentiek kombinációja a hibák és az upstream függőség – a fizetési szolgáltatás – elérhetetlensége esetén történő újrapróbálkozásokkal egy sokkal rugalmasabb kialakítást eredményezhet. Például a Kosár és a Fizetési szolgáltatások közötti szinkron integráció esemény- vagy kötegelt újrapróbálkozással támogatható meghibásodás esetén. Ez a megközelítés további hatással van az ügyfélélményre – előfordulhat, hogy az ügyfelek hibás fizetési adatokat adtak meg, és nem lesz online, amikor offline feldolgozzuk a fizetéseket. Vagy a vállalkozásnak többletköltséget jelenthet a sikertelen fizetések visszakövetelése. De minden valószínűség szerint a Cart szolgáltatásnak a fizetési szolgáltatás elérhetetlenségével vagy hibáival szembeni ellenálló képességéből fakadó előnyei felülmúlják a hiányosságokat. Például értesíthetjük az ügyfeleket, ha nem tudjuk offline beszedni a fizetéseket. Röviden, vannak kompromisszumok a felhasználói élmény, a rugalmasság és az üzemeltetési költségek között, és bölcs dolog a rendszereket úgy tervezni, hogy ezeket a kompromisszumokat szem előtt tartjuk.

Kerüljük a szolgáltatások közötti hangszerelést a fogyasztó-specifikus adatigényekhez

A szolgáltatásorientált architektúrák egyik antimintája, hogy a szolgáltatások a fogyasztók speciális hozzáférési mintáinak megfelelően gondoskodnak. Általában ez akkor történik, amikor a fogyasztói csapatok szorosan együttműködnek a szolgáltatási csapatokkal. Ha a csapat egy monolitikus alkalmazáson dolgozik, gyakran egyetlen API-t hoznak létre, amely átlépi a különböző aggregátumok határait, ezáltal szorosan összekapcsolva ezeket az aggregátumokat. Nézzünk egy példát. Tegyük fel, hogy a webes és a mobilalkalmazásokban a Megrendelés részletei oldalnak egyetlen oldalon kell megjelenítenie mind a Megrendelés részleteit, mind a megrendeléshez feldolgozott visszatérítések részleteit. Egy monolitikus alkalmazásban a Megrendelés GET API – feltételezve, hogy REST API – együttesen lekérdezi a Megrendeléseket és a visszatérítéseket, összevonja mindkét aggregátumot, és egy összetett választ küld a hívóknak. Ez nagy rezsiköltség nélkül lehetséges, mivel az aggregátumok ugyanahhoz a folyamathatárhoz tartoznak. Így a fogyasztók egyetlen hívással megkaphatják az összes szükséges adatot.

Ha a Megrendelések és a Visszatérítések különböző kontextusokhoz tartoznak, az adatok már nem egyetlen mikroszolgáltatáson vagy aggregátumhatáron belül vannak jelen. Az egyik lehetőség, hogy ugyanaz a funkcionalitás megmaradjon a fogyasztók számára, ha a Rendelés szolgáltatást felelőssé tesszük a Visszatérítés szolgáltatás meghívásáért, és egy összetett választ hozunk létre. Ez a megközelítés számos aggályt vet fel:

1. A Megrendelés szolgáltatás most egy másik szolgáltatással integrálódik, pusztán a fogyasztók támogatására, akiknek a Visszatérítési adatokra a Megrendelés adatokkal együtt van szükségük. A Rendelés szolgáltatás most már kevésbé autonóm, mivel a Visszatérítési aggregátumban bekövetkező bármilyen változás a Rendelés aggregátum változásához vezet.

2. A Rendelés szolgáltatás egy másik integrációval rendelkezik, és így egy másik hibapontot is figyelembe kell venni – ha a Visszatérítési szolgáltatás nem működik, a Rendelés szolgáltatás továbbra is tud részleges adatokat küldeni, és a fogyasztók kíméletesen leállhatnak?

3. Ha a fogyasztóknak változásra van szükségük, hogy több adatot kérjenek a Visszatérítési aggregátumból, most két csapat vesz részt a változtatás végrehajtásában

4. Ez a minta, ha az egész platformon követik, a különböző tartományi szolgáltatások közötti függőségek bonyolult hálójához vezethet, mindezt azért, mert ezek a szolgáltatások a hívók sajátos hozzáférési mintáinak megfelelnek.

Backend for Frontends (BFFs)

Egy megközelítés e kockázat csökkentésére az, hogy a fogyasztói csapatok kezelik a különböző tartományi szolgáltatások közötti összehangolást. Végül is a hívók jobban ismerik a hozzáférési mintákat, és teljes mértékben ők irányíthatják e minták bármilyen változását. Ez a megközelítés leválasztja a tartományi szolgáltatásokat a prezentációs szintről, így azok az alapvető üzleti folyamatokra koncentrálhatnak. Ha azonban a webes és mobilalkalmazások a monolitból származó egyetlen összetett API helyett közvetlenül különböző szolgáltatásokat kezdenek hívni, az teljesítménybeli túlterhelést okozhat ezeknek az alkalmazásoknak – többszörös hívások alacsonyabb sávszélességű hálózatokon, a különböző API-kból származó adatok feldolgozása és összevonása stb.

Ehelyett használhatunk egy másik mintát, a Backend for Front-ends nevű mintát. Ebben a tervezési mintában a fogyasztók – ebben az esetben a webes és mobilcsapatok – által létrehozott és kezelt backend szolgáltatás gondoskodik a több tartományi szolgáltatáson keresztüli integrációról, pusztán azért, hogy a front-end élményt nyújtsa az ügyfeleknek. A webes és mobilcsapatok mostantól az általuk kiszolgált felhasználási esetek alapján tervezhetik meg az adatszerződéseket. REST API-k helyett akár GraphQL-t is használhatnak, hogy rugalmasan lekérdezhessék és pontosan azt kapják vissza, amire szükségük van. Fontos megjegyezni, hogy ezt a szolgáltatást a fogyasztói csapatok birtokolják és tartják fenn, nem pedig a tartományi szolgáltatásokat birtokló csapatok. A front-end csapatok mostantól az igényeik alapján optimalizálhatnak – egy mobilalkalmazás kisebb hasznos terhet kérhet, csökkentheti a mobilalkalmazás hívásainak számát, és így tovább. Tekintse meg az alábbiakban az orchestráció felülvizsgált nézetét. A BFF szolgáltatás most már mind a Megrendelések, mind a Visszatérítések tartományi szolgáltatásokat meghívja a használati esethez.

Fig 9. Backend for Frontends

A BFF szolgáltatást is célszerű korán felépíteni, mielőtt szolgáltatások sokaságát törjük ki a monolitból. Ellenkező esetben vagy a tartományi szolgáltatásoknak kell támogatniuk a tartományok közötti hangszerelést, vagy a webes és mobilalkalmazásoknak több szolgáltatást kell közvetlenül a frontendből hívniuk. Mindkét lehetőség teljesítménytöbblethez, eldobott munkához és a csapatok közötti autonómia hiányához vezet.

Következtetés

Ebben a blogban különböző fogalmakat, stratégiákat és tervezési heurisztikákat érintettünk, amelyeket figyelembe kell vennünk, amikor a mikroszolgáltatások világába merészkedünk, pontosabban amikor egy monolitot több tartományalapú mikroszolgáltatásra próbálunk bontani. Ezek közül sok önmagában is hatalmas téma, és azt hiszem, nem tettünk eleget annak, hogy teljes részletességgel elmagyarázzuk őket, de szerettünk volna bemutatni néhány kritikus témát és az ezek elfogadásával kapcsolatos tapasztalatainkat. A Further Reading (link) részben találunk néhány hivatkozást és hasznos tartalmat mindazok számára, akik ezen az úton szeretnének haladni.

Frissítés: A sorozat következő két blogja megjelent. Ez a két blog a Cart mikroszolgáltatás megvalósítását tárgyalja, kódpéldákkal, a Domain-Driven Design elvek, valamint a Ports and Adapters tervezési minták felhasználásával. E blogok elsődleges célja annak bemutatása, hogy ez a két elv/mintázat hogyan segít nekünk olyan moduláris alkalmazások építésében, amelyek agilisak, tesztelhetőek és refaktorálhatóak – egyszóval képesek reagálni arra a gyorsan változó környezetre, amelyben mindannyian működünk.

Implementing Cart Microservice using Domain Driven Design and Ports and Adapters Pattern – Part 1

Implementing Cart Microservice using Domain Driven Design and Ports and Adapters Pattern – Part 2

Further Reading

1. Eric Evans: Domain Driven Design

2. Vaughn Vernon: Implementing Domain Driven Design

3. Martin Fowler cikke a mikroszolgáltatásokról

4. Sam Newman: Building Microservices

5. Event storming

7. Backend for Frontends

8. Az elosztott számítástechnika tévedései

.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.