Menerapkan Arsitektur Bersih di Layanan Mikro

Halo, Habr!



Banyak proyek sekarang menggunakan arsitektur layanan mikro. Kami juga tidak terkecuali, dan selama lebih dari 2 tahun sekarang kami telah mencoba membangun RBS untuk badan hukum di bank dengan menggunakan microservices.







Penulis artikel: ctimas dan Alexey_Salaev



Pentingnya arsitektur layanan mikro



Proyek kami adalah RBS untuk badan hukum. Banyak proses berbeda di bawah tenda dan antarmuka minimalis yang bagus. Tapi tidak selalu demikian. Untuk waktu yang lama kami menggunakan solusi dari kontraktor, tetapi suatu hari yang baik diputuskan untuk mengembangkan produk kami.



Memulai proyek, ada banyak diskusi: pendekatan mana yang harus dipilih? bagaimana cara membangun sistem RBS baru kami? Semuanya dimulai dengan diskusi “monolith vs microservices”: membahas kemungkinan bahasa pemrograman yang digunakan, berdebat tentang framework (“haruskah saya menggunakan spring cloud?”, “Protokol mana yang harus saya pilih untuk komunikasi antar layanan mikro?”). Pertanyaan-pertanyaan ini, pada umumnya, memiliki jumlah jawaban yang terbatas, dan kami hanya memilih pendekatan dan teknologi tertentu tergantung pada kebutuhan dan kemampuan. Dan jawaban atas pertanyaan "Bagaimana cara menulis layanan mikro itu sendiri?" tidak sepenuhnya langsung.



Banyak yang mungkin berkata, “Mengapa mengembangkan konsep arsitektur umum untuk layanan mikro itu sendiri? Ada arsitektur perusahaan dan arsitektur proyek, dan vektor umum pembangunan. Jika Anda menetapkan tugas ke tim, itu akan menyelesaikannya, dan layanan mikro akan ditulis dan itu akan melakukan tugasnya. Bagaimanapun, inilah inti dari layanan mikro - kemandirian. " Dan mereka akan benar! Namun seiring waktu, tim menjadi lebih besar, oleh karena itu, jumlah layanan mikro dan karyawan terus bertambah, dan jumlah orang yang lama lebih sedikit. Pengembang baru datang yang perlu membenamkan diri dalam proyek, beberapa pengembang berganti tim. Selain itu, tim tidak lagi ada seiring waktu, tetapi layanan mikro mereka terus hidup, dan dalam beberapa kasus mereka perlu ditingkatkan.



Saat mengembangkan konsep umum arsitektur layanan mikro, kami meninggalkan cadangan besar untuk masa depan:



  • ;
  • ;
  • : .




Setiap orang yang bekerja dengan layanan mikro sangat menyadari pro dan kontra mereka, salah satunya adalah kemampuan untuk dengan cepat mengganti implementasi lama dengan yang baru. Tetapi seberapa kecilkah layanan mikro yang harus dimiliki agar dapat dengan mudah diganti? Di manakah batas yang menentukan ukuran layanan mikro? Bagaimana tidak membuat monolit mini atau nanservice? Dan Anda selalu dapat langsung ke sisi fungsi yang menjalankan sebagian kecil logika dan membangun proses bisnis dengan



menyusun urutan pemanggilan fungsi tersebut ... Kami memutuskan untuk memilih layanan mikro berdasarkan domain bisnis (misalnya, layanan mikro untuk pembayaran rubel), dan membangun layanan mikro sendiri sesuai dengan tugas domain ini.



Mari pertimbangkan contoh proses bisnis standar untuk bank mana pun - "membuat perintah pembayaran"







Anda dapat melihat bahwa permintaan klien yang tampaknya sederhana adalah rangkaian operasi yang cukup besar. Skenario ini adalah perkiraan, beberapa tahapan dihilangkan untuk kesederhanaan, beberapa tahapan terjadi pada tingkat komponen infrastruktur dan tidak mencapai logika bisnis utama dalam layanan produk, bagian lain dari operasi bekerja secara asynchronous. Intinya adalah kita memiliki proses yang pada satu titik waktu dapat menggunakan banyak layanan yang berdekatan, menggunakan fungsionalitas pustaka yang berbeda, menerapkan beberapa jenis logika di dalam dirinya sendiri dan menyimpan data ke berbagai penyimpanan.



Melihat lebih dekat, Anda dapat melihat bahwa proses bisnis cukup linier dan dalam proses kerjanya itu perlu mendapatkan beberapa data di suatu tempat atau entah bagaimana memproses data yang dimilikinya, dan ini mungkin memerlukan bekerja dengan sumber data eksternal ( layanan mikro, database) atau logika (perpustakaan).







