ViennaNET: një koleksion bibliotekash për backend-in. Pjesa 2

Komuniteti i zhvilluesve .NET të Raiffeisenbank vazhdon me një analizë të shkurtër të përmbajtjes së ViennaNET. Më shumë rreth mënyrës dhe arsyeve pse arritëm këtu, mund të lexoni në pjesën e parë.

NĂ« kĂ«tĂ« artikull do tĂ« hedhim njĂ« vĂ«shtrim te bibliotekat qĂ« nuk janĂ« shqyrtuar akoma pĂ«r punĂ« me transaksione tĂ« shpĂ«rndara, radhĂ«t dhe DB-tĂ«, tĂ« cilat mund t'i gjeni nĂ« depozitĂ«n tonĂ« nĂ« GitHub (kodet burimore ndodhen kĂ«tu— emri i funksionit ( Paketat Nuget janĂ« kĂ«tu.

ViennaNET: një koleksion bibliotekash për backend-in. Pjesa 2

ViennaNET.Sagas

Kur projekti kalon në DDD dhe arkitekturë mikrosherish, në ndarjen e logjikës së biznesit në shërbime të ndryshme, shfaqet një problem i lidhur me nevojën për të implementuar një mekanizëm të transaksioneve të shpërndara, pasi shumë skenarë shpesh prekin disa domain-e. Një njohje më e thellë me këto mekanizma mund të bëhet, për shembull, në librin «Microservices Patterns», Chris Richardson.

Në projektet tona kemi implementuar një mekanizëm të thjeshtë, por të dobishëm: saga, dhe më saktë, një saga të bazuar në orkestrimin. Thelbi i saj është: ka një skenar biznesi, në të cilin nevojitet të kryhen operacione në mënyrë të njëpasnjëshme në shërbime të ndryshme; në rast se ndodhin probleme në ndonjë hap, është e nevojshme të thirret një procedurë rikthimi për të gjitha hapat e mëparshëm, ku është parashikuar. Kështu, në fund të kryerjes së sagës, pavarësisht nga suksesi, ne marrim të dhëna konsistente në të gjitha domain-et.

Implementimi ynë aktualisht është i realizuar në një formë bazike dhe nuk është i lidhur me çdo mënyrë interaksioni me shërbime të tjera. Të aplikosh atë nuk është e vështirë: mjafton të krijosh një trashëgimtar nga klasa bazë abstrakte SagaBase, ku T është klasa juaj e kontekstit, në të cilën mund të ruani të dhënat fillestare të nevojshme për punën me sagën, si dhe disa rezultate ndërmjetëse. Instanca e kontekstit do të kalojë në të gjitha hapat gjatë ekzekutimit. Vetë saga është një klasë stateless, prandaj instanca mund të vendoset në DI si Singleton për të siguruar varësitë e nevojshme.

Shembulli i shpalljes:

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

Shembulli i thirrjes:

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

Mund të shikoni shembuj të plotë të realizimeve të ndryshme këtu dhe në grupin me teste.

ViennaNET.Orm.*

Një grup bibliotekash për punën me baza të dhënash të ndryshme përmes Nhibernate. Ne përdorim qasjen DB-First me aplikimin e Liquibase, prandaj këtu është pranishëm vetëm funksionaliteti për punën me të dhënat në një bazë të dhënash të gatshme.

ViennaNET.Orm.Seedwork dhe ViennaNET.Orm – mbledhjet kryesore, qĂ« pĂ«rmbajnĂ« ndĂ«rfaqet bazĂ« dhe zbatimet e tyre pĂ«rkatĂ«se. Le tĂ« ndalemi pak mĂ« shumĂ« te pĂ«rmbajtja e tyre.

NdĂ«rfaqja IEntityFactoryService dhe zbatimi i tij EntityFactoryService janĂ« pika kryesore pĂ«r punĂ«n me bazĂ«n e tĂ« dhĂ«nave, pasi kĂ«tu krijohet NjĂ«sia e PunĂ«s, repozitorĂ«t pĂ«r tĂ« punuar me entitete tĂ« caktuara, si dhe ekzekutorĂ«t e komandave dhe pyetjeve SQL tĂ« drejtpĂ«rdrejta. NdonjĂ«herĂ« Ă«shtĂ« e dobishme tĂ« kufizosh mundĂ«sitĂ« e klasĂ«s pĂ«r punĂ«n me bazĂ«n e tĂ« dhĂ«nave, pĂ«r shembull, pĂ«r tĂ« lejuar vetĂ«m leximin e tĂ« dhĂ«nave. PĂ«r kĂ«to raste, IEntityFactoryService ka njĂ« paraardhĂ«s – ndĂ«rfaqen IEntityRepositoryFactory, nĂ« tĂ« cilĂ«n shpallet vetĂ«m njĂ« metodĂ« pĂ«r krijimin e repozitorĂ«ve.

