Megosztás a következőn keresztül:


CQRS-minta

A Command Query Responsibility Szegregáció (CQRS) egy olyan tervezési minta, amely elkülöníti az adattár olvasási és írási műveleteit külön adatmodellekre. Ez a megközelítés lehetővé teszi az egyes modellek egymástól függetlenül történő optimalizálását, és javíthatja az alkalmazások teljesítményét, méretezhetőségét és biztonságát.

Környezet és probléma

A hagyományos architektúrákban gyakran egyetlen adatmodellt használnak olvasási és írási műveletekhez is. Ez a megközelítés egyszerű, és alkalmas alapszintű létrehozási, olvasási, frissítési és törlési (CRUD) műveletekhez.

Hagyományos CRUD-architektúrát bemutató diagram.

Az alkalmazások növekedésének következtében egyre nehezebbé válik az olvasási és írási műveletek optimalizálása egyetlen adatmodellen. Az olvasási és írási műveletek gyakran eltérő teljesítménnyel és méretezési követelményekkel rendelkeznek. A hagyományos CRUD-architektúra nem veszi figyelembe ezt az aszimmetriát, ami a következő kihívásokat eredményezheti:

  • Adateltérés: Az adatok olvasási és írási ábrázolása gyakran eltér. A frissítések során szükséges mezők némelyike szükségtelen lehet az olvasási műveletek során.

  • Zárolási versengés: ugyanazon adatkészlet párhuzamos műveletei zárolási versengést okozhatnak.

  • Teljesítményproblémák: A hagyományos megközelítés negatív hatással lehet a teljesítményre az adattárra és az adatelérési rétegre gyakorolt terhelés, valamint az információk lekéréséhez szükséges lekérdezések összetettsége miatt.

  • Biztonsági kihívások: Nehéz lehet kezelni a biztonságot, ha az entitások olvasási és írási műveleteknek vannak kitéve. Ez az átfedés nem szándékos környezetekben teheti elérhetővé az adatokat.

A felelősségek kombinálása túl bonyolult modellt eredményezhet.

Megoldás

A CQRS-minta használatával elválaszthatja az írási műveleteket vagy parancsokat az olvasási műveletektől vagy lekérdezésektől. A parancsok frissítik az adatokat. A lekérdezések adatokat kérnek le. A CQRS-minta olyan helyzetekben hasznos, amelyek a parancsok és az olvasások egyértelmű elkülönítését igénylik.

  • A parancsok ismertetése. A parancsok az alacsony szintű adatfrissítések helyett konkrét üzleti feladatokat jelölnek. Egy szállodai foglalási alkalmazásban például használja a "Foglalási szoba foglalása" parancsot a "ReservationStatus beállítása fenntartottra" helyett. Ez a megközelítés jobban rögzíti a felhasználó szándékát, és összehangolja a parancsokat az üzleti folyamatokkal. A parancsok sikerességének biztosításához finomítania kell a felhasználói interakciós folyamatot és a kiszolgálóoldali logikát, és fontolóra kell vennie az aszinkron feldolgozást.

    Finomítási terület Ajánlás
    Ügyféloldali ellenőrzés A parancs elküldése előtt ellenőrizze a konkrét feltételeket a nyilvánvaló hibák elkerülése érdekében. Ha például nincs elérhető szoba, tiltsa le a "Könyv" gombot, és adjon meg egy egyértelmű, felhasználóbarát üzenetet a felhasználói felületen, amely elmagyarázza, miért nem lehetséges a foglalás. Ez a beállítás csökkenti a szükségtelen kiszolgálói kéréseket, és azonnali visszajelzést nyújt a felhasználóknak, ami javítja a felhasználói élményt.
    Kiszolgálóoldali logika Az üzleti logika továbbfejlesztése a szélsőséges esetek és hibák sikeres kezelése érdekében. Ha például a versenyhelyzeteket szeretné kezelni, például ha több felhasználó megkísérel lefoglalni az utolsó szabad szobát, érdemes lehet felhasználókat felvenni egy várólistára, vagy alternatív lehetőségeket javasolni.
    Aszinkron feldolgozás A parancsok aszinkron feldolgozásához helyezze őket egy sorba, ahelyett, hogy szinkron módon kezelné őket.
  • A lekérdezések ismertetése. A lekérdezések soha nem módosítják az adatokat. Ehelyett olyan adatátviteli objektumokat (DTO-kat) adnak vissza, amelyek a szükséges adatokat kényelmes formátumban, tartománylogika nélkül mutatják be. A felelősségek e különálló elkülönítése leegyszerűsíti a rendszer kialakítását és megvalósítását.

