Útmutató az Axon keretrendszerhez

1. Áttekintés

Ebben a cikkben megnézzük Axon és hogyan segít az alkalmazások megvalósításában CQRS (Parancslekérdezés-felelősség szegregációja) és Események beszerzése gondolatban.

A kézikönyv során mind az Axon Framework, mind az Axon Server felhasználásra kerül. Az előbbi a megvalósításunkat fogja tartalmazni, az utóbbi pedig a dedikált Event Store és Message Routing megoldásunk lesz.

Az általunk építeni kívánt minta alkalmazás egy Rendelés tartomány. Ezért, kihasználjuk a CQRS-t és az Event Sourcing építőköveit, amelyeket az Axon biztosít számunkra.

Ne feledje, hogy a megosztott fogalmak közül sokan éppen ezekből származnak DDD, amely meghaladja e jelenlegi cikk kereteit.

2. Maven-függőségek

Létrehozunk egy Axon / Spring Boot alkalmazást. Ezért hozzá kell adnunk a legfrissebbet axon-spring-boot-starter függőség a mi pom.xml, valamint a axon-teszt a tesztelés függősége:

 org.axonframework axon-spring-boot-starter 4.1.2 org.axonframework axon-test 4.1.2 test 

3. Axon Server

Az Axon Server szolgáltatást fogjuk használni Event Store-ként, és dedikált parancs-, esemény- és lekérdezési útválasztó megoldásként.

Rendezvények áruházaként megadja nekünk az események tárolásához szükséges ideális jellemzőket. Ez a cikk bemutatja, miért kívánatos ez.

Üzenetirányítási megoldásként lehetőséget ad arra, hogy több példányt összekapcsoljunk anélkül, hogy az üzenetek megosztására és elküldésére összpontosítanánk, például egy RabbitMQ vagy egy Kafka témát.

Az Axon Server itt letölthető. Mivel egyszerű JAR fájlról van szó, a következő művelet elegendő az indításhoz:

java -jar axonserver.jar

Ez elindít egyetlen Axon Server példányt, amely elérhető helyi gazda: 8024. A végpont áttekintést nyújt a csatlakoztatott alkalmazásokról és az általuk kezelhető üzenetekről, valamint egy lekérdezési mechanizmust nyújt az Axon Serverben található Event Store felé.

Az Axon Server alapértelmezett konfigurációja a axon-spring-boot-starter a függőség biztosítja, hogy Rendelési szolgáltatásunk automatikusan csatlakozzon hozzá.

4. Order Service API - Parancsok

A CQRS-t szem előtt tartva állítjuk be Rendelési szolgáltatásunkat. Ezért kiemeljük az alkalmazásunkon átáramló üzeneteket.

Először meghatározzuk a Parancsokat, vagyis a szándék kifejezését. A Order szolgáltatás három különböző típusú művelet kezelésére képes:

  1. Új megrendelés leadása
  2. Rendelés megerősítése
  3. Rendelés szállítása

Természetesen három parancsüzenet lesz, amellyel tartományunk foglalkozni tud - PlaceOrderCommand, ConfirmOrderCommand, és ShipOrderCommand:

