ViennaNET: eng Rei vu Bibliothéike fir de Backend. Deel 2

D'Raiffeisenbank .NET Entwéckler Communautéit weider kuerz den Inhalt vun ViennaNET iwwerschaffen. Iwwert wéi a firwat mir dozou komm sinn, Dir kënnt den éischten Deel liesen.

An dësem Artikel wäerte mir duerch nach ze betruechte Bibliothéike goen fir mat verdeelt Transaktiounen, Schlaangen an Datenbanken ze schaffen, déi an eisem GitHub Repository (Quellen sinn hei), an Nuget Packagen hei.

ViennaNET: eng Rei vu Bibliothéike fir de Backend. Deel 2

ViennaNET.Sagas

Wann e Projet op DDD a Mikroservicearchitektur wiesselt, da wann d'Geschäftslogik iwwer verschidde Servicer verdeelt gëtt, entsteet e Problem am Zesummenhang mat der Bedierfnes fir e verdeelt Transaktiounsmechanismus ëmzesetzen, well vill Szenarien dacks e puer Domänen gläichzäiteg beaflossen. Dir kënnt mat esou Mechanismen méi detailléiert kennen léieren, zum Beispill, am Buch "Microservices Patterns", Chris Richardson.

An eise Projeten hu mir en einfachen awer nëtzlechen Mechanismus ëmgesat: eng Saga, oder éischter eng Orchesterbaséiert Saga. Seng Essenz ass wéi follegt: et gëtt e bestëmmte Geschäftsszenario an deem et noutwendeg ass Operatiounen a verschiddene Servicer sequenziell auszeféieren, an am Fall vun Probleemer an all Schrëtt, ass et néideg d'Rollback-Prozedur vun alle fréiere Schrëtt ze ruffen, wou et gëtt zur Verfügung gestallt. Also, um Enn vun der Saga, onofhängeg vum Erfolleg, kréien mir konsequent Donnéeën an all Domainen.

Eis Ëmsetzung ass nach ëmmer a senger Basisform gemaach an ass net mat der Notzung vun Interaktiounsmethoden mat anere Servicer gebonnen. Et ass net schwéier ze benotzen: maacht just en Nofolger vun der Basis abstrakt Klass SagaBase<T>, wou T Är Kontextklass ass, an där Dir déi initial Donnéeën déi néideg ass fir d'Saga ze späicheren, souwéi e puer Tëscheresultater. D'Kontextinstanz gëtt op all Schrëtt wärend der Ausféierung duerchgefouert. Saga selwer ass eng stateless Klass, sou datt d'Instanz an DI als Singleton plazéiert ka ginn fir déi néideg Ofhängegkeeten ze kréien.

Beispill Annonce:

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

Beispill Uruff:

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

Voll Beispiller vu verschiddenen Implementatiounen kënne gekuckt ginn hei an Assemblée mat Tester.

ViennaNET.Orm.*

Eng Rei vu Bibliothéike fir mat verschiddenen Datenbanken iwwer Nhibernate ze schaffen. Mir benotzen d'DB-First Approche mat Liquibase, sou datt et nëmme Funktionalitéit gëtt fir mat Daten an enger fäerdeger Datebank ze schaffen.

ViennaNET.Orm.Seedwork и ViennaNET.Orm - Haaptversammlungen mat Basisinterfaces an hir Implementatioune respektiv. Loosst eis hir Inhalter méi detailléiert kucken.

Interface IEntityFactoryService an hir Ëmsetzung EntityFactoryService sinn den Haaptausgangspunkt fir mat der Datebank ze schaffen, well d'Unit of Work, Repositories fir mat spezifeschen Entitéiten ze schaffen, souwéi Exekutoren vu Kommandoen an direkt SQL Ufroen hei erstallt ginn. Heiansdo ass et bequem d'Kapazitéiten vun enger Klass ze limitéieren fir mat enger Datebank ze schaffen, zum Beispill, d'Fäegkeet ze bidden nëmmen Daten ze liesen. Fir esou Fäll IEntityFactoryService et gëtt e Virgänger - Interface IEntityRepositoryFactory, déi nëmmen eng Method deklaréiert fir Repositories ze kreéieren.

Fir direkt Zougang zu der Datebank ze kréien, gëtt de Providermechanismus benotzt. All DBMS déi mir an eisen Teams benotzen huet seng eege Implementatioun: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Zur selwechter Zäit kënne verschidde Ubidder gläichzäiteg an enger Applikatioun registréiert ginn, wat et zum Beispill am Kader vun engem Service erlaabt, ouni Käschten fir d'Ännerung vun der Infrastruktur, eng Schrëtt-fir-Schrëtt Migratioun auszeféieren. eng DBMS zu engem aneren. De Mechanismus fir déi erfuerderlech Verbindung ze wielen an dofir de Provider fir eng spezifesch Entitéitsklass (fir déi d'Mapping op d'Datebanktabelle geschriwwe gëtt) gëtt duerch d'Registrierung vun der Entitéit an der BoundedContext Klass ëmgesat (enthält eng Method fir d'Aschreiwung vun Domain Entitéiten) oder hiren Nofolger ApplicationContext (enthält Methoden fir d'Aschreiwung vun Applikatiounsentitéiten, direkt Ufroen a Kommandoen), wou de Verbindungsidentifizéierer aus der Konfiguratioun als Argument akzeptéiert gëtt:

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

