ViennaNET: aro de bibliotekoj por la backend. Parto 2

La programkomunumo de Raiffeisenbank .NET daŭre mallonge revizias la enhavon de ViennaNET. Pri kiel kaj kial ni venis al ĉi tio, vi povas legi la unuan parton.

En ĉi tiu artikolo, ni ekzamenos ankoraŭ konsideratajn bibliotekojn por labori kun distribuitaj transakcioj, atendovicoj kaj datumbazoj, kiuj troveblas en nia GitHub-deponejo (fontoj estas ĉi tie), kaj Nuget-pakaĵoj ĉi tie.

ViennaNET: aro de bibliotekoj por la backend. Parto 2

ViennaNET.Sagas

Kiam projekto ŝanĝas al DDD kaj mikroserva arkitekturo, tiam kiam komerca logiko estas distribuita tra malsamaj servoj, problemo ekestas rilate al la bezono efektivigi distribuitan transakcian mekanismon, ĉar multaj scenaroj ofte influas plurajn domajnojn samtempe. Vi povas pli detale konatiĝi kun tiaj mekanismoj, ekzemple, en la libro "Microservices Patterns", Chris Richardson.

En niaj projektoj, ni efektivigis simplan sed utilan mekanismon: sagao, aŭ pli ĝuste orkestrado-bazita sagao. Ĝia esenco estas kiel sekvas: ekzistas certa komerca scenaro, en kiu necesas sinsekve plenumi operaciojn en malsamaj servoj, kaj se iuj problemoj aperas ĉe iu paŝo, necesas nomi la procedon de retrotrado por ĉiuj antaŭaj paŝoj, kie ĝi estas. provizita. Tiel, ĉe la fino de la sagao, sendepende de sukceso, ni ricevas konsekvencajn datumojn en ĉiuj domajnoj.

Nia efektivigo ankoraŭ estas farita en sia baza formo kaj ne estas ligita al la uzo de iuj metodoj de interagado kun aliaj servoj. Ĝi ne estas malfacila uzi: simple faru posteulon de la baza abstrakta klaso SagaBase<T>, kie T estas via kunteksta klaso en kiu vi povas konservi la komencajn datumojn necesajn por ke la sagao funkciu, same kiel iujn mezajn rezultojn. La kuntekstokazaĵo estos trapasita al ĉiuj paŝoj dum ekzekuto. Saga mem estas sennacia klaso, do la kazo povas esti metita en DI kiel Singleton por akiri la necesajn dependecojn.

Ekzempla anonco:

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

Ekzemplo voko:

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

Plenaj ekzemploj de malsamaj efektivigoj povas esti rigarditaj tie kaj en asembleo kun provoj.

ViennaNET.Orm.*

Aro da bibliotekoj por labori kun diversaj datumbazoj per Nhibernate. Ni uzas la aliron DB-First uzante Liquibase, do ekzistas nur funkcieco por labori kun datumoj en preta datumbazo.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – ĉefaj asembleoj enhavantaj bazajn interfacojn kaj iliajn efektivigojn, respektive. Ni rigardu ilian enhavon pli detale.

interfaco IEntityFactoryService kaj ĝia efektivigo EntityFactoryService estas la ĉefa deirpunkto por labori kun la datumbazo, ekde la Unuo de Laboro, deponejoj por labori kun specifaj estaĵoj, same kiel ekzekutistoj de komandoj kaj rektaj SQL-demandoj estas kreitaj ĉi tie. Kelkfoje estas oportune limigi la kapablojn de klaso por labori kun datumbazo, ekzemple, por provizi la kapablon nur legi datumojn. Por tiaj kazoj IEntityFactoryService ekzistas praulo - interfaco IEntityRepositoryFactory, kiu nur deklaras metodon por krei deponejojn.

Por rekte aliri la datumbazon, la provizanta mekanismo estas uzata. Ĉiu DBMS, kiun ni uzas en niaj teamoj, havas sian propran efektivigon: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Samtempe, pluraj provizantoj povas esti registritaj en unu aplikaĵo samtempe, kio ebligas, ekzemple, en la kadro de unu servo, sen ajna kosto por modifado de la infrastrukturo, efektivigi paŝon post paŝo migrado de unu DBMS al alia. La mekanismo por elekti la postulatan konekton kaj, tial, la provizanton por specifa unuoklaso (por kiu mapado al datumbazaj tabeloj estas skribita) estas efektivigita per registrado de la unuo en la BoundedContext-klaso (enhavas metodon por registri domajnaj unuoj) aŭ ĝia posteulo. ApplicationContext (enhavas metodojn por registri aplikajn entojn, rektajn petojn kaj komandojn), kie la konektoidentigilo de la agordo estas akceptita kiel argumento:

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