Për qasje të drejtpërdrejtë në bazën e të dhënave përdoret mekanizmi i ofruesve. Për çdo DBMS që përdorim në ekipe, ka një zbatim të saj: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Në të njëjtën kohë, në një aplikacion mund të regjistrohen disa ofrues njëkohësisht, çka lejon, për shembull, të realizohet një migrim hap pas hapi nga një DBMS në një tjetër brenda një shërbimi, pa ndonjë kosto për përmirësimin e infrastrukturës. Mekanizmi përzgjedhës i lidhjes së nevojshme dhe, për rrjedhojë, i ofruesit për një klasë të veçantë të entitetit (për të cilin shkruhet mapimi në tabelat e bazës së të dhënave) është realizuar përmes regjistrimit të entitetit në klasën BoundedContext (përmban metodën për regjistrimin e entiteteve domene) ose trashëgimtarit të saj ApplicationContext (përmban metodat për regjistrimin e entiteteve aplikative, pyetjeve të drejtpërdrejta dhe komandave), ku si argument merret identifikatori i lidhjes nga konfigurimi:

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

Shembuj i ApplicationContext:

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

Nëse identifikatori i lidhjes nuk është i specifikuar, do të përdoret lidhja me emrin «default».

Mapping of entities to database tables is implemented using standard NHibernate tools. You can use either XML files or classes for the description. To facilitate the writing of stub repositories in unit tests, there is a library ViennaNET.TestUtils.Orm.

You can find full examples of using ViennaNET.Orm.* këtu.

ViennaNET.Messaging.*

A set of libraries for working with queues.

The same approach used for various DBMSs was chosen for working with queues, namely, a maximally unified approach in terms of working with the library, regardless of the queue manager being used. The library ViennaNET.Messaging is responsible for this unification, and ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue, and ViennaNET.Messaging.KafkaQueue contain implementations of adapters for IBM MQ, RabbitMQ, and Kafka, respectively.

In working with queues, there are two processes: receiving a message and sending one.

Let's consider receiving. There are 2 options here: for continuous listening and for receiving a single message. To continuously listen to the queue, you first need to describe a processor class, inherited from IMessageProcessor, which will be responsible for processing the incoming message. Next, it needs to be 'bound' to a specific queue, which is done through registration in IQueueReactorFactory with the queue identifier specified in the configuration:

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

Example of starting listening:

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

Then, upon starting the service and calling the method to begin listening, all messages from the specified queue will be routed to the corresponding processor.

To receive a single message in the factory interface IMessagingComponentFactory there is a method CreateMessageReceiver, which will create a receiver waiting for messages from the specified queue:

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

To send a message you need to use the same IMessagingComponentFactory and create a message sender:

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

Për serializimin dhe deserializimin e mesazheve, ka tri variante të gatshme: thjesht tekst, XML dhe JSON, por nëse është e nevojshme, mund të krijoni lehtësisht implementime të veta për ndërfaqet. IMessageSerializer dhe IMessageDeserializer.

Ne pĂ«rpiqemi tĂ« ruajmĂ« mundĂ«sitĂ« unike tĂ« çdo menaxheri tĂ« radhĂ«ve, pĂ«r shembull, ViennaNET.Messaging.MQSeriesQueue lejon dĂ«rgimin e jo vetĂ«m mesazheve tekst, por edhe mesazheve me byte, dhe ViennaNET.Messaging.RabbitMQQueue mbĂ«shtet routing dhe krijimin e radhĂ«ve “nĂ« flakĂ«â€. NĂ« mbĂ«shtjellĂ«sin tonĂ« tĂ« adapterit pĂ«r RabbitMQ, gjithashtu Ă«shtĂ« implementuar njĂ« ngjashmĂ«ri e RPC: dĂ«rgojmĂ« mesazh dhe presim njĂ« pĂ«rgjigje nga njĂ« radhĂ« e pĂ«rkohshme e veçantĂ«, e cila krijohet vetĂ«m pĂ«r njĂ« mesazh pĂ«rgjigje.

Ja shembuj përdorimi i radhëve me nuanca të rëndësishme të lidhjes.

ViennaNET.CallContext

Ne i përdorim radhët jo vetëm për integrim midis sistemeve të ndryshme, por edhe për komunikim midis mikrosherbimeve të një aplikacioni, për shembull, brenda sagës. Kjo ka sjellë nevojën për të dërguar së bashku me mesazhin të dhëna ndihmës, siç janë emri i përdoruesit, identifikuesi i kërkesës për regjistrimin e vazhdueshëm, adresa IP e burimit dhe të dhënat e autorizimit. Për të realizuar kalimin e këtyre të dhënave, ne kemi zhvilluar një bibliotekë ViennaNET.CallContext, e cila lejon ruajtjen e të dhënave nga kërkesa e ardhshme në shërbim. Në këtë rast, mënyra se si është bërë kërkesa, përmes radhës ose përmes Http, nuk ka rëndësi. Pas kësaj, para dërgimit të kërkesës ose mesazhit të dalshëm, të dhënat eksportohen nga konteksti dhe vendosen në tituj. Ashtu, shërbimi që pason merr të dhënat ndihmëse dhe në mënyrë të ngjashme i menaxhon ato.

Faleminderit për vëmendjen, presim komentet dhe kërkesat tuaja të tërheqjes!

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster