ViennaNET: një grup bibliotekash për backend. Pjesa 2

Komuniteti i zhvilluesve të Raiffeisenbank .NET vazhdon të shqyrtojë shkurtimisht përmbajtjen e ViennaNET. Rreth asaj se si dhe pse arritëm në këtë, mund të lexoni pjesën e parë.

Në këtë artikull, ne do të kalojmë nëpër biblioteka që ende nuk janë konsideruar për të punuar me transaksionet e shpërndara, radhët dhe bazat e të dhënave, të cilat mund të gjenden në depon tonë të GitHub (burimet janë këtu), dhe Paketat nuget këtu.

ViennaNET: një grup bibliotekash për backend. Pjesa 2

VjenëNET.Sagas

Kur një projekt kalon në arkitekturën DDD dhe mikroservice, atëherë kur logjika e biznesit shpërndahet nëpër shërbime të ndryshme, lind një problem që lidhet me nevojën për të zbatuar një mekanizëm transaksioni të shpërndarë, sepse shumë skenarë shpesh prekin disa fusha në të njëjtën kohë. Ju mund të njiheni me mekanizma të tillë në më shumë detaje, për shembull, në librin "Modelet e Microservices", Chris Richardson.

Në projektet tona, ne kemi zbatuar një mekanizëm të thjeshtë por të dobishëm: një sagë, ose më mirë një sagë me bazë orkestrimi. Thelbi i tij është si më poshtë: ekziston një skenar i caktuar biznesi në të cilin është e nevojshme të kryhen në mënyrë sekuenciale operacionet në shërbime të ndryshme, dhe nëse lindin ndonjë problem në ndonjë hap, është e nevojshme të thirret procedura e rikthimit për të gjithë hapat e mëparshëm, ku është me kusht. Kështu, në fund të sagës, pavarësisht nga suksesi, ne marrim të dhëna të qëndrueshme në të gjitha fushat.

Implementimi ynë është bërë ende në formën e tij bazë dhe nuk është i lidhur me përdorimin e ndonjë metode ndërveprimi me shërbime të tjera. Nuk është e vështirë për t'u përdorur: thjesht bëni një pasardhës të klasës abstrakte bazë SagaBase<T>, ku T është klasa juaj e kontekstit në të cilën mund të ruani të dhënat fillestare të nevojshme për funksionimin e sagës, si dhe disa rezultate të ndërmjetme. Shembulli i kontekstit do të kalohet në të gjitha hapat gjatë ekzekutimit. Saga në vetvete është një klasë pa shtetësi, kështu që shembulli mund të vendoset në DI si një Singleton për të marrë varësitë e nevojshme.

Shembull i reklamës:

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

Shembull i thirrjes:

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

Mund të shihen shembuj të plotë të zbatimeve të ndryshme këtu dhe në montim me testet.

ViennaNET.Orm.*

Një grup bibliotekash për të punuar me baza të të dhënave të ndryshme nëpërmjet Nhibernate. Ne përdorim qasjen DB-First duke përdorur Liquibase, kështu që ka vetëm funksionalitet për të punuar me të dhëna në një bazë të dhënash të gatshme.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – asambletë kryesore që përmbajnë ndërfaqet bazë dhe zbatimet e tyre, përkatësisht. Le të shohim përmbajtjen e tyre në mënyrë më të detajuar.

ndërfaqe IEntityFactoryService dhe zbatimin e tij EntityFactoryService janë pika kryesore e fillimit për të punuar me bazën e të dhënave, pasi këtu krijohen njësia e punës, depo për të punuar me entitete specifike, si dhe ekzekutues komandash dhe pyetje direkte SQL. Ndonjëherë është i përshtatshëm për të kufizuar aftësitë e një klase për të punuar me një bazë të dhënash, për shembull, për të siguruar aftësinë për të lexuar vetëm të dhëna. Për raste të tilla IEntityFactoryService ekziston një ndërfaqe paraardhëse IEntityRepositoryFactory, i cili deklaron vetëm një metodë për krijimin e depove.

Për të hyrë drejtpërdrejt në bazën e të dhënave, përdoret mekanizmi i ofruesit. Çdo DBMS që përdorim në ekipet tona ka zbatimin e vet: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Në të njëjtën kohë, disa ofrues mund të regjistrohen në një aplikacion në të njëjtën kohë, gjë që lejon, për shembull, brenda kornizës së një shërbimi, pa asnjë kosto për modifikimin e infrastrukturës, të kryejë një migrim hap pas hapi nga një DBMS në një tjetër. Mekanizmi për zgjedhjen e lidhjes së kërkuar dhe, për rrjedhojë, ofruesit për një klasë specifike të njësisë (për të cilën është shkruar hartimi në tabelat e bazës së të dhënave) zbatohet përmes regjistrimit të entitetit në klasën BoundedContext (përmban një metodë për regjistrimin e entiteteve të domenit) ose pasardhësin e tij. ApplicationContext (përmban metoda për regjistrimin e entiteteve të aplikacionit, kërkesa të drejtpërdrejta dhe komanda), ku identifikuesi i lidhjes nga konfigurimi pranohet si argument:

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