Beberapa layanan mikro tidak sesuai dengan konsep ini, tetapi jumlah layanan mikro tersebut dalam persentase keseluruhan kecil dan berjumlah sekitar 5%.



Arsitektur bersih



Setelah melihat pendekatan yang berbeda untuk mengatur kode, kami memutuskan untuk mencoba pendekatan "arsitektur bersih" dengan mengatur kode di layanan mikro kami sebagai lapisan.



Mengenai "arsitektur bersih" itu sendiri, lebih dari satu buku telah ditulis, ada banyak artikel baik di Internet maupun di Habré ( pasal 1 , pasal 2 ), lebih dari sekali pro dan kontranya telah dibahas.



Diagram populer yang dapat ditemukan tentang topik ini digambar oleh Bob Martin dalam bukunya Clean Architecture:







Di sini, diagram lingkaran di sebelah kiri di tengah menunjukkan arah ketergantungan antar lapisan, dan sederhana di sudut kanan Anda dapat melihat arah aliran eksekusi.



Pendekatan ini, sebagaimana, memang, dalam teknologi pemrograman apa pun, memiliki pro dan kontra. Tetapi bagi kami, ada lebih banyak aspek positif daripada negatif ketika menggunakan pendekatan ini.



Penerapan "arsitektur bersih" dalam proyek



Kami telah menggambar ulang diagram ini berdasarkan skenario kami.







Secara alami, diagram ini mencerminkan satu skenario. Sering terjadi bahwa layanan mikro melakukan lebih banyak operasi pada satu entitas domain, tetapi, dalam keadilan, banyak adaptor dapat digunakan kembali.



Pendekatan yang berbeda dapat digunakan untuk memisahkan layanan mikro menjadi beberapa lapisan, tetapi kami memilih pembagian menjadi modul di tingkat pembuat proyek. Implementasi di tingkat modul memberikan persepsi visual yang lebih mudah tentang proyek, dan juga memberikan lapisan perlindungan lain untuk proyek dari penyalahgunaan gaya arsitektur.



Dari pengalaman, kami memperhatikan bahwa ketika tenggelam dalam sebuah proyek, pengembang baru hanya perlu membiasakan diri dengan bagian teoritis dan dia dapat dengan mudah dan cepat menavigasi hampir semua layanan mikro.



Kami menggunakan Gradle untuk membangun layanan mikro kami di Java, sehingga lapisan utama dibentuk sebagai satu set modulnya:







Sekarang proyek kami terdiri dari modul yang mengimplementasikan kontrak atau menggunakannya. Agar modul ini mulai bekerja dan menyelesaikan masalah, kita perlu mengimplementasikan injeksi ketergantungan dan membuat titik masuk yang akan meluncurkan seluruh aplikasi kita. Dan di sini ada pertanyaan menarik di buku Paman Bob "Arsitektur murni" ada bab-bab penuh yang memberi tahu kami tentang detail, model dan kerangka kerja, tetapi kami tidak membangun arsitekturnya di sekitar kerangka atau di sekitar database, kami menggunakannya sebagai salah satu komponen ...



Saat kami perlu menyimpan entitas, kami merujuk ke database, misalnya, agar skrip kami menerima implementasi kontrak yang dibutuhkan pada saat eksekusi, kami menggunakan kerangka kerja yang memberikan kami arsitektur DI.



Ada tugas ketika kita perlu mengimplementasikan layanan mikro tanpa database, atau kita dapat meninggalkan DI, karena tugas tersebut terlalu sederhana dan lebih cepat untuk menyelesaikannya secara langsung. Dan jika kita akan melaksanakan semua pekerjaan dengan database dalam modul "repositori", lalu di mana kita menggunakan kerangka kerja untuk menyiapkan semua DI untuk kita? Tidak banyak pilihan: kita menambahkan ketergantungan ke setiap modul aplikasi kita, atau kita akan mencoba memilih seluruh DI sebagai modul terpisah.

Kami memilih pendekatan modul baru yang terpisah dan menyebutnya "infrastruktur" atau "aplikasi".



Benar, ketika modul seperti itu diperkenalkan, prinsipnya sedikit dilanggar, yang dengannya kami mengarahkan semua dependensi ke pusat ke lapisan domain, karena itu harus memiliki akses ke semua kelas dalam aplikasi.



