ViennaNET: nabor knjižnic za zaledje. 2. del

Skupnost razvijalcev Raiffeisenbank .NET nadaljuje s kratkim pregledom vsebine ViennaNET. O tem, kako in zakaj smo prišli do tega, lahko preberete prvi del.

V tem članku bomo šli skozi knjižnice, ki jih še ni treba upoštevati, za delo s porazdeljenimi transakcijami, čakalnimi vrstami in bazami podatkov, ki jih lahko najdete v našem repozitoriju GitHub (viri so tukaj), in Paketi Nuget tukaj.

ViennaNET: nabor knjižnic za zaledje. 2. del

ViennaNET.Sagas

Ko projekt preklopi na DDD in mikrostoritveno arhitekturo, ko se poslovna logika porazdeli po različnih storitvah, se pojavi problem, povezan s potrebo po implementaciji mehanizma porazdeljenih transakcij, saj veliko scenarijev pogosto vpliva na več domen hkrati. S takimi mehanizmi se lahko podrobneje seznanite, npr. v knjigi "Microservices Patterns", Chris Richardson.

V naših projektih smo implementirali preprost, a uporaben mehanizem: sago ali bolje rečeno sago na osnovi orkestracije. Njegovo bistvo je naslednje: obstaja določen poslovni scenarij, v katerem je treba zaporedno izvajati operacije v različnih storitvah, in če se na katerem koli koraku pojavijo težave, je treba poklicati postopek povrnitve za vse prejšnje korake, kjer je pod pogojem. Tako na koncu sage, ne glede na uspeh, dobimo konsistentne podatke na vseh področjih.

Naša izvedba je še vedno narejena v osnovni obliki in ni vezana na uporabo kakršnih koli načinov interakcije z drugimi storitvami. Uporaba ni težka: samo naredite potomca osnovnega abstraktnega razreda SagaBase<T>, kjer je T vaš kontekstni razred, v katerega lahko shranite začetne podatke, potrebne za delovanje sage, kot tudi nekaj vmesnih rezultatov. Instanca konteksta bo med izvajanjem posredovana vsem korakom. Saga je razred brez stanja, zato je primerek mogoče postaviti v DI kot Singleton, da dobite potrebne odvisnosti.

Primer oglasa:

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

Primer klica:

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

Celotne primere različnih izvedb si lahko ogledate tukaj in v sestavi z testi.

ViennaNET.Orm.*

Nabor knjižnic za delo z različnimi zbirkami podatkov prek Nhibernate. Uporabljamo pristop DB-First z uporabo Liquibase, tako da obstaja samo funkcionalnost za delo s podatki v že pripravljeni bazi podatkov.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – glavne sklope, ki vsebujejo osnovne vmesnike oziroma njihove izvedbe. Oglejmo si njihovo vsebino podrobneje.

vmesnik IEntityFactoryService in njegovo izvajanje EntityFactoryService so glavno izhodišče za delo z bazo podatkov, saj se tukaj ustvarijo enota dela, repozitoriji za delo s specifičnimi entitetami, pa tudi izvajalci ukazov in neposrednih SQL poizvedb. Včasih je priročno omejiti zmožnosti razreda za delo z bazo podatkov, na primer, da omogočite samo branje podatkov. Za take primere IEntityFactoryService obstaja prednik - vmesnik IEntityRepositoryFactory, ki deklarira le metodo za ustvarjanje repozitorijev.

Za neposreden dostop do podatkovne baze se uporablja mehanizem ponudnika. Vsak DBMS, ki ga uporabljamo v naših skupinah, ima svojo izvedbo: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Hkrati je v eni aplikaciji lahko prijavljenih več ponudnikov hkrati, kar omogoča na primer v okviru ene storitve brez stroškov spreminjanja infrastrukture izvesti postopno migracijo iz enega DBMS v drugega. Mehanizem za izbiro zahtevane povezave in s tem ponudnika za določen razred entitet (za katerega je napisana preslikava v tabele podatkovne baze) je implementiran z registracijo entitete v razredu BoundedContext (vsebuje metodo za registracijo domenskih entitet) ali njegovem nasledniku. ApplicationContext (vsebuje metode za registracijo aplikacijskih entitet, neposrednih zahtev in ukazov), kjer je identifikator povezave iz konfiguracije sprejet kot argument:

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