Shembull ApplicationContext:

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

Nëse ID-ja e lidhjes nuk është e specifikuar, atëherë do të përdoret lidhja me emrin "default".

Hartëzimi i drejtpërdrejtë i entiteteve në tabelat e bazës së të dhënave zbatohet duke përdorur mjete standarde NHibernate. Ju mund ta përdorni përshkrimin si përmes skedarëve xml ashtu edhe përmes klasave. Për shkrim të përshtatshëm të depove të cungëve në testet e njësisë, ekziston një bibliotekë ViennaNET.TestUtils.Orm.

Mund të gjenden shembuj të plotë të përdorimit të ViennaNET.Orm.* këtu.

ViennaNET.Mesazhet.*

Një grup bibliotekash për të punuar me radhë.

Për të punuar me radhë, u zgjodh e njëjta qasje si me DBMS të ndryshme, përkatësisht, qasja maksimale e mundshme e unifikuar për sa i përket punës me bibliotekën, pavarësisht nga menaxheri i radhës i përdorur. Librari ViennaNET.Messaging është pikërisht përgjegjës për këtë bashkim, dhe ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue përmbajnë zbatime të përshtatësve për IBM MQ, RabbitMQ dhe Kafka, përkatësisht.

Kur punoni me radhë, ekzistojnë dy procese: marrja e një mesazhi dhe dërgimi i tij.

Merrni parasysh marrjen. Këtu ka 2 opsione: për dëgjim të vazhdueshëm dhe për marrjen e një mesazhi të vetëm. Për të dëgjuar vazhdimisht radhën, së pari duhet të përshkruani klasën e procesorit të trashëguar nga IMessageProcessor, i cili do të jetë përgjegjës për përpunimin e mesazhit në hyrje. Më pas, duhet të "lidhet" me një radhë specifike; kjo bëhet përmes regjistrimit në IQueueReactorFactory duke treguar identifikuesin e radhës nga konfigurimi:

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

Shembull i fillimit të dëgjimit:

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

Pastaj, kur shërbimi fillon dhe metoda thirret për të filluar dëgjimin, të gjitha mesazhet nga radha e specifikuar do të shkojnë në procesorin përkatës.

Për të marrë një mesazh të vetëm në një ndërfaqe të fabrikës IMessagingComponentFactory ka një metodë CreateMessageReceiveri cili do të krijojë një marrës që pret një mesazh nga radha e specifikuar për të:

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

Për të dërguar një mesazh ju duhet të përdorni të njëjtën gjë IMessagingComponentFactory dhe krijoni një dërgues mesazhi:

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

Ekzistojnë tre opsione të gatshme për serializimin dhe deserializimin e një mesazhi: vetëm tekst, XML dhe JSON, por nëse është e nevojshme, mund të bëni lehtësisht implementimet tuaja të ndërfaqes IMessageSerializer и IMessageDeserializer.

Jemi përpjekur të ruajmë aftësitë unike të çdo menaxheri të radhës, p.sh. ViennaNET.Messaging.MQSeriesQueue ju lejon të dërgoni jo vetëm mesazhe me tekst, por edhe me byte, dhe ViennaNET.Messaging.RabbitMQQueue mbështet rrugëzimin dhe radhën në fluturim. Mbështjellësi ynë i përshtatësit për RabbitMQ zbaton gjithashtu një ngjashmëri të RPC: ne dërgojmë një mesazh dhe presim një përgjigje nga një radhë e veçantë e përkohshme, e cila krijohet vetëm për një mesazh përgjigjeje.

Këtu një shembull i përdorimit të radhëve me nuanca bazë të lidhjes.

ViennaNET.CallContext

Ne përdorim radhët jo vetëm për integrimin midis sistemeve të ndryshme, por edhe për komunikimin midis mikroshërbimeve të të njëjtit aplikacion, për shembull, brenda një sage. Kjo çoi në nevojën për të transmetuar së bashku me mesazhin të dhëna të tilla ndihmëse si identifikimi i përdoruesit, identifikuesi i kërkesës për regjistrimin nga fundi në fund, adresa IP e burimit dhe të dhënat e autorizimit. Për të zbatuar përcjelljen e këtyre të dhënave, ne krijuam një bibliotekë ViennaNET.CallContext, i cili ju lejon të ruani të dhënat nga një kërkesë që hyn në shërbim. Në këtë rast, si është bërë kërkesa, përmes një radhe apo përmes Http, nuk ka rëndësi. Më pas, përpara se të dërgoni kërkesën ose mesazhin dalës, të dhënat merren nga konteksti dhe vendosen në titujt. Kështu, shërbimi tjetër merr të dhënat ndihmëse dhe i menaxhon ato në të njëjtën mënyrë.

Faleminderit për vëmendjen tuaj, presim me padurim komentet dhe kërkesat tuaja!

Burimi: www.habr.com

Shto një koment