Olvasási modellek és írási modellek elkülönítése

Az olvasási modell és az írási modell elválasztása leegyszerűsíti a rendszer tervezését és implementálását azáltal, hogy az adatírással és az adatolvasással kapcsolatos konkrét problémákat kezeli. Ez az elkülönítés javítja az egyértelműséget, a méretezhetőséget és a teljesítményt, de kompromisszumokat vezet be. Az állványzateszközök, például az objektum-relációs leképezési (O/RM) keretrendszerek például nem tudnak automatikusan CQRS-kódot generálni egy adatbázisséma alapján, ezért egyéni logikára van szükség a rés áthidalásához.

Az alábbi szakaszok az olvasási modell és az írási modell elkülönítésének CQRS-ben történő implementálásának két elsődleges megközelítését ismertetik. Minden megközelítés egyedi előnyökkel és kihívásokkal rendelkezik, például a szinkronizálással és a konzisztenciakezeléssel.

Modellek elkülönítése egyetlen adattárban

Ez a megközelítés a CQRS alapszintű szintjét jelenti, ahol az olvasási és írási modellek egyetlen mögöttes adatbázissal osztoznak, de a műveleteikhez eltérő logikát tartanak fenn. Az egyszerű CQRS-architektúra lehetővé teszi az írási modell lehatárolását az olvasási modellből, miközben egy megosztott adattárra támaszkodik.

Alapszintű CQRS-architektúrát bemutató diagram.

Ez a megközelítés az olvasási és írási problémák kezelésére szolgáló különböző modellek meghatározásával javítja az egyértelműséget, a teljesítményt és a méretezhetőséget.

  • Az írási modell úgy lett kialakítva, hogy kezelje az adatokat frissítő vagy megőrző parancsokat. Magában foglalja az érvényesítést és a tartománylogikát, és segít biztosítani az adatkonzisztenciát a tranzakciós integritás és az üzleti folyamatok optimalizálásával.

  • Az olvasási modell úgy lett kialakítva, hogy lekérdezéseket szolgáljon ki az adatok lekéréséhez. A bemutató réteghez optimalizált DTO-k vagy kivetítések létrehozására összpontosít. A tartománylogika elkerülésével javítja a lekérdezési teljesítményt és a válaszkészséget.

Modellek elkülönítése különböző adattárakban

Egy fejlettebb CQRS-implementáció különböző adattárakat használ az olvasási és írási modellekhez. Az olvasási és írási adattárak elkülönítése lehetővé teszi az egyes modellek méretezését a terhelésnek megfelelően. Emellett lehetővé teszi, hogy minden adattárhoz más tárolási technológiát használjon. Használhat dokumentum-adatbázist az olvasási adattárhoz és egy relációs adatbázist az írási adattárhoz.

Diagram, amely egy CQRS-architektúrát jelenít meg külön olvasási adattárakkal és írási adattárakkal.

Ha külön adattárakat használ, győződjön meg arról, hogy mindkettő szinkronizálva marad. Gyakori példa, hogy az írási modell eseményeket tesz közzé az adatbázis frissítésekor, amelyeket az olvasási modell az adatok frissítéséhez használ. Az események használatáról további információt az eseményvezérelt architektúrastílusban talál. Mivel az üzenetközvetítőket és -adatbázisokat általában nem tudja egyetlen elosztott tranzakcióba bevonni, az adatbázis frissítésekor és az események közzétételekor konzisztenciával kapcsolatos problémák léphetnek fel. További információ: Idempotens üzenetfeldolgozási.

