ViennaNET: un inseme di biblioteche per u backend. Parte 2

A cumunità di sviluppatori di Raiffeisenbank .NET cuntinueghja à rivisione brevemente u cuntenutu di ViennaNET. Circa cumu è perchè avemu ghjuntu à questu, pudete leghje a prima parte.

In questu articulu, andemu à traversu biblioteche chì anu da esse cunsideratu per travaglià cù transazzioni distribuite, file è basa di dati, chì ponu esse truvati in u nostru repository GitHub (e fonti sò quì), a Pacchetti Nuget quì.

ViennaNET: un inseme di biblioteche per u backend. Parte 2

ViennaNET.Sagas

Quandu un prughjettu cambia à DDD è l'architettura di microserviziu, allora quandu a logica di l'affari hè distribuita in diversi servizii, un prublema nasce in relazione à a necessità di implementà un mecanismu di transazzione distribuitu, perchè parechji scenarii spessu affettanu parechji domini à una volta. Pudete cunnosce tali miccanismi in più detail, per esempiu, in u libru "Microservices Patterns", Chris Richardson.

In i nostri prughjetti, avemu implementatu un mecanismu simplice ma utile: una saga, o megliu una saga basatu à l'orchestrazione. A so essenza hè a siguenti: ci hè un certu scenariu cummerciale in quale hè necessariu di realizà sequenzialmente operazioni in diversi servizii, è se ci sò prublemi in ogni passu, hè necessariu chjamà a prucedura di rollback per tutti i passi previ, induve hè furnitu. Cusì, à a fine di a saga, a priscinniri di successu, avemu ricevutu dati cunsistenti in tutti i duminii.

A nostra implementazione hè sempre fatta in a so forma basica è ùn hè micca ligata à l'usu di qualsiasi metudi di interazzione cù altri servizii. Ùn hè micca difficiule d'utilizà: fate solu un discendente di a classa astratta di basa SagaBase<T>, induve T hè a vostra classa di cuntestu in quale pudete almacenà e dati iniziali necessarii per a saga per travaglià, è ancu parechji risultati intermedi. L'istanza di u cuntestu serà passatu à tutti i passi durante l'esecuzione. Saga stessu hè una classa senza statu, cusì l'istanza pò esse piazzata in DI cum'è Singleton per uttene e dependenzii necessarii.

Esempiu di annunziu:

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

Esempiu di chjama:

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

Esempi cumpleti di diverse implementazioni ponu esse vistu ccà è in assemblea cù testi.

ViennaNET.Orm.*

Un inseme di biblioteche per travaglià cù diverse basa di dati via Nhibernate. Utilizemu l'approcciu DB-First cù Liquibase, cusì ci hè solu funziunalità per travaglià cù dati in una basa di dati pronta.

ViennaNET.Orm.Seedwork и ViennaNET.Orm - assemblei principali chì cuntenenu interfacce basi è e so implementazioni, rispettivamente. Fighjemu u so cuntenutu in più detail.

interfaccia IEntityFactoryService è a so implementazione EntityFactoryService sò u principale puntu di partenza per travaglià cù a basa di dati, postu chì l'Unità di u travagliu, repository per travaglià cù entità specifiche, è ancu esecutori di cumandamenti è dumande dirette SQL sò creati quì. A volte hè cunvenutu per limità e capacità di una classe per travaglià cù una basa di dati, per esempiu, per furnisce a capacità di leghje solu dati. Per tali casi IEntityFactoryService ci hè un antenatu - interfaccia IEntityRepositoryFactory, chì dichjara solu un metudu per creà repository.

Per accede direttamente à a basa di dati, u mecanismu di u fornitore hè utilizatu. Ogni DBMS chì usemu in i nostri squadre hà a so propria implementazione: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

À u stessu tempu, parechji fornituri ponu esse registrati in una sola applicazione à u stessu tempu, chì permette, per esempiu, in u quadru di un serviziu, senza alcunu costu per mudificà l'infrastruttura, per realizà una migrazione passu à passu da un DBMS à l'altru. U mecanismu per selezziunà a cunnessione necessaria è, per quessa, u fornitore per una classe di entità specifica (per a quale hè scritta a mappatura di e tabelle di basa di dati) hè implementatu da a registrazione di l'entità in a classa BoundedContext (contene un metudu per registrà entità di duminiu) o u so successore. ApplicationContext (contene metudi per registrà entità di l'applicazione, richieste dirette è cumandamenti), induve l'identificatore di cunnessione da a cunfigurazione hè accettatu cum'è argumentu:

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

