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

Komuniteti i zhvilluesve .NET të Raiffeisenbank po vazhdon një përmbledhje të shkurtër të përmbajtjes së ViennaNET. Rreth asaj se si dhe pse arritëm në këtë pikë, mund ta lexoni në pjesën e parë.

Në këtë artikull do të shqyrtojmë biblioteka të tjera për punën me transaksione të shpërndara, radhët dhe DB-të, të cilat mund të gjenden në depot tona në GitHub (burimet ndodhen këtu), ndërsa paketat Nuget këtu.

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

ViennaNET.Sagas

Kur një projekt kalon në DDD dhe arkitekturë mikros-services, ndahen logjikat e biznesit në shërbime të ndryshme, lind problemi i realizimit të mekanizmit të transaksioneve të shpërndara, pasi shumë skenarë shpesh përfshijnë disa domenë. Për këto mekanizma mund të njiheni më shumë, për shembull, në librin «Microservices Patterns», Chris Richardson.

Në projektet tona kemi realizuar një mekanizëm të thjeshtë, por të dobishëm: saga, më saktësisht saga mbi bazën e orkestrimit. Esenca e saj është si vijon: ka një skenar biznesi, në të cilin është e nevojshme të realizoni operacione në një rend të caktuar në shërbime të ndryshme, dhe në rast se ndodhin ndonjë problem në çdo hap, është e nevojshme të thirret procedura e kthimit të të gjithë hapave të mëparshëm, kur ajo është e parashikuar. Kështu, në fund të përfundimit të sagas, pavarësisht nga suksesi, ne marrim të dhëna konsistente në të gjitha domenët.

Realizimi ynë për momentin është bërë në formën bazike dhe nuk është i lidhur me përdorimin e ndonjë mënyre ndërveprimi me shërbime të tjera. Ta aplikosh është e lehtë: mjafton të krijosh një trashëgimtar nga klasa bazë abstrakte SagaBase, ku T është klasa juaj e kontekstit, në të cilin mund të mbani të dhëna origjinale të nevojshme për punën e sagas, si dhe disa rezultate ndërmjetëse. Një instancë e kontekstit do të kalojë në të gjithë hapat gjatë ekzekutimit. Vetë saga është një klasë stateless, prandaj instanca mund të vendoset në DI si Singleton, për të marrë 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 => ...);
  }
}

Shembuj thirrjeje:

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

Shembujt e plotë të realizimeve të ndryshme mund të shihen këtu edhe në paketën me teste.

ViennaNET.Orm.*

Grup bibliotekash për punën me DB-e të ndryshme përmes Nhibernate. Ne përdorim qasjen DB-First me përdorimin e Liquibase, prandaj këtu është e pranishme vetëm funksionaliteti për punën me të dhënat në DB-në e gatshme.

ViennaNET.Orm.Seedwork dhe ViennaNET.Orm janë paketat kryesore, që përmbajnë interfaces bazë dhe realizimet e tyre përkatëse. Le të ndalemi në përmbajtjen e tyre më në detaje.

Interfaci IEntityFactoryService dhe realizimi i tij EntityFactoryService janĂ« pika kryesore pĂ«r punĂ«n me DB-nĂ«, pasi kĂ«tu krijohet Unit of Work, repository pĂ«r punĂ«n me entitete specifike, si dhe ekzekutorĂ«t pĂ«r komandat dhe kĂ«rkesat SQL tĂ« drejtpĂ«rdrejta. NdonjĂ«herĂ« Ă«shtĂ« e dobishme tĂ« kufizoni mundĂ«sitĂ« e klasĂ«s pĂ«r punĂ«n me DB-nĂ«, pĂ«r shembull, duke lejuar vetĂ«m pĂ«r tĂ« lexuar tĂ« dhĂ«nat. PĂ«r kĂ«to raste, IEntityFactoryService ka njĂ« paraardhĂ«s – interfacin IEntityRepositoryFactory, nĂ« tĂ« cilin Ă«shtĂ« shpallur vetĂ«m metoda pĂ«r krijimin e repository-eve.

Për qasjen direkte në DB përdoret mekanizmi i ofruesve. Për secilën DBMS të përdorur në grupet tona ekziston realizimi i vet: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Në këtë mënyrë, në një aplikacion mund të regjistrohen disa ofrues në të njëjtën kohë, gjë që lejon, për shembull, të realizosh 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 i zgjedhjes së lidhjes së nevojshme dhe, për rrjedhojë, ofruesit për një klasë-entiteti të caktuar (për të cilën do të shkruhet mapimi në tabelat e DB-së) është realizuar përmes regjistrimit të entitetit në klasën BoundedContext (përmban metodën për regjistrimin e entiteteve domain) ose trashëgimtarit të saj ApplicationContext (përmban metodat për regjistrimin e entiteteve aplikacionale, kërkesave të drejtpërdrejta dhe komandave), ku si argument merret identifikuesi i lidhjes nga konfigurimi:

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