Az olvasási adattár használhatja a lekérdezésekhez optimalizált saját adatsémát. Tárolhat például egy materializált nézetet az adatok az összetett illesztések és az O/RM-leképezések elkerülése érdekében. Az olvasási adattár lehet az írási tár írásvédett replikája, vagy más struktúrával rendelkezik. Több írásvédett replika üzembe helyezése javíthatja a teljesítményt a késés csökkentésével és a rendelkezésre állás növelésével, különösen az elosztott forgatókönyvekben.

A CQRS előnyei

  • Független skálázás. A CQRS lehetővé teszi, hogy az olvasási és írási modellek egymástól függetlenül skálázhatók. Ez a megközelítés segít minimalizálni a zárolási versengést, és javítani a rendszer teljesítményét terhelés alatt.

  • Optimalizált adatséma. Az olvasási műveletek lekérdezésekhez optimalizált sémát használhatnak. Az írási műveletek frissítésekre optimalizált sémát használnak.

  • Biztonság. Az olvasások és írások elkülönítésével biztosíthatja, hogy csak a megfelelő tartományi entitások vagy műveletek rendelkezzenek engedéllyel az adatok írási műveleteinek végrehajtásához.

  • Az aggodalmak elkülönítése. Az olvasási és írási feladatok elkülönítése tisztább, karbantarthatóbb modelleket eredményez. Az írási oldal általában összetett üzleti logikát kezel. Az olvasási oldal továbbra is egyszerű marad, és a lekérdezési hatékonyságra összpontosít.

  • Egyszerűbb lekérdezések. Ha materializált nézetet tárol az olvasási adatbázisban, az alkalmazás elkerülheti az összetett illesztéseket a lekérdezések során.

Problémák és szempontok

Vegye figyelembe a következő szempontokat, amikor úgy dönt, hogy hogyan valósítja meg ezt a mintát:

  • Nagyobb összetettség. A CQRS alapkoncepciója egyszerű, de jelentősen összetettebbé teheti az alkalmazás kialakítását, különösen az Event Sourcing mintával kombinálva.

  • Üzenetkezelési kihívások. Az üzenetkezelés nem követelmény a CQRS-hez, de gyakran használja parancsok feldolgozására és frissítési események közzétételére. Amikor az üzenetküldés szerepel, a rendszernek figyelembe kell vennie a lehetséges problémákat, például az üzenethibákat, az ismétlődéseket és az újrapróbálkozásokat. A különböző prioritású parancsok kezelésére szolgáló stratégiákról további információt a Prioritási üzenetsorok című témakörben talál.

  • Végleges konzisztencia. Ha az olvasási adatbázisok és az írási adatbázisok külön vannak választva, előfordulhat, hogy az olvasási adatok nem jelennek meg azonnal a legutóbbi módosítások. Ez a késés elavult adatokat eredményez. Kihívást jelenthet annak biztosítása, hogy az olvasási modell tárolója up-to-date maradjon az írási modell tárolójának változásaival. Emellett az olyan forgatókönyvek észlelése és kezelése, ahol a felhasználó elavult adatokon jár el, gondos megfontolást igényel.

Mikor érdemes használni ezt a mintát?