Ekzemplo Aplika Kunteksto:

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

Se la konekto ID ne estas specifita, tiam la konekto nomita "defaŭlta" estos uzata.

Rekta mapado de estaĵoj al datumbazaj tabeloj estas efektivigita per normaj NHibernate-iloj. Vi povas uzi la priskribon kaj per xml-dosieroj kaj per klasoj. Por oportuna skribo de stump-deponejoj en unutestoj, ekzistas biblioteko ViennaNET.TestUtils.Orm.

Plenaj ekzemploj pri uzado de ViennaNET.Orm.* troveblas tie.

ViennaNET.Mesaging.*

Aro da bibliotekoj por labori kun atendovicoj.

Por labori kun atendovicoj, oni elektis la saman aliron kiel kun diversaj DBMS-oj, nome, la maksimuma ebla unuigita aliro laŭ laborado kun la biblioteko, sendepende de la vostomanaĝero uzita. Biblioteko ViennaNET.Messaging ĝuste respondecas pri tiu ĉi unuiĝo, kaj ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue enhavas adaptilojn por IBM MQ, RabbitMQ kaj Kafka, respektive.

Kiam vi laboras kun atendovicoj, estas du procezoj: ricevi mesaĝon kaj sendi ĝin.

Konsideru ricevi. Estas 2 ebloj ĉi tie: por daŭra aŭskultado kaj por ricevi ununuran mesaĝon. Por konstante aŭskulti la atendovicon, vi unue devas priskribi la procesoran klason de heredita IMessageProcessor, kiu respondecos pri prilaborado de la envenanta mesaĝo. Poste, ĝi devas esti "ligita" al specifa atendovico; tio estas farita per registrado en IQueueReactorFactory indikante la atendovicidentigilon de la agordo:

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

Ekzemplo de komenci aŭskulti:

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

Tiam, kiam la servo komenciĝas kaj la metodo estas vokita por komenci aŭskulti, ĉiuj mesaĝoj de la specifita vosto iros al la responda procesoro.

Por ricevi ununuran mesaĝon en fabrika interfaco IMessagingComponentFactory estas metodo CreateMessageReceiverkiu kreos ricevanton atendantan mesaĝon de la vostovico specifita al ĝi:

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

Por sendi mesaĝon vi devas uzi la samon IMessagingComponentFactory kaj kreu mesaĝ-sendilon:

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

Estas tri pretaj opcioj por seriigi kaj deserialigi mesaĝon: nur teksto, XML kaj JSON, sed se necese, vi povas facile fari viajn proprajn interfacajn realigojn. IMessageSerializer и IMessageDeserializer.

Ni provis konservi la unikajn kapablojn de ĉiu vicadministranto, ekz. ViennaNET.Messaging.MQSeriesQueue permesas sendi ne nur tekston, sed ankaŭ bajtajn mesaĝojn, kaj ViennaNET.Messaging.RabbitMQQueue subtenas vojigon kaj surla-muŝan atendovicon. Nia adaptila envolvaĵo por RabbitMQ ankaŭ efektivigas iun ŝajnon de RPC: ni sendas mesaĝon kaj atendas respondon de speciala provizora atendovico, kiu estas kreita nur por unu respondmesaĝo.

tie ekzemplo de uzado de vostoj kun bazaj konektnuancoj.

ViennaNET.CallContext

Ni uzas vostojn ne nur por integriĝo inter malsamaj sistemoj, sed ankaŭ por komunikado inter mikroservoj de la sama aplikaĵo, ekzemple, ene de sagao. Ĉi tio kaŭzis la bezonon transdoni kune kun la mesaĝo tiajn helpajn datumojn kiel la uzanta ensaluto, peti identigilon por fin-al-fina registradado, fonta IP-adreso kaj rajtigaj datumoj. Por efektivigi la plusendon de ĉi tiuj datumoj, ni evoluigis bibliotekon ViennaNET.CallContext, kiu permesas vin stoki datumojn de peto eniranta la servon. En ĉi tiu kazo, kiel la peto estis farita, per vosto aŭ per Http, ne gravas. Tiam, antaŭ sendi la eksiĝintan peton aŭ mesaĝon, datumoj estas prenitaj el la kunteksto kaj metitaj en la kapliniojn. Tiel, la sekva servo ricevas la helpajn datumojn kaj administras ĝin same.

Dankon pro via atento, ni atendas viajn komentojn kaj tiri petojn!

fonto: www.habr.com

Aldoni komenton