public class PlaceOrderCommand {@TargetAggregateIdentifier private final String orderId; saját végső String termék; // konstruktor, getters, equals / hashCode és toString} public class ConfirmOrderCommand {@TargetAggregateIdentifier private final String orderId; // konstruktor, getters, equals / hashCode és toString} public class ShipOrderCommand {@TargetAggregateIdentifier private final String orderId; // konstruktor, getters, egyenlő / hashCode és toString}

A TargetAggregateIdentifier az annotáció azt mondja az Axonnak, hogy az annotált mező egy adott aggregátum azonosítója, amelyre a parancsot meg kell célozni. A cikk későbbi részében röviden kitérünk az összesítésekre.

Ezenkívül vegye figyelembe, hogy a parancsokban szereplő mezőket végső. Ez szándékos, mint ez a legjobb gyakorlat Bármi üzenet megvalósítása megváltoztathatatlan.

5. Order Service API - Események

Összesítésünk kezeli a parancsokat, mivel annak feladata annak eldöntése, hogy megrendelés leadható-e, visszaigazolható-e vagy elküldhető-e.

Esemény közzétételével értesíti határozatának alkalmazásának többi részét. Háromféle rendezvényünk lesz - OrderPlacedEvent, OrderConfirmedEvent, és OrderShippedEvent:

public class OrderPlacedEvent {private final String orderId; saját végső String termék; // alapértelmezett konstruktor, getters, equals / hashCode és toString} public class OrderConfirmedEvent {private final String orderId; // alapértelmezett konstruktor, getters, equals / hashCode és toString} public class OrderShippedEvent {private final String orderId; // alapértelmezett konstruktor, getters, equals / hashCode és toString}

6. A parancsnoki modell - Rend összesítése

Most, hogy a parancsok és események vonatkozásában modelleztük az alapvető API-t, elkezdhetjük a Parancsmodell létrehozását.

Mivel domainünk a megrendelések kezelésére összpontosít, létrehozunk egy OrderAggregate mint parancsnoki modellünk központja.

6.1. Összesített osztály

Így hozzuk létre az alap összesített osztályunkat:

@Aggregate public class OrderAggregate {@AggregateIdentifier private String orderId; privát logikai rend Megerősítve; @CommandHandler public OrderAggregate (PlaceOrderCommand parancs) {AggregateLifecycle.apply (új OrderPlacedEvent (command.getOrderId (), command.getProduct ())); } @EventSourcingHandler public void on (OrderPlacedEvent event) {this.orderId = event.getOrderId (); orderConfirmed = hamis; } védett OrderAggregate () {}}

A Összesített az annotáció egy Axon Spring-specifikus feljegyzés, amely ezt az osztályt aggregátumként jelöli. Értesíteni fogja a keretrendszert, hogy ehhez elő kell állítani a szükséges CQRS-t és az esemény-beszerzési specifikus építőelemeket OrderAggregate.

Mivel egy aggregátum egy adott összesített példányra irányított parancsokat kezeli, meg kell adnunk az azonosítót a AggregateIdentifier annotáció.

Összességünk életciklusát a PlaceOrderCommand ban,-ben OrderAggregate ’Parancskezelő konstruktor’. Hozzáadjuk a keretrendszert, hogy az adott függvény képes kezelni a parancsokat CommandHandler annotáció.

A PlaceOrderCommand, értesíti az alkalmazás többi részét, hogy megrendelést adtak le a OrderPlacedEvent. Egy esemény összesített közzétételéhez felhasználjuk AggregateLifecycle # Apply (objektum…).

Ettől a ponttól kezdve tulajdonképpen elkezdhetjük beépíteni az Események beszerzését, mint mozgatórugót az összesített példány újbóli létrehozására az események áramlatából.

Ezt az „összesített létrehozási eseményről”, a OrderPlacedEvent, amelyet egy EventSourcingHandler annotált függvény a Rendelés azonosító és Rendelés Megerősítve a Rend összesítésének állapota.

Vegye figyelembe azt is, hogy az események alapján összesítéshez az Axon alapértelmezett konstruktort igényel.

6.2. Összesített parancsnokkezelők

Most, hogy megvan az alapvető összesítés, elkezdhetjük a többi parancskezelő implementálását:

@CommandHandler public void hand (ConfirmOrderCommand parancs) {Apply (új OrderConfirmedEvent (orderId)); } @CommandHandler public void hand (ShipOrderCommand parancs) {if (! OrderConfirmed) {dobj új UnconfirmedOrderException (); } Apply (új OrderShippedEvent (orderId)); } @EventSourcingHandler public void on (OrderConfirmedEvent event) {orderConfirmed = true; }

Parancsnokságunk és eseményforrás-kezelőink aláírása egyszerűen kimondja fogantyú ({a-parancs}) és bekapcsolva ({the-event}) tömör formátum fenntartása érdekében.

Ezenkívül meghatároztuk, hogy a megrendeléseket csak megerősítésük esetén lehet feladni. Így dobunk egy UnconfirmedOrderException ha ez nem így van.

Ez példázza a OrderConfirmedEvent beszerzés kezelője a Rendelés Megerősítve államnak igaz a Rendelés összesítésére.

7. A parancsmodell tesztelése

Először fel kell állítanunk tesztünket a FixtureConfiguration a OrderAggregate:

privát FixtureConfiguration készülék; @ Mielőtt nyilvános void setUp () {fixture = new AggregateTestFixture (OrderAggregate.class); }

Az első tesztesetnek a legegyszerűbb helyzetre kell kiterjednie. Amikor az aggregátum kezeli a PlaceOrderCommand, meg kell termelnie egy OrderPlacedEvent:

String orderId = UUID.randomUUID (). ToString (); Húrtermék = "Deluxe szék"; fixture.givenNoPriorActivity () .when (új PlaceOrderCommand (orderId, termék)) .expectEvents (új OrderPlacedEvent (orderId, termék));

Ezután kipróbálhatjuk a döntéshozatali logikát, miszerint csak akkor tudjuk megrendelést szállítani, ha azt megerősítették. Ennek köszönhetően két forgatókönyvünk van: az egyik, ahol kivételt várunk, és egy, ahol egy OrderShippedEvent.

Vessünk egy pillantást az első forgatókönyvre, ahol kivételt várunk:

String orderId = UUID.randomUUID (). ToString (); Húrtermék = "Deluxe szék"; fixture.given (új OrderPlacedEvent (orderId, termék)) .when (új ShipOrderCommand (orderId)) .expectException (IllegalStateException.class); 

És most a második forgatókönyv, ahol egy OrderShippedEvent:

String orderId = UUID.randomUUID (). ToString (); Húrtermék = "Deluxe szék"; fixture.given (új OrderPlacedEvent (orderId, termék), új OrderConfirmedEvent (orderId)) .when (új ShipOrderCommand (orderId)) .expectEvents (új OrderShippedEvent (orderId));

8. A lekérdezési modell - eseménykezelők

Eddig létrehoztuk az alapvető API-t a parancsokkal és eseményekkel, és a helyén van a CQRS Order szolgáltatásunk Parancs modellje, a Order aggregate.

Következő, elkezdhetünk gondolkodni az egyik Query modellen, amelyet alkalmazásunknak ki kell szolgálnia.

Ezen modellek egyike a Rendelt termékek:

public class OrderedProduct {private final String orderId; saját végső String termék; privát OrderStatus orderStatus; public OrderedProduct (String orderId, String termék) {this.orderId = orderId; this.termék = termék; orderStatus = OrderStatus.PLACED; } public void setOrderConfirmed () {this.orderStatus = OrderStatus.CONFIRMED; } public void setOrderShipped () {this.orderStatus = OrderStatus.SHIPPED; } // getters, equals / hashCode és toString függvények} public enum OrderStatus {PLACED, CONFIRMED, SHIPPED}

Ezt a modellt a rendszerünkön keresztül terjedő események alapján frissítjük. Egy tavasz Szolgáltatás Bean a modellünk frissítéséhez megteszi a trükköt:

@Service public class OrderedProductsEventHandler {privát végleges Térkép rendezettProducts = új HashMap (); @EventHandler public void itt: (OrderPlacedEvent esemény) {String orderId = event.getOrderId (); rendeltProducts.put (orderId, új OrderedProduct (orderId, event.getProduct ())); } // Eseménykezelők a OrderConfirmedEvent és a OrderShippedEvent ...}

Ahogy használtuk a axon-spring-boot-starter Az Axon alkalmazás elindításától függően a keretrendszer automatikusan átvizsgálja az összes babot a meglévő üzenetkezelő funkciók szempontjából.

Mivel a OrderedProductsEventHandler van Eseménykezelő kommentált funkciók egy Rendelt termék és frissítse, ezt a babot a keretrendszer osztályként regisztrálja, amelynek eseményeket kell fogadnia anélkül, hogy részünkről bármilyen konfigurációt igényelnénk.

9. A lekérdezés modell - Lekérdezés kezelők

Ezután a modell lekérdezéséhez, például az összes megrendelt termék letöltéséhez először be kell vezetnünk egy Query üzenetet az alapvető API-ba:

public class FindAllOrderedProductsQuery {}

Másodszor, frissítenünk kell a OrderedProductsEventHandler hogy kezelni tudja a FindAllOrderedProductsQuery:

@QueryHandler public List kezelése (FindAllOrderedProductsQuery lekérdezés) {return new ArrayList (rendezettProducts.values ​​()); }

A QueryHandler kommentált függvény kezeli a FindAllOrderedProductsQuery és vissza van állítva a Lista ettől függetlenül, hasonlóan bármelyik „mindent megtalál” lekérdezéshez.

10. Mindent összerakni

Kiegészítettük az alapvető API-t parancsokkal, eseményekkel és lekérdezésekkel, és beállítottuk a Command and Query modellünket egy OrderAggregate és Rendelt termékek modell.

A következő az infrastruktúra laza végeinek lekötése. Ahogy használjuk a axon-spring-boot-starter, ez sok szükséges beállítást automatikusan beállít.

Első, mivel az Események beszerzését szeretnénk felhasználni összesítettünk számára, szükségünk lesz egy EventStore. Az Axon Server, amelyet a harmadik lépésben indítottunk el, kitölti ezt a lyukat.

Másodszor, egy mechanizmusra van szükségünk a tároláshoz Rendelt termék lekérdezési modell. Ehhez a példához hozzáadhatjuk h2 mint memóriában lévő adatbázis és spring-boot-starter-data-jpa a könnyű használat érdekében:

 org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 futásidejű 

10.1. REST végpont beállítása

Ezután hozzáférnünk kell alkalmazásunkhoz, amelyhez a REST végpontot fogjuk kihasználni a tavasz-boot-starter-web függőség:

 org.springframework.boot spring-boot-starter-web 

A REST végpontunkról megkezdhetjük a parancsok és lekérdezések elküldését:

@RestController public class OrderRestEndpoint {private final CommandGateway commandGateway; privát végső QueryGateway queryGateway; // Konstruktor és POST / GET végpontok automatikus letöltése}

A CommandGateway a parancsüzenetek küldésének mechanizmusaként használatos, és a QueryGatewayviszont lekérdező üzenetek küldésére. Az átjárók egyszerűbb, egyszerűbb API-t biztosítanak, a CommandBus és QueryBus hogy kapcsolatba lépnek.

Innentől kezdve a mi OrderRestEndpoint rendelkeznie kell egy POST végponttal a megrendelés leadásához, megerősítéséhez és elküldéséhez:

@PostMapping ("/ ship-order") public void shipOrder () {String orderId = UUID.randomUUID (). ToString (); commandGateway.send (új PlaceOrderCommand (orderId, "Deluxe Chair")); commandGateway.send (új ConfirmOrderCommand (orderId)); commandGateway.send (új ShipOrderCommand (orderId)); }

Ez kerekíti a CQRS alkalmazás Command oldalát.

Most már csak egy GET végpont van hátra, amellyel lekérdezheti az összeset Rendelt termékek:

@GetMapping ("/ all-order") public list findAllOrderedProducts () {return queryGateway.query (new FindAllOrderedProductsQuery (), ResponseTypes.multipleInstancesOf (OrderedProduct.class)). Join (); }

A GET végpontban kihasználjuk a QueryGateway pont-pont lekérdezés elküldéséhez. Ennek során létrehozunk egy alapértelmezettet FindAllOrderedProductsQuery, de meg kell adnunk a várható hozamtípust is.

Ahogy többre számítunk Rendelt termék a visszaküldendő példányok, akkor a statikusat kihasználjuk ResponseTypes # multipleInstancesOf (Osztály) funkció. Ezzel alapbejáratot biztosítottunk a Rendelés szolgáltatás Query oldalán.

Befejeztük a telepítést, így most néhány parancsot és kérdést elküldhetünk a REST vezérlőn keresztül, miután elindítottuk a OrderApplication.

POST-végzés a végpontig / ship-order példányosítani fog egy OrderAggregate amelyek eseményeket tesznek közzé, amelyek viszont megmentik / frissítik az oldalunkat Rendelt termékek. Kapható a / minden megrendelés A végpont közzétesz egy lekérdező üzenetet, amelyet a OrderedProductsEventHandler, amely visszaadja az összes létezőt Rendelt termékek.

11. Következtetés

Ebben a cikkben bemutattuk az Axon keretrendszert, amely hatékony alap a CQRS és az Event Sourcing előnyeit kihasználó alkalmazás kiépítéséhez.

Egy egyszerű Order szolgáltatást vezettünk be a keretrendszer segítségével, hogy megmutassuk, hogyan kell egy ilyen alkalmazást felépíteni a gyakorlatban.

Végül az Axon Server az Event Store-ként és az üzenet továbbítási mechanizmusként jelent meg.

Ezen példák és kódrészletek megvalósítása megtalálható a GitHub-on.

Ha bármilyen további kérdése van, keresse fel az Axon Framework felhasználói csoportot is.