Esempiu ApplicationContext:

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

Se l'ID di cunnessione ùn hè micca specificatu, allora a cunnessione chjamata "default" serà utilizata.

A mappatura diretta di entità à e tabelle di basa di dati hè implementata cù l'arnesi standard di NHibernate. Pudete aduprà a descrizzione sia attraversu i schedari xml sia attraversu e classi. Per una scrittura còmuda di i repositori stub in i testi di unità, ci hè una biblioteca ViennaNET.TestUtils.Orm.

Esempi cumpleti di usu ViennaNET.Orm.* ponu esse truvati ccà.

ViennaNET.Messaging.*

Un inseme di biblioteche per travaglià cù fila.

Per travaglià cù fila, u listessu approcciu hè statu sceltu cum'è cù diversi DBMS, vale à dì, l'approcciu unificatu massimu pussibule in quantu à travaglià cù a biblioteca, indipendentemente da u gestore di fila utilizatu. Biblioteca ViennaNET.Messaging hè precisamente rispunsevuli di sta unificazione, è ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue cuntene implementazioni di adattatori per IBM MQ, RabbitMQ è Kafka, rispettivamente.

Quandu u travagliu cù fila, ci sò dui prucessi: riceve un missaghju è mandà.

Cunsiderate di riceve. Ci sò 2 opzioni quì: per l'ascolta cuntinua è per riceve un missaghju unicu. Per ascultà in permanenza a fila, deve prima discrive a classa di processore ereditata da IMessageProcessor, chì serà rispunsevuli di trasfurmà u missaghju entrata. Dopu, deve esse "ligatu" à una fila specifica, questu hè fattu per a registrazione in IQueueReactorFactory indicà l'identificatore di fila da a cunfigurazione:

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

Esempiu di cumincià à sente:

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

Allora, quandu u serviziu principia è u metudu hè chjamatu per cumincià à sente, tutti i missaghji da a fila specificata andaranu à u processatore currispundente.

Per riceve un missaghju unicu in una interfaccia di fabbrica IMessagingComponentFactory ci hè un mètudu CreateMessageReceiverchì creà un destinatariu chì aspetta un missaghju da a fila specificata à ellu:

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

Per mandà un missaghju avete bisognu à aduprà u listessu IMessagingComponentFactory è crea un mittente di missaghju:

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

Ci hè trè opzioni pronti per serializà è deserializà un messagiu: solu testu, XML è JSON, ma se ne necessariu, pudete fà facilmente e vostre implementazioni di l'interfaccia. IMessageSerializer и IMessageDeserializer.

Avemu pruvatu à priservà e capacità uniche di ogni gestore di fila, per esempiu. ViennaNET.Messaging.MQSeriesQueue permette à voi à mandà micca solu testu, ma dinù missaghji byte, è ViennaNET.Messaging.RabbitMQQueue supporta l'instradamentu è a fila on-the-fly. U nostru wrapper di l'adattatore per RabbitMQ implementa ancu una certa simulazione di RPC: mandemu un missaghju è aspittemu una risposta da una fila temporale speciale, chì hè creata solu per un missaghju di risposta.

quì un esempiu di usu di fila cù sfumature di cunnessione basi.

ViennaNET.CallContext

Avemu aduprà fila micca solu per l'integrazione trà i diversi sistemi, ma ancu per a cumunicazione trà i microservizii di a listessa applicazione, per esempiu, in una saga. Questu hà purtatu à a necessità di trasmette cù u messagiu tali dati ausiliari cum'è u login di l'utilizatore, l'identificatore di dumanda per u logu end-to-end, l'indirizzu IP di l'origine è i dati d'autorizazione. Per implementà a trasmissione di sti dati, avemu sviluppatu una biblioteca ViennaNET.CallContext, chì permette di almacenà dati da una dumanda chì entra in u serviziu. In questu casu, a manera chì a dumanda hè stata fatta, attraversu una fila o via Http, ùn importa micca. Allora, prima di mandà a dumanda o missaghju in uscita, i dati sò pigliati da u cuntestu è posti in l'intestazione. Cusì, u prossimu serviziu riceve i dati ausiliari è gestisce in u listessu modu.

Grazie per a vostra attenzione, aspittemu i vostri cumenti è e dumande di pull !

Source: www.habr.com

Add a comment