ViennaNET: skup knjižnica za backend. 2. dio

Zajednica programera Raiffeisenbank .NET nastavlja s kratkim pregledom sadržaja ViennaNET-a. O tome kako i zašto smo došli do ovoga, možete pročitati prvi dio.

U ovom ćemo članku proći kroz još nerazmotrene biblioteke za rad s distribuiranim transakcijama, redovima čekanja i bazama podataka, a koje se mogu pronaći u našem GitHub repozitoriju (izvori su ovdje), i Nuget paketi ovdje.

ViennaNET: skup knjižnica za backend. 2. dio

ViennaNET.Sagas

Kada se projekt prebaci na DDD i mikroservisnu arhitekturu, tada kada se poslovna logika distribuira na različite servise, javlja se problem vezan uz potrebu implementacije mehanizma distribuiranih transakcija, jer mnogi scenariji često utječu na nekoliko domena odjednom. Možete se detaljnije upoznati s takvim mehanizmima, na primjer, u knjizi "Microservices Patterns", Chris Richardson.

U našim smo projektima implementirali jednostavan, ali koristan mehanizam: sagu, odnosno sagu baziranu na orkestraciji. Njegova bit je sljedeća: postoji određeni poslovni scenarij u kojem je potrebno sekvencijalno izvoditi operacije u različitim servisima, a ako se u bilo kojem koraku pojave problemi, potrebno je pozvati proceduru vraćanja na staro stanje za sve prethodne korake, gdje je pod uvjetom. Tako na kraju sage, bez obzira na uspjeh, dobivamo konzistentne podatke u svim domenama.

Naša implementacija još uvijek je napravljena u osnovnom obliku i nije vezana uz korištenje bilo kakvih metoda interakcije s drugim servisima. Nije ga teško koristiti: samo napravite potomka osnovne apstraktne klase SagaBase<T>, gdje je T vaša klasa konteksta u koju možete pohraniti početne podatke potrebne za rad sage, kao i neke međurezultate. Instanca konteksta bit će proslijeđena svim koracima tijekom izvođenja. Sama Saga je klasa bez stanja, tako da se instanca može postaviti u DI kao Singleton da bi se dobile potrebne ovisnosti.

Primjer oglasa:

public class ExampleSaga : SagaBase<ExampleContext>
{
  public ExampleSaga()
  {
    Step("Step 1")
      .WithAction(c => ...)
      .WithCompensation(c => ...);
	
    AsyncStep("Step 2")
      .WithAction(async c => ...);
  }
}

Primjer poziva:

var saga = new ExampleSaga();
var context = new ExampleContext();
await saga.Execute(context);

Mogu se pogledati puni primjeri različitih implementacija здесь a u sklopu sa testovi.

ViennaNET.Orm.*

Skup knjižnica za rad s različitim bazama podataka putem Nhibernatea. Koristimo DB-First pristup koristeći Liquibase, tako da postoji samo funkcionalnost za rad s podacima u gotovoj bazi podataka.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – glavne sklopove koji sadrže osnovna sučelja i njihove implementacije. Pogledajmo detaljnije njihov sadržaj.

sučelje IEntityFactoryService i njegovu provedbu EntityFactoryService glavno su polazište za rad s bazom podataka, budući da se ovdje kreira Unit of Work, repozitoriji za rad s određenim entitetima, kao i izvršitelji naredbi i direktnih SQL upita. Ponekad je zgodno ograničiti mogućnosti klase za rad s bazom podataka, na primjer, pružiti mogućnost samo čitanja podataka. Za takve slučajeve IEntityFactoryService postoji predak - sučelje IEntityRepositoryFactory, koji deklarira samo metodu za stvaranje repozitorija.

Za izravan pristup bazi podataka koristi se mehanizam provajdera. Svaki DBMS koji koristimo u našim timovima ima vlastitu implementaciju: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Istodobno, nekoliko pružatelja usluga može biti registrirano u jednoj aplikaciji u isto vrijeme, što omogućuje, na primjer, u okviru jedne usluge, bez ikakvih troškova za modificiranje infrastrukture, izvršiti korak po korak migraciju iz jedan DBMS u drugi. Mehanizam za odabir potrebne veze i, prema tome, pružatelja za određenu klasu entiteta (za koju je napisano mapiranje u tablice baze podataka) implementiran je registracijom entiteta u klasi BoundedContext (sadrži metodu za registraciju entiteta domene) ili njezinom nasljedniku ApplicationContext (sadrži metode za registriranje aplikacijskih entiteta, izravnih zahtjeva i naredbi), gdje se identifikator veze iz konfiguracije prihvaća kao argument:

"db": [
  {
    "nick": "mssql_connection",
    "dbServerType": "MSSQL",
    "ConnectionString": "...",
    "useCallContext": true
  },
  {
    "nick": "oracle_connection",
    "dbServerType": "Oracle",
    "ConnectionString": "..."
  }
],

Primjer konteksta aplikacije:

internal sealed class DbContext : ApplicationContext
{
  public DbContext()
  {
    AddEntity<SomeEntity>("mssql_connection");
    AddEntity<MigratedSomeEntity>("oracle_connection");
    AddEntity<AnotherEntity>("oracle_connection");
  }
}

Ako ID veze nije naveden, tada će se koristiti veza pod nazivom "default".

Izravno preslikavanje entiteta u tablice baze podataka provodi se pomoću standardnih NHibernate alata. Opis možete koristiti i kroz xml datoteke i kroz klase. Za praktično pisanje repozitorija stubova u Unit testovima postoji biblioteka ViennaNET.TestUtils.Orm.

Potpuni primjeri korištenja ViennaNET.Orm.* mogu se pronaći здесь.

ViennaNET.Messaging.*

Skup knjižnica za rad s redovima čekanja.

Za rad s redovima čekanja odabran je isti pristup kao i kod različitih DBMS-ova, odnosno maksimalno mogući unificirani pristup u smislu rada s knjižnicom, bez obzira na korišteni upravitelj čekanja. Knjižnica ViennaNET.Messaging je upravo odgovoran za ovo ujedinjenje, i ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue sadrže implementacije adaptera za IBM MQ, RabbitMQ i Kafka, redom.

Kod rada s redovima čekanja postoje dva procesa: primanje poruke i njeno slanje.

Razmislite o primanju. Ovdje postoje 2 opcije: za neprekidno slušanje i za primanje jedne poruke. Da biste stalno slušali red čekanja, prvo morate opisati naslijeđenu klasu procesora IMessageProcessor, koji će biti odgovoran za obradu dolazne poruke. Zatim se mora "povezati" s određenim redom; to se radi registracijom u IQueueReactorFactory koji označava identifikator reda čekanja iz konfiguracije:

"messaging": {
    "ApplicationName": "MyApplication"
},
"rabbitmq": {
    "queues": [
      {
        "id": "myQueue",
        "queuename": "lalala",
        ...
      }
    ]
},

Primjer početka slušanja:

_queueReactorFactory.Register<MyMessageProcessor>("myQueue");
var queueReactor = queueReactorFactory.CreateQueueReactor("myQueue");
queueReactor.StartProcessing();

Zatim, kada se usluga pokrene i metoda se pozove za početak slušanja, sve poruke iz navedenog reda čekanja će ići u odgovarajući procesor.

Za primanje jedne poruke u tvorničkom sučelju IMessagingComponentFactory postoji metoda CreateMessageReceiverkoji će stvoriti primatelja koji čeka poruku iz reda čekanja koji mu je naveden:

using (var receiver = _messagingComponentFactory.CreateMessageReceiver<TestMessage>("myQueue"))
{
    var message = receiver.Receive();
}

Za slanje poruke trebate koristiti isti IMessagingComponentFactory i kreirajte pošiljatelja poruke:

using (var sender = _messagingComponentFactory.CreateMessageSender<MyMessage>("myQueue"))
{
    sender.SendMessage(new MyMessage { Value = ...});
}

Postoje tri gotove opcije za serijalizaciju i deserijalizaciju poruke: samo tekst, XML i JSON, ali ako je potrebno, možete jednostavno napraviti vlastite implementacije sučelja IMessageSerializer и IMessageDeserializer.

Pokušali smo sačuvati jedinstvene mogućnosti svakog upravitelja čekanja, npr. ViennaNET.Messaging.MQSeriesQueue omogućuje slanje ne samo tekstualnih, već i bajtnih poruka, i ViennaNET.Messaging.RabbitMQQueue podržava usmjeravanje i on-the-fly čekanje. Naš omotač adaptera za RabbitMQ također implementira neki privid RPC-a: šaljemo poruku i čekamo odgovor iz posebnog privremenog reda koji se kreira samo za jednu poruku odgovora.

ovdje je primjer korištenja redova s ​​osnovnim nijansama povezivanja.

ViennaNET.CallContext

Redove čekanja koristimo ne samo za integraciju između različitih sustava, već i za komunikaciju između mikroservisa iste aplikacije, na primjer, unutar sage. To je dovelo do potrebe da se zajedno s porukom prenesu i pomoćni podaci kao što je prijava korisnika, identifikator zahtjeva za end-to-end evidentiranje, izvorna IP adresa i podaci za autorizaciju. Kako bismo implementirali prosljeđivanje ovih podataka, razvili smo biblioteku ViennaNET.CallContext, koji vam omogućuje pohranjivanje podataka iz zahtjeva koji ulazi u uslugu. U ovom slučaju, kako je zahtjev napravljen, kroz red čekanja ili putem Http-a, nije bitno. Zatim, prije slanja odlaznog zahtjeva ili poruke, podaci se uzimaju iz konteksta i smještaju u zaglavlja. Dakle, sljedeći servis prima pomoćne podatke i njima upravlja na isti način.

Hvala vam na pažnji, veselimo se vašim komentarima i zahtjevima za povlačenje!

Izvor: www.habr.com

Dodajte komentar