Shembulli i ApplicationContext:

internal sealed class DbContext : ApplicationContext
{
  public DbContext()
  {
    AddEntity("mssql_connection");
    AddEntity("oracle_connection");
    AddEntity("oracle_connection");
  }
}

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

Mapping i entiteteve në tabelat e DB realizohet me mjete standarde të NHibernate. Mund të përdoren përshkrime si përmes skedarëve XML ashtu edhe përmes klasave. Për lehtësimin e shkruarjes së repositorëve të mekanizmit në testet e njësisë, ekziston një bibliotekë ViennaNET.TestUtils.Orm.

Shembuj të plotë të përdorimit të ViennaNET.Orm.* mund të gjenden këtu.

ViennaNET.Messaging.*

Një grup bibliotekash për punën me radhët.

Për punën me radhët u zgjodh një qasje e ngjashme si me sistemet e ndryshme të menaxhimit të bazave të dhënash, pra një qasje sa më e unifikuar e mundshme nga pikëpamja e punës me bibliotekën, pavarësisht menaxherit të radhës që përdoret. Biblioteka ViennaNET.Messaging përgjigjet për këtë unifikim, dhe ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue dhe ViennaNET.Messaging.KafkaQueue përmbajnë implementimet e adaptuesve për IBM MQ, RabbitMQ dhe Kafka përkatësisht.

Në punën me radhët ka dy procese: marrja e mesazheve dhe dërgimi.

Le të shqyrtojmë marrjen. Këtu ka 2 mundësi: për dëgjim të vazhdueshëm dhe për marrjen e një mesazhi të vetëm. Për dëgjimin e vazhdueshëm të radhës, fillimisht duhet të përshkruhet një klasë procesori, e trashëguar nga IMessageProcessor, e cila do të përgjigjet për përpunimin e mesazhit të ardhshëm. Më pas, duhet ta "lidhnim" atë me një radhë të caktuar, që bëhet përmes regjistrimit në IQueueReactorFactory duke specifikuar identifikuesin e radhës nga konfigurimi:

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

Shembulli i fillimit të dëgjimit:

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

Më pas, gjatë fillimit të shërbimit dhe thirrjes së metodës për fillimin e dëgjimit, të gjitha mesazhet nga radhë e caktuar do të kalojnë në procesorin përkatës.

Për të marrë një mesazh të vetëm, në ndërfaqen e fabrikës IMessagingComponentFactory ka një metodë CreateMessageReceiver, e cila do të krijojë një marrës që pret mesazhe nga radhë e caktuar:

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

Për dërgimin e një mesazhi nevojitet të përdoret gjithashtu IMessagingComponentFactory dhe të krijoni një dërgues mesazhesh:

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ë realizoni pa problem përgatitjet tuaja për interfecat IMessageSerializer dhe IMessageDeserializer.

Ne përpiqemi të ruajmë mundësitë unike të secilit menaxher të radhës, për shembull, ViennaNET.Messaging.MQSeriesQueue lejon dërgimin jo vetëm të mesazheve tekstuale, por edhe të atyre me byte, dhe ViennaNET.Messaging.RabbitMQQueue mbështet rihapjen dhe krijimin e radhëve "në flutur". Në mbulesën tonë të adaptuesit për RabbitMQ gjithashtu është realizuar një lloj RPC: dërgoni një mesazh dhe prisni një përgjigje nga një radhë speciale përkohshme, e cila krijohet vetëm për një mesazh përgjigje.

Këtu është shembulli i përdorimit të radhëve me nuancat kryesore të lidhjes.

ViennaNET.CallContext

Ne 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 sagi. Kjo çoi në nevojën për dërgimin e të dhënave ndihmëse si p.sh. emri i përdoruesit, identifikuesi i kërkesës për regjistrimin e log-ëve, adresa IP e burimit dhe të dhënat e autorizimit. Për realizimin e këtyre të dhënave u zhvillua një bibliotekë ViennaNET.CallContext, e cila lejon ruajtjen e të dhënave nga kërkesa e ardhur në shërbim. Në këtë mënyrë, mënyra se si është bërë kërkesa, përmes radhës apo përmes Http, nuk ka rëndësi. Më pas, para dërgimit të kërkesës ose mesazhit, të dhënat nxirren nga konteksti dhe vendosen në tituj. Kështu, shërbimi i ardhshëm merr të dhënat ndihmëse dhe i përdor ato po ashtu.

Faleminderit për vëmendjen, presim komentet dhe pull request-et tuaja!

Burimi: habr.com

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