Dalam artikel ini, kita akan membahas perpustakaan yang belum dianggap untuk bekerja dengan transaksi terdistribusi, antrian dan database, yang dapat ditemukan di repositori kita di GitHub ( sumbernya ada di sini ), dan paket Nuget ada di sini .
ViennaNET.Sagas
Ketika sebuah proyek bertransisi ke DDD dan arsitektur layanan mikro, maka ketika logika bisnis tersebar di berbagai layanan, masalah muncul terkait dengan kebutuhan untuk mengimplementasikan mekanisme transaksi terdistribusi, karena banyak skenario sering memengaruhi beberapa domain sekaligus. Anda dapat mempelajari lebih lanjut tentang mekanisme tersebut, misalnya, dalam buku "Pola Layanan Mikro" oleh Chris Richardson .
Dalam proyek kami, kami telah menerapkan mekanisme yang sederhana namun berguna: saga, atau lebih tepatnya hikayat berdasarkan orkestrasi. Esensinya adalah sebagai berikut: ada skenario bisnis tertentu di mana diperlukan untuk melakukan operasi secara berurutan di layanan yang berbeda, sementara, jika ada masalah pada langkah apa pun, perlu memanggil prosedur rollback dari semua langkah sebelumnya, di mana itu disediakan. Jadi, di akhir saga, terlepas dari kesuksesannya, kami mendapatkan data yang konsisten di semua domain.
Implementasi kami masih mendasar dan tidak terikat dengan penggunaan metode interaksi apa pun dengan layanan lain. Tidak sulit untuk menggunakannya: itu cukup untuk mewarisi dari kelas abstrak dasar SagaBase <T>, di mana T adalah kelas konteks Anda, di mana Anda dapat menyimpan data awal yang diperlukan untuk saga agar berfungsi, serta beberapa hasil antara. Contoh konteks akan diteruskan ke semua langkah saat runtime. Saga itu sendiri adalah kelas stateless, sehingga instance dapat ditempatkan di DI sebagai Singleton untuk mendapatkan dependensi yang diperlukan.
Contoh deklarasi:
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 lengkap dari implementasi yang berbeda dapat ditemukan di sini dan di rakitan dengan tes .
ViennaNET.Orm. *
Satu set pustaka untuk bekerja dengan berbagai database melalui Nhibernate. Kami menggunakan pendekatan DB-First dengan penggunaan Liquibase, jadi hanya ada fungsionalitas untuk bekerja dengan data di database yang sudah jadi.
ViennaNET.Orm.Seedwork ViennaNET.Orm- rakitan utama yang masing-masing berisi antarmuka dasar dan implementasinya. Mari kita bahas konten mereka lebih detail.
Antarmuka
IEntityFactoryServicedan implementasinya EntityFactoryServiceadalah titik awal utama untuk bekerja dengan database, karena Unit Kerja, repositori untuk bekerja dengan entitas tertentu, serta pelaksana perintah dan kueri SQL langsung dibuat di sini. Terkadang nyaman untuk membatasi kapabilitas kelas untuk bekerja dengan database, misalnya, untuk mengaktifkan data hanya-baca. Untuk kasus seperti itu, ia IEntityFactoryServicememiliki leluhur - antarmuka IEntityRepositoryFactorydi mana hanya metode untuk membuat repositori yang dideklarasikan.
Untuk akses langsung ke database, mekanisme penyedia digunakan. Untuk masing-masing digunakan dalam tim database kami memiliki implementasi sendiri:
ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.
Pada saat yang sama, beberapa penyedia dapat didaftarkan dalam satu aplikasi pada saat yang sama, yang memungkinkan, misalnya, dalam kerangka satu layanan, tanpa biaya untuk memperbarui infrastruktur, migrasi langkah demi langkah dari satu DBMS ke DBMS lainnya. Mekanisme untuk memilih koneksi yang diperlukan dan, oleh karena itu, penyedia untuk kelas entitas tertentu (yang pemetaan ke tabel database ditulis) diimplementasikan melalui pendaftaran entitas di kelas BoundedContext (berisi metode untuk mendaftarkan entitas domain) atau penggantinya ApplicationContext (berisi metode untuk mendaftarkan entitas aplikasi , permintaan dan perintah langsung), di mana pengenal koneksi dari konfigurasi diambil sebagai argumen:
"db": [
{
"nick": "mssql_connection",
"dbServerType": "MSSQL",
"ConnectionString": "...",
"useCallContext": true
},
{
"nick": "oracle_connection",
"dbServerType": "Oracle",
"ConnectionString": "..."
}
],
Contoh ApplicationContext:
internal sealed class DbContext : ApplicationContext
{
public DbContext()
{
AddEntity<SomeEntity>("mssql_connection");
AddEntity<MigratedSomeEntity>("oracle_connection");
AddEntity<AnotherEntity>("oracle_connection");
}
}
Jika pengenal koneksi tidak ditentukan, koneksi bernama "default" akan digunakan.
Pemetaan langsung entitas ke tabel database diimplementasikan menggunakan alat NHibernate standar. Anda dapat menggunakan deskripsi melalui file xml dan melalui kelas. Untuk kemudahan menulis repositori rintisan dalam Tes unit, ada perpustakaan
ViennaNET.TestUtils.Orm.
Contoh lengkap penggunaan ViennaNET.Orm. * Dapat ditemukan di sini .
ViennaNET.Messaging. *
Satu set perpustakaan untuk bekerja dengan antrian.
Untuk bekerja dengan antrian, pendekatan yang sama dipilih dengan berbagai DBMS, yaitu, pendekatan terpadu yang paling mungkin dalam hal bekerja dengan perpustakaan, terlepas dari manajer antrian yang digunakan. Pustaka
ViennaNET.Messaginghanya bertanggung jawab untuk penyatuan ini, dan ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue ViennaNET.Messaging.KafkaQueueberisi implementasi adaptor untuk masing-masing IBM MQ, RabbitMQ, dan Kafka.
Ada dua proses dalam bekerja dengan antrian: menerima pesan dan mengirim.
Pertimbangkan untuk mendapatkan. Ada 2 opsi di sini: untuk mendengarkan terus-menerus dan untuk menerima satu pesan. Untuk terus mendengarkan antrian, pertama-tama Anda harus mendeskripsikan kelas prosesor yang diwarisi dari
IMessageProcessor, yang akan bertanggung jawab untuk memproses pesan masuk. Selanjutnya, itu harus "diikat" ke antrian tertentu, ini dilakukan dengan mendaftar IQueueReactorFactorydengan menentukan pengenal antrian dari konfigurasi:
"messaging": {
"ApplicationName": "MyApplication"
},
"rabbitmq": {
"queues": [
{
"id": "myQueue",
"queuename": "lalala",
...
}
]
},
Contoh mulai mendengarkan:
_queueReactorFactory.Register<MyMessageProcessor>("myQueue");
var queueReactor = queueReactorFactory.CreateQueueReactor("myQueue");
queueReactor.StartProcessing();
Kemudian, ketika layanan dimulai dan metode dipanggil untuk mulai mendengarkan, semua pesan dari antrian yang ditentukan akan masuk ke prosesor yang sesuai.
Untuk menerima satu pesan di antarmuka pabrik,
IMessagingComponentFactoryada metode CreateMessageReceiveryang akan membuat penerima menunggu pesan dari antrian yang ditentukan:
using (var receiver = _messagingComponentFactory.CreateMessageReceiver<TestMessage>("myQueue"))
{
var message = receiver.Receive();
}
Untuk mengirim pesan, Anda harus menggunakan yang sama
IMessagingComponentFactorydan membuat pengirim pesan:
using (var sender = _messagingComponentFactory.CreateMessageSender<MyMessage>("myQueue"))
{
sender.SendMessage(new MyMessage { Value = ...});
}
Ada tiga opsi siap pakai untuk membuat serial dan deserialisasi pesan: hanya teks, XML, dan JSON, tetapi jika perlu, Anda dapat dengan aman membuat implementasi antarmuka Anda sendiri
IMessageSerializer IMessageDeserializer.
Kami mencoba untuk mempertahankan kemampuan unik dari setiap manajer antrian, misalnya,
ViennaNET.Messaging.MQSeriesQueuememungkinkan pengiriman tidak hanya pesan teks, tetapi juga pesan byte, dan ViennaNET.Messaging.RabbitMQQueuemendukung perutean dan antrian dengan cepat. Pembungkus adaptor kami untuk RabbitMQ juga mengimplementasikan beberapa kemiripan dengan RPC: kami mengirim pesan dan menunggu respons dari antrian sementara khusus yang dibuat hanya untuk satu pesan respons.
Berikut adalah contoh penggunaan antrian dengan nuansa koneksi dasar .
ViennaNET.CallContext
Kami menggunakan antrian tidak hanya untuk integrasi antara sistem yang berbeda, tetapi juga untuk komunikasi antar layanan mikro dari satu aplikasi, misalnya, dalam kerangka sebuah saga. Hal ini menyebabkan kebutuhan untuk mentransfer bersama dengan pesan data tambahan seperti nama pengguna, ID permintaan untuk pencatatan ujung-ke-ujung, alamat ip sumber dan data otorisasi. Untuk mengimplementasikan penerusan data ini, perpustakaan dikembangkan
ViennaNET.CallContextyang memungkinkan penyimpanan data dari permintaan yang memasuki layanan. Dalam hal ini, bagaimana permintaan dibuat, melalui antrian atau melalui Http, tidak menjadi masalah. Kemudian, sebelum mengirim permintaan atau pesan keluar, data diambil dari konteks dan ditempatkan di tajuk. Jadi, layanan berikutnya menerima data tambahan dan membuangnya dengan cara yang sama.
Terima kasih atas perhatian Anda, kami menantikan komentar dan permintaan tarik Anda!