Használja ezt a mintát, ha:

  • Együttműködésen alapuló környezetben dolgozik. Olyan környezetekben, ahol egyszerre több felhasználó fér hozzá és módosítja ugyanazokat az adatokat, a CQRS segít csökkenteni az egyesítési ütközéseket. A parancsok elég részletességet tartalmazhatnak az ütközések megelőzéséhez, és a rendszer képes feloldani a parancslogikán belül előforduló ütközéseket.

  • Feladatalapú felhasználói felületei vannak. Azok az alkalmazások, amelyek lépéssorozatként vagy összetett tartománymodellekkel végigvezetik a felhasználókat az összetett folyamatokon, a CQRS előnyeit élvezhetik.

    • Az írási modell teljes körű parancsfeldolgozási vermet tartalmaz üzleti logikával, bemenet-ellenőrzéssel és üzleti ellenőrzéssel. Az írási modell egyetlen egységként kezelhet egy társított objektumhalmazt az adatváltozásokhoz, amelyet a tartományalapú tervezési terminológiában összesítésnek neveznek. Az írási modell azt is segítheti, hogy ezek az objektumok mindig konzisztens állapotban legyenek.

    • Az olvasási modell nem tartalmaz üzleti logikát és nincs érvényesítési stackje. Egy nézetmodellben használható DTO-t ad vissza. Az olvasási modell véglegesen konzisztens az írási modellel.

  • Teljesítményhangolásra van szükség. Azok a rendszerek, ahol az adatolvasások teljesítményét az adatírás teljesítményétől elkülönítve kell finomhangolni, a CQRS előnye. Ez a minta különösen akkor hasznos, ha az olvasások száma nagyobb, mint az írások száma. Az olvasási modell vízszintesen skálázva kezeli a nagy lekérdezésköteteket. Az írási modell kevesebb példányon fut az egyesítési ütközések minimalizálása és a konzisztencia fenntartása érdekében.

  • A fejlesztési szempontok elkülönítése megtörtént. A CQRS lehetővé teszi, hogy a csapatok egymástól függetlenül dolgozzanak. Az egyik csapat implementálja az összetett üzleti logikát az írási modellben, egy másik csapat pedig az olvasási modell és a felhasználói felület összetevőit fejleszti.

  • Folyamatosan fejlődő rendszerekkel rendelkezik. A CQRS támogatja azokat a rendszereket, amelyek idővel fejlődnek. Az új modellverziókat, az üzleti szabályok gyakori módosításait vagy más módosításokat tartalmazza a meglévő funkciók befolyásolása nélkül.

  • Rendszerintegrációra van szükség: A más alrendszerekkel integrálható rendszerek, különösen az Event Sourcing mintát használó rendszerek akkor is elérhetők maradnak, ha egy alrendszer átmenetileg meghibásodik. A CQRS elkülöníti a hibákat, ami megakadályozza, hogy egyetlen összetevő hatással van a teljes rendszerre.

Ez a minta nem feltétlenül megfelelő, ha:

  • Az üzleti terület vagy az üzleti szabályok egyszerűek.

  • Elegendő egy egyszerű CRUD-stílusú felhasználói felület és adatelérési művelet.

Munkaterhelés tervezés

Értékelje ki, hogyan használhatja a CQRS-mintát a számítási feladatok tervezésében az Azure Well-Architected-keretrendszer pilléreiben szereplő célok és alapelvek kezelésére. Az alábbi táblázat útmutatást nyújt arról, hogy ez a minta hogyan támogatja a Teljesítményhatékonyság pillér céljait.

Pillér Hogyan támogatja ez a minta a pillércélokat?
A teljesítményhatékonyság a skálázás, az adatok és a kód optimalizálásával segíti a számítási feladatok hatékony kielégítését. Az olvasási műveletek és az írási műveletek elkülönítése magas olvasási-írási számítási feladatokban célzott teljesítményt és skálázási optimalizálást tesz lehetővé az egyes műveletek konkrét céljához.

- PE:05 Skálázás és particionálás
- PE:08 Adatteljesítmény

Fontolja meg a más pillérek céljaival szembeni kompromisszumokat, amelyeket ez a minta bevezethet.

Az Event Sourcing és a CQRS minták kombinálása