Beispill Applikatioun Kontext:

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

Wann d'Verbindungs-ID net uginn ass, da gëtt d'Verbindung mam Numm "Standard" benotzt.

Direkt Kartéierung vun Entitéiten op Datebank Dëscher gëtt mat Standard NHibernate Tools implementéiert. Dir kënnt d'Beschreiwung souwuel duerch xml Dateien an duerch Klassen benotzen. Fir bequem Schreiwen vu Stéck Repositories an Eenheetstester gëtt et eng Bibliothéik ViennaNET.TestUtils.Orm.

Voll Beispiller vun benotzen ViennaNET.Orm.* kann fonnt ginn hei.

ViennaNET.Messaging.*

Eng Rei vu Bibliothéike fir mat Schlaangen ze schaffen.

Fir mat Schlaangen ze schaffen, gouf déiselwecht Approche gewielt wéi mat verschiddene DBMSen, nämlech déi maximal méiglech vereenegt Approche wat d'Aarbecht mat der Bibliothéik ugeet, onofhängeg vum benotzte Schlaangmanager. Bibliothéik ViennaNET.Messaging ass genee responsabel fir dës Unifikatioun, an ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue enthalen Adapterimplementatiounen fir IBM MQ, RabbitMQ a Kafka, respektiv.

Wann Dir mat Schlaangen schafft, ginn et zwee Prozesser: e Message kréien a schécken.

Betruecht ze kréien. Et ginn 2 Optiounen hei: fir kontinuéierlech nozelauschteren a fir eng eenzeg Noriicht ze kréien. Fir stänneg op d'Schlaang ze lauschteren, musst Dir als éischt d'Prozessorklass beschreiwen, déi ierflecher ass IMessageProcessor, déi verantwortlech ass fir d'Veraarbechtung vum erakommende Message. Als nächst muss et mat enger spezifescher Schlaang "verlinkt" sinn; dëst gëtt duerch Aschreiwung gemaach IQueueReactorFactory wat de Schlaangidentifizéierer aus der Konfiguratioun ugeet:

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

Beispill fir unzefänken ze lauschteren:

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

Dann, wann de Service ufänkt an d'Method genannt gëtt fir ze lauschteren, ginn all Messagen aus der spezifizéierter Schlaang an de entspriechende Prozessor.

Fir eng eenzeg Noriicht an enger Fabréck Interface ze kréien IMessagingComponentFactory et gëtt eng Method CreateMessageReceiverdeen en Empfänger erstellt, deen op e Message aus der Schlaang waart, déi him spezifizéiert ass:

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

Fir e Message ze schécken Dir musst déi selwecht benotzen IMessagingComponentFactory a erstellt e Message Sender:

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

Et ginn dräi fäerdeg Optiounen fir e Message ze serialiséieren an ze deserialiséieren: just Text, XML an JSON, awer wann néideg, kënnt Dir einfach Är eegen Interface-Implementatioune maachen IMessageSerializer и IMessageDeserializer.

Mir hu probéiert déi eenzegaarteg Fäegkeeten vun all Schlaangmanager ze erhaalen, z.B. ViennaNET.Messaging.MQSeriesQueue erlaabt Iech net nëmmen Text ze schécken, awer och Byte Messagen, an ViennaNET.Messaging.RabbitMQQueue ënnerstëtzt Routing an on-the-fly Schlaangen. Eise Adapterwrapper fir RabbitMQ implementéiert och e bësse RPC: mir schécken e Message a waarden op eng Äntwert vun enger spezieller temporärer Schlaang, déi nëmme fir eng Äntwertmeldung erstallt gëtt.

hei e Beispill fir Schlaangen mat Basisverbindungsnuancen ze benotzen.

ViennaNET.CallContext

Mir benotzen Schlaangen net nëmme fir Integratioun tëscht verschiddene Systemer, awer och fir Kommunikatioun tëscht Mikroservicer vun der selwechter Applikatioun, zum Beispill bannent enger Saga. Dëst huet zu der Bedierfnes gefouert fir zesumme mat der Noriicht sou Hëllefsdaten wéi de Benotzer Login ze vermëttelen, Ufro Identifizéierer fir Enn-zu-Enn Logbicher, Quell IP Adress an Autorisatiounsdaten. Fir d'Forwarding vun dësen Donnéeën ëmzesetzen, hu mir eng Bibliothéik entwéckelt ViennaNET.CallContext, wat Iech erlaabt Daten aus enger Demande an de Service ze späicheren. An dësem Fall ass et egal wéi d'Ufro gemaach gouf, duerch eng Schlaang oder iwwer Http. Dann, ier Dir déi erausginn Ufro oder Message schéckt, ginn d'Donnéeën aus dem Kontext geholl an an den Header gesat. Also kritt de nächste Service d'Hëllefsdaten a geréiert se op déiselwecht Manéier.

Merci fir Är Opmierksamkeet, mir freeën eis op Är Kommentaren an zitt Ufroen!

Source: will.com

Setzt e Commentaire