ViennaNET: satu set perpustakaan untuk bahagian belakang. Bahagian 2

Komuniti pembangun Raiffeisenbank .NET terus menyemak secara ringkas kandungan ViennaNET. Mengenai bagaimana dan mengapa kami sampai kepada ini, anda boleh membaca bahagian pertama.

Dalam artikel ini, kami akan melalui perpustakaan yang belum dipertimbangkan untuk mengendalikan transaksi, baris gilir dan pangkalan data yang diedarkan, yang boleh didapati dalam repositori GitHub kami (sumber ada di sini), dan Pakej Nuget di sini.

ViennaNET: satu set perpustakaan untuk bahagian belakang. Bahagian 2

ViennaNET.Sagas

Apabila projek bertukar kepada DDD dan seni bina perkhidmatan mikro, maka apabila logik perniagaan diedarkan merentas perkhidmatan yang berbeza, masalah timbul berkaitan dengan keperluan untuk melaksanakan mekanisme transaksi teragih, kerana banyak senario sering menjejaskan beberapa domain sekaligus. Anda boleh berkenalan dengan mekanisme sedemikian dengan lebih terperinci, sebagai contoh, dalam buku "Corak Perkhidmatan Mikro", Chris Richardson.

Dalam projek kami, kami telah melaksanakan mekanisme yang mudah tetapi berguna: saga, atau lebih tepat saga berasaskan orkestrasi. Intipatinya adalah seperti berikut: terdapat senario perniagaan tertentu di mana ia perlu melakukan operasi secara berurutan dalam perkhidmatan yang berbeza, dan jika sebarang masalah timbul pada mana-mana langkah, adalah perlu untuk memanggil prosedur rollback untuk semua langkah sebelumnya, di mana ia adalah disediakan. Oleh itu, pada penghujung kisah, tanpa mengira kejayaan, kami menerima data yang konsisten dalam semua domain.

Pelaksanaan kami masih dibuat dalam bentuk asasnya dan tidak terikat dengan penggunaan sebarang kaedah interaksi dengan perkhidmatan lain. Ia tidak sukar untuk digunakan: cuma buat keturunan kelas abstrak asas SagaBase<T>, dengan T ialah kelas konteks anda di mana anda boleh menyimpan data awal yang diperlukan untuk saga berfungsi, serta beberapa hasil perantaraan. Contoh konteks akan diteruskan ke semua langkah semasa pelaksanaan. Saga sendiri ialah kelas tanpa kewarganegaraan, jadi contoh boleh diletakkan dalam DI sebagai Singleton untuk mendapatkan kebergantungan yang diperlukan.

Contoh iklan:

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

Contoh panggilan:

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

Contoh penuh pelaksanaan berbeza boleh dilihat di sini dan dalam perhimpunan dengan ujian.

ViennaNET.Orm.*

Satu set perpustakaan untuk bekerja dengan pelbagai pangkalan data melalui Nhibernate. Kami menggunakan pendekatan DB-First menggunakan Liquibase, jadi hanya ada fungsi untuk bekerja dengan data dalam pangkalan data sedia.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – perhimpunan utama yang mengandungi antara muka asas dan pelaksanaannya, masing-masing. Mari kita lihat kandungan mereka dengan lebih terperinci.

antara muka IEntityFactoryService dan pelaksanaannya EntityFactoryService adalah titik permulaan utama untuk bekerja dengan pangkalan data, kerana Unit Kerja, repositori untuk bekerja dengan entiti tertentu, serta pelaksana perintah dan pertanyaan SQL langsung dibuat di sini. Kadang-kadang adalah mudah untuk mengehadkan keupayaan kelas untuk bekerja dengan pangkalan data, sebagai contoh, untuk menyediakan keupayaan untuk hanya membaca data. Untuk kes sebegini IEntityFactoryService terdapat nenek moyang - antara muka IEntityRepositoryFactory, yang hanya mengisytiharkan kaedah untuk mencipta repositori.

Untuk mengakses pangkalan data secara langsung, mekanisme pembekal digunakan. Setiap DBMS yang kami gunakan dalam pasukan kami mempunyai pelaksanaannya sendiri: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Pada masa yang sama, beberapa pembekal boleh didaftarkan dalam satu aplikasi pada masa yang sama, yang membolehkan, sebagai contoh, dalam rangka kerja satu perkhidmatan, tanpa sebarang kos untuk mengubah suai infrastruktur, untuk menjalankan migrasi langkah demi langkah dari satu DBMS kepada yang lain. Mekanisme untuk memilih sambungan yang diperlukan dan, oleh itu, pembekal untuk kelas entiti tertentu (yang mana pemetaan kepada jadual pangkalan data ditulis) dilaksanakan melalui pendaftaran entiti dalam kelas BoundedContext (mengandungi kaedah untuk mendaftarkan entiti domain) atau penggantinya ApplicationContext (mengandungi kaedah untuk mendaftarkan entiti aplikasi , permintaan langsung dan arahan), di mana pengecam sambungan daripada konfigurasi diterima sebagai hujah:

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

Contoh Konteks Aplikasi:

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

Jika ID sambungan tidak dinyatakan, maka sambungan yang dinamakan "lalai" akan digunakan.

Pemetaan terus entiti ke jadual pangkalan data dilaksanakan menggunakan alat NHibernate standard. Anda boleh menggunakan penerangan melalui fail xml dan melalui kelas. Untuk memudahkan penulisan repositori rintisan dalam ujian Unit, terdapat perpustakaan ViennaNET.TestUtils.Orm.

Contoh penuh penggunaan ViennaNET.Orm.* boleh didapati di sini.

ViennaNET.Messaging.*

Satu set perpustakaan untuk bekerja dengan baris gilir.

Untuk bekerja dengan baris gilir, pendekatan yang sama telah dipilih seperti dengan pelbagai DBMS, iaitu pendekatan bersatu maksimum yang mungkin dari segi bekerja dengan perpustakaan, tanpa mengira pengurus baris gilir yang digunakan. Perpustakaan ViennaNET.Messaging bertanggungjawab dengan tepat untuk penyatuan ini, dan ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue mengandungi pelaksanaan penyesuai untuk IBM MQ, RabbitMQ dan Kafka, masing-masing.

Apabila bekerja dengan baris gilir, terdapat dua proses: menerima mesej dan menghantarnya.

Pertimbangkan untuk menerima. Terdapat 2 pilihan di sini: untuk mendengar berterusan dan untuk menerima satu mesej. Untuk sentiasa mendengar baris gilir, anda mesti terlebih dahulu menerangkan kelas pemproses yang diwarisi IMessageProcessor, yang akan bertanggungjawab untuk memproses mesej masuk. Seterusnya, ia mesti "dipautkan" kepada baris gilir tertentu; ini dilakukan melalui pendaftaran masuk IQueueReactorFactory menunjukkan pengecam baris gilir daripada konfigurasi:

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

Contoh mula mendengar:

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

Kemudian, apabila perkhidmatan bermula dan kaedah dipanggil untuk mula mendengar, semua mesej daripada baris gilir yang ditentukan akan pergi ke pemproses yang sepadan.

Untuk menerima satu mesej dalam antara muka kilang IMessagingComponentFactory ada kaedahnya CreateMessageReceiveryang akan mencipta penerima menunggu mesej daripada baris gilir yang ditentukan kepadanya:

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

Untuk menghantar mesej anda perlu menggunakan yang sama IMessagingComponentFactory dan buat penghantar mesej:

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

Terdapat tiga pilihan sedia untuk mensiri dan menyahsiri mesej: hanya teks, XML dan JSON, tetapi jika perlu, anda boleh membuat pelaksanaan antara muka anda sendiri dengan mudah IMessageSerializer и IMessageDeserializer.

Kami telah cuba mengekalkan keupayaan unik setiap pengurus baris gilir, mis. ViennaNET.Messaging.MQSeriesQueue membolehkan anda menghantar bukan sahaja teks, tetapi juga mesej bait, dan ViennaNET.Messaging.RabbitMQQueue menyokong penghalaan dan giliran dalam talian. Pembalut penyesuai kami untuk RabbitMQ juga melaksanakan beberapa kemiripan RPC: kami menghantar mesej dan menunggu respons daripada baris gilir sementara khas, yang dibuat hanya untuk satu mesej respons.

di sini ialah contoh penggunaan baris gilir dengan nuansa sambungan asas.

ViennaNET.CallContext

Kami menggunakan baris gilir bukan sahaja untuk penyepaduan antara sistem yang berbeza, tetapi juga untuk komunikasi antara perkhidmatan mikro aplikasi yang sama, contohnya, dalam saga. Ini membawa kepada keperluan untuk menghantar bersama-sama dengan mesej data tambahan seperti log masuk pengguna, pengecam permintaan untuk pengelogan hujung ke hujung, alamat IP sumber dan data kebenaran. Untuk melaksanakan pemajuan data ini, kami membangunkan perpustakaan ViennaNET.CallContext, yang membolehkan anda menyimpan data daripada permintaan yang memasuki perkhidmatan. Dalam kes ini, cara permintaan dibuat, melalui baris gilir atau melalui Http, tidak penting. Kemudian, sebelum menghantar permintaan atau mesej keluar, data diambil daripada konteks dan diletakkan dalam pengepala. Oleh itu, perkhidmatan seterusnya menerima data tambahan dan mengurusnya dengan cara yang sama.

Terima kasih atas perhatian anda, kami menantikan komen dan permintaan tarik anda!

Sumber: www.habr.com

Tambah komen