A CQRS egyes implementációi tartalmazzák az Event Sourcing mintát. Ez a minta a rendszer állapotát időrendi eseménysorozatként tárolja. Minden esemény egy adott időpontban rögzíti az adatok módosításait. Az aktuális állapot meghatározásához a rendszer sorrendben játssza vissza ezeket az eseményeket. Ebben a beállításban:

  • Az eseménytároló az írási modell és az igazság egyetlen forrása.

  • Az olvasási modell ezekből az eseményekből materializált nézeteket hoz létre, általában erősen denormalizált formában. Ezek a nézetek úgy optimalizálják az adatlekérést, hogy a struktúrákat a lekérdezési és megjelenítési követelményekhez igazítják.

Az Event Sourcing és a CQRS-minták kombinálásának előnyei

Ugyanazok az események, amelyek frissítik az írási modellt, bemenetként szolgálhatnak az olvasási modellhez. Az olvasási modell ezután valós idejű pillanatképet készíthet az aktuális állapotról. Ezek a pillanatképek az adatok hatékony és előre összeállított nézeteinek biztosításával optimalizálják a lekérdezéseket.

Az aktuális állapot közvetlen tárolása helyett a rendszer egy eseményfolyamot használ írási tárolóként. Ez a megközelítés csökkenti az összesítések frissítési ütközéseit, és javítja a teljesítményt és a méretezhetőséget. A rendszer képes ezeket az eseményeket aszinkron módon feldolgozni az olvasási adattár materializált nézeteinek létrehozásához vagy frissítéséhez.

Mivel az eseménytároló az igazság egyetlen forrása, egyszerűen újragenerálhatja a materializált nézeteket, vagy alkalmazkodhat az olvasási modell változásaihoz a korábbi események ismétlésével. A materializált nézetek alapvetően tartós, írásvédett gyorsítótárként működnek, amely gyors és hatékony lekérdezésekhez van optimalizálva.

Megfontolandó szempontok az Event Sourcing és a CQRS-minták kombinálásához

Mielőtt kombinálja a CQRS-mintát az Event Sourcing mintával, értékelje ki a következő szempontokat:

  • Végleges konzisztencia: Mivel az írási és olvasási adattárak különállóak, előfordulhat, hogy az olvasási adattár frissítései elmaradnak az eseménygenerálástól. Ez a késés végleges konzisztenciát eredményez.

  • Nagyobb összetettség: A CQRS-minta és az Event Sourcing minta kombinálásához más tervezési megközelítésre van szükség, ami megnehezítheti a sikeres megvalósítást. Kódot kell írnia az események létrehozásához, feldolgozásához és kezeléséhez, valamint az olvasási modell nézeteinek összeállításához vagy frissítéséhez. Az Event Sourcing minta azonban leegyszerűsíti a tartománymodellezést, és lehetővé teszi az új nézetek egyszerű újraépítését vagy létrehozását az összes adatváltozás előzményeinek és szándékának megőrzésével.

  • Nézetlétrehozás teljesítménye: Az olvasási modell materializált nézeteinek létrehozása jelentős időt és erőforrásokat vehet igénybe. Ugyanez vonatkozik az adatok kivetítésére az egyes entitások vagy gyűjtemények eseményeinek ismétlésével és feldolgozásával. Az összetettség akkor nő, ha a számítások az értékek hosszú időszakokra történő elemzését vagy összegzését foglalják magukban, mivel minden kapcsolódó eseményt meg kell vizsgálni. Az adatok pillanatképeinek rendszeres időközönként történő implementálása. Tárolhatja például egy entitás aktuális állapotát vagy az összesített összegek időszakos pillanatképeit, amely egy adott művelet előfordulási gyakorisága. A pillanatképek csökkentik a teljes eseményelőzmények ismételt feldolgozásának szükségességét, ami javítja a teljesítményt.

példa

Az alábbi kód egy olyan CQRS-implementáció példájából mutat be kivonatokat, amelyek különböző definíciókat használnak az olvasási modellekhez és az írási modellekhez. A modellillesztők nem diktálják a mögöttes adattárak funkcióit, és egymástól függetlenül is fejleszthetők és finomhangolhatók, mivel ezek a felületek külön-külön vannak.

