Layanan dapat dianggap sebagai proses yang didistribusikan dari waktu ke waktu, yang memiliki beberapa poin yang memungkinkan untuk mempengaruhi hasilnya (membatalkan melalui portal, menolak agensi, mengirim informasi tentang perubahan status layanan ke portal, dan juga mengirimkan hasil penyediaannya). Dalam hal ini, setiap layanan menjalani siklus hidupnya sendiri melalui proses ini, mengumpulkan data tentang permintaan pengguna, kesalahan yang diterima, hasil layanan, dll. Ini memungkinkan kapan saja untuk dapat mengontrol dan membuat keputusan tentang tindakan lebih lanjut untuk memproses layanan.
Kami akan berbicara lebih lanjut tentang bagaimana dan dengan bantuan apa Anda dapat mengatur pemrosesan tersebut.
Memilih mesin otomasi proses bisnis
Untuk mengatur pemrosesan data, ada perpustakaan dan sistem untuk mengotomatiskan proses bisnis, secara luasdi pasar: dari solusi tertanam hingga sistem berfitur lengkap yang menyediakan kerangka kerja untuk kontrol proses. Kami memilih Workflow Core sebagai alat untuk mengotomatiskan proses bisnis. Pilihan ini dibuat karena beberapa alasan: pertama, mesin ditulis dalam C # untuk platform .NET Core (ini adalah platform pengembangan utama kami), jadi lebih mudah untuk memasukkannya ke dalam garis besar produk secara keseluruhan, tidak seperti, misalnya, Camunda BPM. Selain itu, ini adalah mesin tertanam, yang memberikan banyak peluang untuk mengelola contoh proses bisnis. Kedua, di antara banyak opsi penyimpanan yang didukung, ada juga PostgreSQL yang digunakan dalam solusi kami. Ketiga, mesin menyediakan sintaks sederhana untuk mendeskripsikan proses dalam bentuk API yang lancar (ada juga varian untuk mendeskripsikan proses dalam file JSON, namun,tampaknya kurang nyaman digunakan karena menjadi sulit untuk mendeteksi kesalahan dalam deskripsi proses sebelum eksekusi sebenarnya).
Proses bisnis
Di antara alat yang diterima secara umum untuk menggambarkan proses bisnis, notasi BPMN harus diperhatikan . Misalnya, solusi untuk masalah FizzBuzz di notasi BPMN mungkin terlihat seperti ini:

Mesin Inti Alur Kerja berisi sebagian besar blok penyusun dan pernyataan yang disajikan dalam notasi dan, seperti yang disebutkan di atas, memungkinkan Anda menggunakan data API atau JSON yang lancar untuk menggambarkan proses tertentu. Implementasi proses ini melalui mesin Inti Alur Kerja dapat mengambil bentuk berikut :
// .
public class FizzBuzzWfData
{
public int Counter { get; set; } = 1;
public StringBuilder Output { get; set; } = new StringBuilder();
}
// .
public class FizzBuzzWorkflow : IWorkflow<FizzBuzzWfData>
{
public string Id => "FizzBuzz";
public int Version => 1;
public void Build(IWorkflowBuilder<FizzBuzzWfData> builder)
{
builder
.StartWith(context => ExecutionResult.Next())
.While(data => data.Counter <= 100)
.Do(a => a
.StartWith(context => ExecutionResult.Next())
.Output((step, data) => data.Output.Append(data.Counter))
.If(data => data.Counter % 3 == 0 || data.Counter % 5 == 0)
.Do(b => b
.StartWith(context => ExecutionResult.Next())
.Output((step, data) => data.Output.Clear())
.If(data => data.Counter % 3 == 0)
.Do(c => c
.StartWith(context => ExecutionResult.Next())
.Output((step, data) =>
data.Output.Append("Fizz")))
.If(data => data.Counter % 5 == 0)
.Do(c => c
.StartWith(context => ExecutionResult.Next())
.Output((step, data) =>
data.Output.Append("Buzz"))))
.Then(context => ExecutionResult.Next())
.Output((step, data) =>
{
Console.WriteLine(data.Output.ToString());
data.Output.Clear();
data.Counter++;
}));
}
}
}
Tentu saja, prosesnya dapat dijelaskan lebih sederhana dengan menambahkan keluaran dari nilai yang diinginkan tepat pada langkah-langkah setelah pemeriksaan kardinalitas. Namun, dengan implementasi saat ini, Anda dapat melihat bahwa setiap langkah dapat membuat beberapa perubahan pada "celengan" umum dari data proses, dan juga dapat memanfaatkan hasil dari langkah sebelumnya. Dalam hal ini, data proses disimpan dalam sebuah instance
FizzBuzzWfData, akses yang diberikan ke setiap langkah pada saat pelaksanaannya.
metode
Buildmengambil objek pembangun proses sebagai argumen, yang berfungsi sebagai titik awal untuk memanggil rangkaian metode ekstensi yang secara berurutan menjelaskan langkah-langkah proses bisnis. Metode ekstensi, pada gilirannya, dapat berisi deskripsi tindakan secara langsung dalam kode saat ini dalam bentuk ekspresi lambda yang diteruskan sebagai argumen, atau dapat dijadikan parameter. Dalam kasus pertama, yang disajikan dalam daftar, algoritme sederhana diterjemahkan ke dalam serangkaian instruksi yang agak rumit. Yang kedua, logika langkah-langkah disembunyikan di kelas terpisah yang mewarisi dari tipe Step(atau AsyncStepuntuk varian asinkron), yang memungkinkan Anda menyesuaikan proses yang kompleks ke dalam deskripsi yang lebih ringkas. Dalam prakteknya, pendekatan kedua tampaknya lebih cocok, yang pertama cukup untuk contoh sederhana atau proses bisnis yang sangat sederhana.
Kelas deskripsi proses yang sebenarnya mengimplementasikan antarmuka berparameter
IWorkflow, dan, menjalankan kontrak, berisi pengenal proses dan nomor versi. Berkat informasi ini, mesin dapat menghasilkan instance proses dalam memori, mengisinya dengan data dan memperbaiki statusnya di penyimpanan. Dukungan pembuatan versi memungkinkan Anda membuat variasi proses baru tanpa risiko memengaruhi instance yang ada di repositori. Untuk membuat versi baru, cukup membuat salinan dari deskripsi yang ada, menetapkan nomor berikutnya ke properti Versiondan mengubah perilaku proses ini sesuai kebutuhan (pengenal tidak boleh diubah).
Contoh proses bisnis dalam konteks tugas kita adalah:
- β .
- β , , .
- β .
- β , .
Seperti yang dapat Anda lihat dari contoh, semua proses secara kondisional dibagi lagi menjadi "siklik", yang pelaksanaannya melibatkan pengulangan berkala, dan "linier", dilakukan dalam konteks pernyataan tertentu dan, bagaimanapun, tidak mengecualikan keberadaan beberapa struktur siklik di dalam dirinya.
Mari kita pertimbangkan contoh salah satu proses yang bekerja dalam solusi kita untuk melakukan polling antrian permintaan yang masuk:
public class LoadRequestWf : IWorkflow<LoadRequestWfData>
{
public const string DefinitionId = "LoadRequest";
public string Id => DefinitionId;
public int Version => 1;
public void Build(IWorkflowBuilder<LoadRequestWfData> builder)
{
builder
.StartWith(then => ExecutionResult.Next())
.While(d => !d.Quit)
.Do(x => x
.StartWith<LoadRequestStep>() // *
.Output(d => d.LoadRequest_Output, s => s.Output)
.If(d => d.LoadRequest_Output.Exception != null)
.Do(then => then
.StartWith(ctx => ExecutionResult.Next()) // *
.Output((s, d) => d.Quit = true))
.If(d => d.LoadRequest_Output.Exception == null
&& d.LoadRequest_Output.Result.SmevReqType
== ReqType.Unknown)
.Do(then => then
.StartWith<LogInfoAboutFaultResponseStep>() // *
.Input((s, d) =>
{ s.Input = d.LoadRequest_Output?.Result?.Fault; })
.Output((s, d) => d.Quit = false))
.If(d => d.LoadRequest_Output.Exception == null
&& d.LoadRequest_Output.Result.SmevReqType
== ReqType.DataRequest)
.Do(then => then
.StartWith<StartWorkflowStep>() // *
.Input(s => s.Input, d => BuildEpguNewApplicationWfData(d))
.Output((s, d) => d.Quit = false))
.If(d => d.LoadRequest_Output.Exception == null
&& d.LoadRequest_Output.Result.SmevReqType == ReqType.Empty)
.Do(then => then
.StartWith(ctx => ExecutionResult.Next()) // *
.Output((s, d) => d.Quit = true))
.If(d => d.LoadRequest_Output.Exception == null
&& d.LoadRequest_Output.Result.SmevReqType
== ReqType.CancellationRequest)
.Do(then => then
.StartWith<StartWorkflowStep>() // *
.Input(s => s.Input, d => BuildCancelRequestWfData(d))
.Output((s, d) => d.Quit = false)));
}
}
Di baris yang ditandai dengan *, Anda dapat melihat penggunaan metode ekstensi berparameter, yang menginstruksikan mesin untuk menggunakan kelas langkah (lebih lanjut tentang itu nanti) sesuai dengan parameter tipe. Dengan bantuan metode ekstensi
Inputdan Outputkami memiliki kesempatan untuk mengatur data awal yang diteruskan ke langkah sebelum memulai eksekusi, dan, karenanya, mengubah data proses (dan mereka diwakili oleh sebuah instance kelas LoadRequestWfData) sehubungan dengan tindakan yang dilakukan oleh langkah tersebut. Dan seperti inilah prosesnya terlihat pada diagram BPMN:

Langkah
Seperti disebutkan di atas, masuk akal untuk menempatkan logika langkah-langkah dalam kelas yang terpisah. Selain membuat prosesnya lebih ringkas, ini memungkinkan Anda membuat langkah yang dapat digunakan kembali untuk operasi umum.
Menurut tingkat keunikan tindakan yang dilakukan dalam solusi kami, langkah-langkah tersebut dibagi menjadi dua kategori: umum dan khusus. Yang pertama dapat digunakan kembali dalam modul apa pun untuk proyek apa pun, sehingga ditempatkan di pustaka solusi bersama. Yang terakhir ini unik untuk setiap pelanggan, melalui ini tempat mereka di modul desain yang sesuai. Contoh langkah umum meliputi:
Mengirim permintaan Ack untuk mendapat tanggapan.
- Mengupload file ke penyimpanan file.
- Mengekstrak data dari paket SMEV, dll.
Langkah-langkah khusus:
- Penciptaan objek di IAS, memungkinkan operator untuk menyediakan layanan.
- .
- ..
Dalam menjelaskan langkah-langkah dalam proses tersebut, kami menganut prinsip tanggung jawab terbatas untuk setiap langkah. Hal ini memungkinkan untuk tidak menyembunyikan fragmen logika proses bisnis tingkat tinggi dalam langkah-langkah dan mengekspresikannya secara eksplisit dalam deskripsi proses. Misalnya, jika kesalahan ditemukan dalam data aplikasi, perlu untuk mengirim pesan tentang penolakan untuk memproses aplikasi ke SMEV, maka blok yang sesuai dari kondisi tersebut akan ditempatkan tepat di kode proses bisnis, dan kelas yang berbeda akan sesuai dengan langkah-langkah untuk menentukan fakta kesalahan dan menanggapinya.
Perlu dicatat bahwa langkah-langkah harus didaftarkan dalam wadah ketergantungan, sehingga mesin akan dapat menggunakan contoh langkah-langkah saat setiap proses bergerak melalui siklus hidupnya.
Setiap langkah adalah tautan penghubung antara kode yang berisi deskripsi proses tingkat tinggi, dan kode yang memecahkan masalah aplikasi - layanan.
Jasa
Layanan mewakili tingkat pemecahan masalah berikutnya yang lebih rendah. Setiap langkah dalam pelaksanaan tugasnya bergantung, sebagai aturan, pada satu atau lebih layanan (NB Konsep "layanan" dalam konteks ini lebih dekat dengan konsep analogi "layanan tingkat aplikasi" dari domain desain khusus domain (DDD)).
Contoh layanan adalah:
- Layanan untuk menerima tanggapan dari antrian tanggapan SMEV menyiapkan paket data yang sesuai dalam format SOAP, mengirimkannya ke SMEV dan mengubah tanggapan tersebut ke dalam bentuk yang sesuai untuk diproses lebih lanjut.
- Layanan untuk mengunduh file dari gudang SMEV - menyediakan pembacaan file yang dilampirkan ke aplikasi dari portal dari gudang file menggunakan protokol FTP.
- Layanan untuk mendapatkan hasil dari penyediaan layanan - membaca data hasil layanan dari IAS dan membentuk objek yang sesuai, atas dasar itu layanan lain akan membuat permintaan SOAP untuk dikirim ke portal.
- Layanan untuk mengunggah file yang terkait dengan hasil layanan penyimpanan file SMEV.
Layanan dalam solusi dibagi menjadi beberapa kelompok berdasarkan sistem, interaksi yang mereka sediakan:
- Layanan SMEV.
- Layanan IAS.
Layanan untuk bekerja dengan infrastruktur internal dari solusi integrasi (mencatat informasi tentang paket data, menghubungkan entitas solusi integrasi dengan objek IAS, dll.).
Dalam istilah arsitektural, layanan adalah level terendah, namun, mereka juga dapat mengandalkan kelas utilitas untuk memecahkan masalah mereka. Jadi, misalnya, dalam solusi ada lapisan kode yang memecahkan masalah serialisasi dan deserialisasi paket data SOAP untuk berbagai versi protokol SMEV. Secara umum, uraian di atas dapat diringkas dalam diagram kelas:

Antarmuka
IWorkflowdan kelas abstrak berhubungan langsung dengan mesin StepBodyAsync(namun, Anda dapat menggunakan StepBody analog sinkron). Diagram di bawah menunjukkan implementasi "blok penyusun" - kelas beton dengan deskripsi proses bisnis Alur Kerja dan langkah-langkah yang digunakan di dalamnya ( Step). Di tingkat yang lebih rendah, layanan disajikan, yang, pada dasarnya, sudah spesifik untuk implementasi solusi khusus ini dan, tidak seperti proses dan langkah, tidak wajib.
Layanan, seperti langkah-langkah, harus didaftarkan dalam wadah ketergantungan sehingga langkah-langkah yang menggunakan layanan mereka bisa mendapatkan contoh yang diperlukan dengan injeksi melalui konstruktor.
Menanamkan mesin dalam larutan
Pada saat dimulainya pembuatan sistem integrasi dengan portal, versi 2.1.2 mesin sudah tersedia di gudang Nuget. Itu dibangun ke dalam wadah ketergantungan dengan cara standar dalam metode
ConfigureServiceskelas Startup:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddWorkflow(opts =>
opts.UsePostgreSQL(connectionString, false, false, schemaName));
// ...
}
Mesin dapat dikonfigurasi untuk salah satu gudang data yang didukung (ada yang lain di antaranya : MySQL, MS SQL, SQLite, MongoDB). Dalam kasus PostgreSQL, mesin menggunakan Entity Framework Core dalam varian Code First untuk bekerja dengan proses. Karenanya, jika ada database kosong, dimungkinkan untuk menerapkan migrasi dan mendapatkan struktur tabel yang diinginkan. Penggunaan migrasi bersifat opsional, dapat dikontrol menggunakan argumen metode
UsePostgreSQL: argumen jenis boolean kedua ( canCreateDB) dan ketiga ( canMigrateDB) memungkinkan Anda memberi tahu mesin apakah ia dapat membuat database jika tidak ada dan menerapkan migrasi.
Karena dengan pembaruan mesin berikutnya, ada kemungkinan tidak nol untuk mengubah model datanya, dan penggunaan terkait migrasi berikutnya dapat merusak data yang sudah terakumulasi, kami memutuskan untuk meninggalkan opsi ini dan mempertahankan struktur basis data sendiri, berdasarkan mekanisme komponen basis data yang digunakan dalam proyek kami yang lain.
Jadi, masalah menyimpan data dan mendaftarkan mesin dalam wadah ketergantungan telah diselesaikan, mari kita lanjutkan ke menghidupkan mesin. Untuk tugas ini, opsi layanan yang dihosting muncul, dan di sinilihat contoh kelas dasar untuk membuat layanan semacam itu). Kode yang diambil sebagai dasar sedikit dimodifikasi untuk mempertahankan modularitas, yang berarti membagi solusi integrasi (disebut "Onyx") menjadi bagian umum yang menyediakan inisialisasi mesin dan pelaksanaan beberapa prosedur layanan, dan bagian khusus untuk setiap pelanggan tertentu (modul integrasi) ...
Setiap modul berisi deskripsi proses, infrastruktur untuk menjalankan logika bisnis, serta beberapa kode terpadu untuk memungkinkan sistem integrasi yang dikembangkan mengenali dan memuat deskripsi proses secara dinamis ke dalam instance mesin Workflow Core:

Pendaftaran dan peluncuran proses bisnis
Sekarang setelah kita memiliki deskripsi proses bisnis yang sudah jadi dan mesin yang terhubung ke solusi, sekarang saatnya memberi tahu mesin tentang proses apa yang akan dikerjakannya.
Ini dilakukan dengan menggunakan kode berikut, yang dapat ditemukan dalam layanan yang disebutkan sebelumnya (kode yang memulai pendaftaran proses dalam modul yang terhubung juga dapat ditempatkan di sini):
public async Task RunWorkflowsAsync(IWorkflowHost host,
CancellationToken token)
{
host.RegisterWorkflow<LoadRequestWf, LoadRequestWfData>();
// ...
await host.StartAsync(token);
token.WaitHandle.WaitOne();
host.Stop();
}
Kesimpulan
Secara umum, kami membahas langkah-langkah yang perlu Anda ambil untuk menggunakan Workflow Core dalam solusi integrasi. Mesin tersebut memungkinkan Anda untuk mendeskripsikan proses bisnis dengan cara yang fleksibel dan nyaman. Mengingat fakta bahwa kita berurusan dengan tugas integrasi dengan portal "Gosuslug" melalui SMEV, diharapkan proses bisnis yang diproyeksikan akan mencakup berbagai tugas yang cukup beragam (polling antrian, unggah / unduh file, memastikan kepatuhan dengan protokol pertukaran dan memastikan konfirmasi penerimaan data, penanganan kesalahan pada tahapan yang berbeda, dll.). Oleh karena itu, akan sangat wajar untuk mengharapkan terjadinya beberapa momen implementasi yang sekilas tidak terlihat, dan kepada merekalah kita akan mencurahkan artikel terakhir berikutnya dari siklus tersebut.