Tidak akan berhasil menambahkan lapisan infrastruktur ke bawang kami dalam bentuk lapisan, tidak ada tempat untuk itu di sana, tetapi di sini Anda dapat melihat semuanya dari sudut yang berbeda, dan ternyata kami memiliki lingkaran " Infrastruktur ”dan bawang bombay kami ada di atasnya ... Untuk kejelasan, mari coba pindahkan layer sedikit agar lebih terlihat:







Tambahkan modul baru dan lihat pohon ketergantungan pada lapisan infrastruktur untuk melihat dependensi terakhir antara modul:







Sekarang yang tersisa hanyalah menambahkan Kerangka DI itu sendiri. Kami menggunakan Spring dalam proyek kami, tetapi ini tidak diperlukan, Anda dapat menggunakan framework apa pun yang mengimplementasikan DI (misalnya, mikronaut).



Bagaimana membangun layanan mikro dan di mana bagian mana dari kode itu - kami telah memutuskan, dan ada baiknya melihat skenario bisnis lagi, karena ada hal menarik lainnya.







Diagram menunjukkan bahwa pemeriksaan hak tindakan mungkin tidak dilakukan di skrip utama. Ini adalah tugas terpisah yang tidak bergantung pada apa yang terjadi selanjutnya. Verifikasi tanda tangan dapat dipindahkan ke layanan mikro terpisah, tetapi di sini ada banyak kontradiksi saat menentukan batas layanan mikro, dan kami memutuskan untuk menambahkan lapisan lain ke arsitektur kami.



Pada lapisan terpisah, perlu menyoroti tahapan yang dapat diulang dalam aplikasi kita, misalnya, verifikasi tanda tangan. Prosedur ini dapat terjadi saat membuat, mengubah atau menandatangani dokumen. Banyak skrip utama memulai operasi yang lebih kecil terlebih dahulu dan kemudian hanya skrip utama. Oleh karena itu, lebih mudah bagi kami untuk mengisolasi operasi yang lebih kecil menjadi skrip kecil, dipecah menjadi beberapa lapisan sehingga dapat digunakan kembali dengan lebih nyaman.



Pendekatan ini membuat logika bisnis lebih mudah dipahami, dan seiring waktu, sekumpulan blok bangunan bisnis kecil akan terbentuk dan dapat digunakan kembali.



Tidak banyak yang bisa dikatakan tentang kode adaptor, pengontrol, dan repositori. mereka cukup sederhana. Adaptor untuk layanan mikro lain menggunakan klien yang dihasilkan dari kesombongan, RestTemplate pegas atau klien Grpc. Di repositori - salah satu variasi penggunaan Hibernate atau ORM lainnya. Pengontrol akan mematuhi pustaka yang akan Anda gunakan.



Kesimpulan



Pada artikel ini, kami ingin menunjukkan mengapa kami membangun arsitektur layanan mikro, pendekatan apa yang kami gunakan dan bagaimana kami mengembangkannya. Proyek kami masih muda dan baru di awal perjalanannya, tetapi sekarang kami sudah dapat menyoroti poin-poin utama perkembangannya dari sudut pandang arsitektur layanan mikro itu sendiri.



Kami sedang membangun layanan mikro multi-modul, dengan keunggulannya meliputi:



  • , - , , - , ;
  • , , - ;
  • , Api, , , , ;
  • , , , , .


Bukan tanpa, tentu saja, lalat di salep. Misalnya, hal yang paling jelas adalah bahwa seringkali setiap modul bekerja dengan model kecilnya sendiri. Misalnya, di pengontrol Anda akan memiliki deskripsi model lainnya, dan di repositori akan ada entitas database. Dalam hubungan ini, Anda harus sering memetakan objek satu sama lain, tetapi alat seperti "mapstruct" memungkinkan Anda melakukan ini dengan cepat dan andal.



Juga, kerugiannya termasuk fakta bahwa Anda perlu terus memantau pengembang lain, karena ada godaan untuk melakukan lebih sedikit pekerjaan daripada biayanya. Misalnya, untuk memindahkan kerangka sedikit lebih jauh dari satu modul, tetapi ini menyebabkan erosi tanggung jawab kerangka ini di seluruh arsitektur, yang di masa depan dapat berdampak negatif pada kecepatan perbaikan.



Pendekatan untuk mengimplementasikan layanan mikro ini cocok untuk proyek dengan umur panjang dan proyek dengan perilaku kompleks. Karena implementasi seluruh infrastruktur membutuhkan waktu, tetapi di masa depan itu terbayar dengan stabilitas dan peningkatan yang cepat.



All Articles