A következő kód bemutatja az olvasási modell definícióját.

// Query interface
namespace ReadModel
{
  public interface ProductsDao
  {
    ProductDisplay FindById(int productId);
    ICollection<ProductDisplay> FindByName(string name);
    ICollection<ProductInventory> FindOutOfStockProducts();
    ICollection<ProductDisplay> FindRelatedProducts(int productId);
  }

  public class ProductDisplay
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal UnitPrice { get; set; }
    public bool IsOutOfStock { get; set; }
    public double UserRating { get; set; }
  }

  public class ProductInventory
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public int CurrentStock { get; set; }
  }
}

A rendszer lehetővé teszi a felhasználóknak számára a termékek értékelését. Ezt az alkalmazáskód az RateProduct alábbi kódban látható paranccsal hajtja végre.

public interface ICommand
{
  Guid Id { get; }
}

public class RateProduct : ICommand
{
  public RateProduct()
  {
    this.Id = Guid.NewGuid();
  }
  public Guid Id { get; set; }
  public int ProductId { get; set; }
  public int Rating { get; set; }
  public int UserId {get; set; }
}

A rendszer az osztály használatával kezeli az ProductsCommandHandler alkalmazás által küldött parancsokat. Az ügyfelek általában egy üzenetkezelő rendszeren, például egy üzenetsoron keresztül küldenek a parancsokat a tartománynak. A parancskezelő fogadja ezeket a parancsokat, és meghívja a tartományi felület metódusait. Az egyes parancsok részletességének célja a kérések ütközési esélyeinek csökkentése. A következő kód a ProductsCommandHandler osztály vázlatát mutatja be.

public class ProductsCommandHandler :
    ICommandHandler<AddNewProduct>,
    ICommandHandler<RateProduct>,
    ICommandHandler<AddToInventory>,
    ICommandHandler<ConfirmItemShipped>,
    ICommandHandler<UpdateStockFromInventoryRecount>
{
  private readonly IRepository<Product> repository;

  public ProductsCommandHandler (IRepository<Product> repository)
  {
    this.repository = repository;
  }

  void Handle (AddNewProduct command)
  {
    ...
  }

  void Handle (RateProduct command)
  {
    var product = repository.Find(command.ProductId);
    if (product != null)
    {
      product.RateProduct(command.UserId, command.Rating);
      repository.Save(product);
    }
  }

  void Handle (AddToInventory command)
  {
    ...
  }

  void Handle (ConfirmItemsShipped command)
  {
    ...
  }

  void Handle (UpdateStockFromInventoryRecount command)
  {
    ...
  }
}

Következő lépés

A minta megvalósításakor a következő információk lehetnek relevánsak:

  • Az adatparticionálási útmutató bemutatja, hogyan oszthat fel adatokat olyan partíciókra, amelyeket külön kezelhet és érhet el a méretezhetőség javítása, a versengés csökkentése és a teljesítmény optimalizálása érdekében.
  • Event Sourcing mintázat. Ez a minta azt ismerteti, hogyan egyszerűsítheti a feladatokat összetett tartományokban, és hogyan javíthatja a teljesítményt, a méretezhetőséget és a válaszképességet. Azt is ismerteti, hogyan biztosíthatja a tranzakciós adatok konzisztenciáját, miközben teljes auditnaplókat és előzményeket tart fenn, amelyek lehetővé teszik a kompenzáló műveleteket.

  • Materializált nézet minta. Ez a minta előre feltöltött nézeteket, más néven materializált nézeteket hoz létre egy vagy több adattárból történő hatékony lekérdezéshez és adatkinyeréshez. A CQRS megvalósítás olvasási modellje tartalmazhatja az írási modell adatainak materializált nézeteit, illetve a materializált nézetek létrehozására is használható.