ViennaNET: 'n stel biblioteke vir die agterkant. Deel 2

Die Raiffeisenbank .NET-ontwikkelaargemeenskap gaan voort om die inhoud van ViennaNET kortliks te hersien. Oor hoe en hoekom ons hiertoe gekom het, jy kan die eerste deel lees.

In hierdie artikel gaan ons deur biblioteke wat nog oorweeg moet word om met verspreide transaksies, rye en databasisse te werk, wat in ons GitHub-bewaarplek gevind kan word (bronne is hier), en Nuget-pakkette hier.

ViennaNET: 'n stel biblioteke vir die agterkant. Deel 2

WeneNET.Sagas

Wanneer 'n projek oorskakel na DDD en mikrodiensargitektuur, dan wanneer besigheidslogika oor verskillende dienste versprei word, ontstaan ​​'n probleem wat verband hou met die behoefte om 'n verspreide transaksiemeganisme te implementeer, omdat baie scenario's dikwels verskeie domeine gelyktydig beïnvloed. U kan in meer besonderhede kennis maak met sulke meganismes, byvoorbeeld, in die boek "Microservices Patterns", Chris Richardson.

In ons projekte het ons 'n eenvoudige maar nuttige meganisme geïmplementeer: 'n sage, of eerder 'n orkestrasie-gebaseerde sage. Die essensie daarvan is soos volg: daar is 'n sekere besigheidsscenario waarin dit nodig is om opeenvolgend bedrywighede in verskillende dienste uit te voer, en as daar probleme by enige stap opduik, is dit nodig om die terugrolprosedure vir alle vorige stappe te skakel, waar dit verskaf. Dus, aan die einde van die sage, ongeag sukses, ontvang ons konsekwente data in alle domeine.

Ons implementering word steeds in sy basiese vorm gemaak en is nie gekoppel aan die gebruik van enige metodes van interaksie met ander dienste nie. Dit is nie moeilik om te gebruik nie: maak net 'n afstammeling van die basiese abstrakte klas SagaBase<T>, waar T jou konteksklas is waarin jy die aanvanklike data kan stoor wat nodig is vir die sage om te werk, asook 'n paar tussenresultate. Die konteksinstansie sal deurgegee word na alle stappe tydens uitvoering. Saga self is 'n staatlose klas, so die instansie kan in DI geplaas word as 'n Singleton om die nodige afhanklikhede te kry.

Voorbeeld advertensie:

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

Voorbeeld oproep:

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

Volledige voorbeelde van verskillende implementerings kan bekyk word hier en in samestelling met toetse.

ViennaNET.Orm.*

'n Stel biblioteke om met verskeie databasisse via Nhibernate te werk. Ons gebruik die DB-First-benadering deur Liquibase te gebruik, so daar is slegs funksionaliteit om met data in 'n klaargemaakte databasis te werk.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – hoofsamestellings wat onderskeidelik basiese koppelvlakke en hul implementerings bevat. Kom ons kyk na hul inhoud in meer detail.

koppelvlak IEntityFactoryService en die implementering daarvan EntityFactoryService is die hoofbeginpunt om met die databasis te werk, aangesien die Eenheid van Werk, bewaarplekke vir werk met spesifieke entiteite, sowel as uitvoerders van opdragte en direkte SQL-navrae hier geskep word. Soms is dit gerieflik om die vermoëns van 'n klas te beperk om met 'n databasis te werk, byvoorbeeld om die vermoë te bied om slegs data te lees. Vir sulke gevalle IEntityFactoryService daar is 'n voorouer-koppelvlak IEntityRepositoryFactory, wat slegs 'n metode vir die skep van bewaarplekke verklaar.

Om direk toegang tot die databasis te verkry, word die verskaffermeganisme gebruik. Elke DBBS wat ons in ons spanne gebruik, het sy eie implementering: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Terselfdertyd kan verskeie verskaffers gelyktydig in een toepassing geregistreer word, wat dit byvoorbeeld moontlik maak om binne die raamwerk van een diens, sonder enige koste vir die wysiging van die infrastruktuur, 'n stap-vir-stap migrasie uit te voer vanaf een DBBS na 'n ander. Die meganisme vir die keuse van die vereiste verbinding en dus die verskaffer vir 'n spesifieke entiteitklas (waarvoor kartering na databasistabelle geskryf word) word geïmplementeer deur die entiteit te registreer in die BoundedContext-klas (bevat 'n metode om domeinentiteite te registreer) of sy opvolger ApplicationContext (bevat metodes vir die registrasie van toepassingsentiteite, direkte versoeke en opdragte), waar die verbinding-identifiseerder van die konfigurasie as 'n argument aanvaar word:

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

