ViennaNET: skup biblioteka za pozadinu. Dio 2

Zajednica programera Raiffeisenbank .NET nastavlja sa 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 članku ćemo proći kroz biblioteke koje tek treba razmotriti za rad s distribuiranim transakcijama, redovima i bazama podataka, koje se mogu naći u našem GitHub repozitoriju (izvori su ovdje), i Nuget paketi ovdje.

ViennaNET: skup biblioteka za pozadinu. Dio 2

ViennaNET.Sagas

Kada se projekat prebaci na DDD i mikroservisnu arhitekturu, onda kada se poslovna logika distribuira na različite servise, nastaje problem vezan za potrebu implementacije mehanizma distribuirane transakcije, jer mnogi scenariji često utiču na nekoliko domena odjednom. S takvim mehanizmima možete se detaljnije upoznati, na primjer, u knjizi "Microservices Patterns", Chris Richardson.

U naše projekte implementirali smo jednostavan, ali koristan mehanizam: sagu, odnosno sagu zasnovanu na orkestraciji. Njegova suština je sljedeća: postoji određeni poslovni scenario u kojem je potrebno uzastopno obavljati operacije u različitim servisima, a ako se u bilo kojem koraku jave problemi, potrebno je pozvati proceduru povratka za sve prethodne korake, gdje je obezbeđeno. Tako na kraju sage, bez obzira na uspjeh, dobijamo konzistentne podatke u svim domenima.

Naša implementacija je i dalje napravljena u svom osnovnom obliku i nije vezana za korištenje bilo kakvih metoda interakcije sa drugim servisima. Nije teško koristiti: samo napravite potomka osnovne apstraktne klase SagaBase<T>, gdje je T vaša kontekstualna klasa u koju možete pohraniti početne podatke potrebne da saga radi, kao i neke međurezultate. Instanca konteksta će biti proslijeđena svim koracima tokom izvršenja. Sama Saga je klasa bez stanja, tako da se instanca može staviti u DI kao Singleton da bi se dobile potrebne zavisnosti.

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 vidjeti puni primjeri različitih implementacija ovdje iu montaži sa testovi.

ViennaNET.Orm.*

Skup biblioteka za rad sa raznim bazama podataka putem Nhibernatea. Koristimo DB-First pristup koristeći Liquibase, tako da postoji samo funkcionalnost za rad sa podacima u gotovim bazama podataka.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – glavni sklopovi koji sadrže osnovne interfejse i njihove implementacije, respektivno. Pogledajmo njihov sadržaj detaljnije.

sučelje IEntityFactoryService i njegovu implementaciju EntityFactoryService su glavna polazna tačka za rad sa bazom podataka, budući da se ovde kreiraju Jedinica rada, spremišta za rad sa određenim entitetima, kao i izvršioci komandi i direktni SQL upiti. Ponekad je zgodno ograničiti mogućnosti klase za rad sa bazom podataka, na primjer, da bi se obezbijedila mogućnost samo čitanja podataka. Za takve slučajeve IEntityFactoryService postoji predak - interfejs IEntityRepositoryFactory, koji samo deklarira metodu za kreiranje spremišta.

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

Istovremeno, više provajdera može biti registrovano u jednoj aplikaciji istovremeno, što omogućava da se, na primer, u okviru jedne usluge, bez ikakvih troškova za modifikaciju infrastrukture, izvrši korak po korak migracija sa jedan DBMS u drugi. Mehanizam za odabir potrebne veze, a samim tim i provajdera za određenu klasu entiteta (za koju se piše mapiranje u tabele baze podataka) implementira se registracijom entiteta u klasu BoundedContext (sadrži metodu za registraciju entiteta domene) ili njenog nasljednika. ApplicationContext (sadrži metode za registraciju entiteta aplikacije, direktne zahtjeve i naredbe), 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 ApplicationContext:

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”.

Direktno mapiranje entiteta u tablice baze podataka implementirano je korištenjem standardnih NHibernate alata. Opis možete koristiti i kroz xml datoteke i kroz klase. Za praktično pisanje stub repozitorija u jediničnim testovima, postoji biblioteka ViennaNET.TestUtils.Orm.

Pune primjere korištenja ViennaNET.Orm.* možete pronaći ovdje.

ViennaNET.Messaging.*

Skup biblioteka za rad sa redovima čekanja.

Za rad sa redovima izabran je isti pristup kao i kod raznih DBMS-ova, odnosno maksimalno mogući objedinjeni pristup u pogledu rada sa bibliotekom, bez obzira na korišćeni upravitelj redova. Biblioteka 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 Kafku, respektivno.

Kada radite sa redovima, 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, prvo morate opisati klasu procesora od koje je naslijeđena IMessageProcessor, koji će biti odgovoran za obradu dolazne poruke. Zatim, mora biti "povezan" sa određenim redom čekanja, to se radi putem registracije; IQueueReactorFactory označavajući identifikator reda 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 servis pokrene i metoda je pozvana da započne slušanje, sve poruke iz navedenog reda će ići u odgovarajući procesor.

Za primanje jedne poruke u fabričkom interfejsu IMessagingComponentFactory postoji metoda CreateMessageReceiverkoji će kreirati primaoca koji čeka poruku iz reda koji mu je naveden:

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

Za slanje poruke morate koristiti isto IMessagingComponentFactory i kreirajte pošiljaoca poruke:

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

Postoje tri gotove opcije za serijalizaciju i deserializaciju poruke: samo tekst, XML i JSON, ali ako je potrebno, lako možete napraviti vlastite implementacije interfejsa IMessageSerializer и IMessageDeserializer.

Pokušali smo da sačuvamo jedinstvene mogućnosti svakog upravitelja redova, npr. ViennaNET.Messaging.MQSeriesQueue omogućava vam slanje ne samo tekstualnih, već i bajt poruka, i ViennaNET.Messaging.RabbitMQQueue podržava rutiranje i čekanje u hodu. 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 primjer korištenja redova s ​​osnovnim nijansama povezivanja.

ViennaNET.CallContext

Redove koristimo ne samo za integraciju između različitih sistema, već i za komunikaciju između mikroservisa iste aplikacije, na primjer, unutar sage. To je dovelo do potrebe da se uz poruku prenesu i pomoćni podaci kao što su prijava korisnika, identifikator zahtjeva za end-to-end logovanje, izvorna IP adresa i autorizacijski podaci. Za implementaciju prosljeđivanja ovih podataka razvili smo biblioteku ViennaNET.CallContext, koji vam omogućava pohranjivanje podataka iz zahtjeva koji ulazi u uslugu. U ovom slučaju, način na koji je zahtjev napravljen, kroz red čekanja ili preko Http-a, nije bitno. Zatim, prije slanja odlaznog zahtjeva ili poruke, podaci se uzimaju iz konteksta i stavljaju u zaglavlja. Dakle, sljedeći servis prima pomoćne podatke i upravlja njima na isti način.

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

izvor: www.habr.com

Dodajte komentar