Primer konteksta aplikacije:

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

Če ID povezave ni določen, bo uporabljena povezava z imenom »privzeto«.

Neposredno preslikavo entitet v tabele baze podatkov je izvedeno s standardnimi orodji NHibernate. Opis lahko uporabite tako prek datotek xml kot prek razredov. Za priročno pisanje škrbinskih repozitorijev v testih enot obstaja knjižnica ViennaNET.TestUtils.Orm.

Popolne primere uporabe ViennaNET.Orm.* lahko najdete tukaj.

ViennaNET.Messaging.*

Nabor knjižnic za delo s čakalnimi vrstami.

Za delo s čakalnimi vrstami je bil izbran enak pristop kot pri različnih DBMS, in sicer maksimalno možno enoten pristop pri delu s knjižnico, ne glede na uporabljenega upravljalnika čakalnih vrst. Knjižnica ViennaNET.Messaging je prav zaslužen za to združitev in ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue vsebujejo implementacije adapterjev za IBM MQ, RabbitMQ in Kafka.

Pri delu s čakalnimi vrstami obstajata dva procesa: prejem sporočila in njegovo pošiljanje.

Razmislite o prejemu. Tu sta na voljo 2 možnosti: za neprekinjeno poslušanje in za prejem enega sporočila. Če želite nenehno poslušati čakalno vrsto, morate najprej opisati razred procesorja, ki je podedovan IMessageProcessor, ki bo odgovoren za obdelavo dohodnega sporočila. Nato ga je treba »povezati« z določeno čakalno vrsto; to se naredi z registracijo v IQueueReactorFactory ki označuje identifikator čakalne vrste iz konfiguracije:

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

Primer začetka poslušanja:

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

Potem, ko se storitev zažene in se pokliče metoda za začetek poslušanja, bodo vsa sporočila iz določene čakalne vrste šla v ustrezen procesor.

Za prejem enega sporočila v tovarniškem vmesniku IMessagingComponentFactory obstaja metoda CreateMessageReceiverki bo ustvaril prejemnika, ki čaka na sporočilo iz čakalne vrste, ki mu je določena:

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

Za pošiljanje sporočila morate uporabiti isto IMessagingComponentFactory in ustvarite pošiljatelja sporočila:

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

Obstajajo tri že pripravljene možnosti za serializacijo in deserializacijo sporočila: samo besedilo, XML in JSON, po potrebi pa lahko preprosto naredite lastne implementacije vmesnika IMessageSerializer и IMessageDeserializer.

Poskušali smo ohraniti edinstvene zmožnosti vsakega upravitelja čakalnih vrst, npr. ViennaNET.Messaging.MQSeriesQueue vam omogoča pošiljanje ne samo besedilnih, ampak tudi bajtnih sporočil in ViennaNET.Messaging.RabbitMQQueue podpira usmerjanje in sprotno postavljanje v čakalno vrsto. Naš ovoj adapterja za RabbitMQ izvaja tudi nekaj podobnega RPC: pošljemo sporočilo in čakamo na odgovor iz posebne začasne čakalne vrste, ki je ustvarjena samo za eno odzivno sporočilo.

Tu primer uporabe čakalnih vrst z osnovnimi odtenki povezave.

ViennaNET.CallContext

Čakalne vrste ne uporabljamo le za integracijo med različnimi sistemi, ampak tudi za komunikacijo med mikrostoritvami iste aplikacije, na primer znotraj sage. To je povzročilo potrebo po prenosu skupaj s sporočilom takšnih pomožnih podatkov, kot so prijava uporabnika, identifikator zahteve za beleženje od konca do konca, izvorni naslov IP in podatki o avtorizaciji. Za izvedbo posredovanja teh podatkov smo razvili knjižnico ViennaNET.CallContext, ki omogoča shranjevanje podatkov iz zahteve, ki vstopa v storitev. V tem primeru ni pomembno, kako je bila zahteva podana, prek čakalne vrste ali prek Http. Nato se pred pošiljanjem odhodne zahteve ali sporočila podatki vzamejo iz konteksta in postavijo v glave. Tako naslednja storitev prejme pomožne podatke in jih na enak način upravlja.

Hvala za vašo pozornost, veselimo se vaših komentarjev in zahtevkov za vleko!

Vir: www.habr.com

Dodaj komentar