Voorbeeld Toepassingskonteks:

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

As die verbindings-ID nie gespesifiseer is nie, sal die verbinding met die naam "verstek" gebruik word.

Direkte kartering van entiteite na databasistabelle word geïmplementeer met behulp van standaard NHibernate-nutsgoed. U kan die beskrywing beide deur xml-lêers en deur klasse gebruik. Vir gerieflike skryf van stompbewaarplekke in Eenheidstoetse is daar 'n biblioteek ViennaNET.TestUtils.Orm.

Volledige voorbeelde van die gebruik van ViennaNET.Orm.* kan gevind word hier.

ViennaNET.Messaging.*

'n Stel biblioteke om met toue te werk.

Om met toue te werk, is dieselfde benadering as met verskeie DBBS'e gekies, naamlik die maksimum moontlike verenigde benadering in terme van werk met die biblioteek, ongeag die toubestuurder wat gebruik word. Biblioteek ViennaNET.Messaging is juis verantwoordelik vir hierdie eenwording, en ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue bevat adapterimplementasies vir onderskeidelik IBM MQ, RabbitMQ en Kafka.

Wanneer daar met toue gewerk word, is daar twee prosesse: ontvangs van 'n boodskap en stuur dit.

Oorweeg om te ontvang. Daar is 2 opsies hier: vir deurlopende luister en om 'n enkele boodskap te ontvang. Om voortdurend na die tou te luister, moet jy eers die verwerkerklas beskryf wat van geërf is IMessageProcessor, wat verantwoordelik sal wees vir die verwerking van die inkomende boodskap. Vervolgens moet dit aan 'n spesifieke tou "gekoppel" word; dit word gedoen deur registrasie in IQueueReactorFactory wat die tou-identifiseerder van die konfigurasie aandui:

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

Voorbeeld van begin luister:

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

Dan, wanneer die diens begin en die metode geroep word om te begin luister, sal alle boodskappe van die gespesifiseerde tou na die ooreenstemmende verwerker gaan.

Om 'n enkele boodskap in 'n fabriekskoppelvlak te ontvang IMessagingComponentFactory daar is 'n metode CreateMessageReceiverwat 'n ontvanger sal skep wat wag vir 'n boodskap uit die tou wat daaraan gespesifiseer is:

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

Om 'n boodskap te stuur jy moet dieselfde gebruik IMessagingComponentFactory en skep 'n boodskapsender:

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

Daar is drie gereedgemaakte opsies om 'n boodskap te serialiseer en te deserialiseer: net teks, XML en JSON, maar indien nodig, kan jy maklik jou eie koppelvlakimplementerings maak IMessageSerializer и IMessageDeserializer.

Ons het probeer om die unieke vermoëns van elke toubestuurder te bewaar, bv. ViennaNET.Messaging.MQSeriesQueue laat jou toe om nie net teks te stuur nie, maar ook byte boodskappe, en ViennaNET.Messaging.RabbitMQQueue ondersteun roetering en on-the-fly toustaan. Ons adapter-omhulsel vir RabbitMQ implementeer ook 'n mate van RPC: ons stuur 'n boodskap en wag vir 'n reaksie van 'n spesiale tydelike tou, wat slegs vir een antwoordboodskap geskep word.

Hier 'n voorbeeld van die gebruik van toue met basiese verbindingsnuanses.

ViennaNET.CallContext

Ons gebruik toue nie net vir integrasie tussen verskillende stelsels nie, maar ook vir kommunikasie tussen mikrodienste van dieselfde toepassing, byvoorbeeld binne 'n sage. Dit het gelei tot die behoefte om saam met die boodskap sulke bykomstige data soos die gebruikeraanmelding, versoekidentifiseerder vir end-tot-end-aantekening, bron-IP-adres en magtigingsdata oor te dra. Om die aanstuur van hierdie data te implementeer, het ons 'n biblioteek ontwikkel ViennaNET.CallContext, wat jou toelaat om data te stoor vanaf 'n versoek wat die diens binnegaan. In hierdie geval maak dit nie saak hoe die versoek gemaak is deur 'n tou of via Http nie. Dan, voordat die uitgaande versoek of boodskap gestuur word, word data uit die konteks geneem en in die opskrifte geplaas. Die volgende diens ontvang dus die hulpdata en bestuur dit op dieselfde manier.

Dankie vir jou aandag, ons sien uit na jou kommentaar en trek versoeke!

Bron: will.com

Voeg 'n opmerking