A cumunità di sviluppatori di Raiffeisenbank .NET cuntinueghja à rivisione brevemente u cuntenutu di ViennaNET. Circa cumu è perchè avemu ghjuntu à questu,
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 (
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 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
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
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 CreateMessageReceiver
chì 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ì
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