CQRS: prinsip "divide and conquer" dalam pelayanan seorang programmer

Arsitektur Puff adalah keselamatan di dunia pengembangan perusahaan. Dengan bantuannya, Anda dapat membongkar besi, memparalelkan proses, dan memulihkan ketertiban dalam kode. Kami mencoba menggunakan pola CQRS ketika mengembangkan proyek perusahaan. Semuanya menjadi lebih logis dan ... lebih rumit. Baru-baru ini, saya berbicara tentang apa yang harus saya hadapi di pertemuan Panda-Meetup C # .Net , dan sekarang saya berbagi dengan Anda.



Pernahkah Anda memperhatikan seperti apa aplikasi perusahaan Anda? Mengapa tidak seperti Apple dan Google? Ya, karena kami kekurangan waktu. Persyaratan sering berubah, istilah untuk perubahan mereka biasanya "kemarin". Dan apa yang paling tidak menyenangkan, bisnis tidak suka kesalahan.



Agar bisa hidup dengan ini, para pengembang mulai membagi aplikasi mereka menjadi beberapa bagian. Semuanya dimulai sederhana - dengan data. Banyak yang akrab dengan skema ketika data terpisah, klien terpisah, sementara logika disimpan di tempat yang sama dengan data.



Garis besar yang bagus. DBMS terbesar memiliki ekstensi prosedural yang berfungsi penuh untuk SQL. Ada pepatah tentang Oracle: "Di mana ada Oracle, ada logika." Sulit untuk berdebat tentang kenyamanan dan kecepatan konfigurasi seperti itu.

Tetapi kami memiliki aplikasi perusahaan, dan ada masalah: logikanya sulit untuk diukur. Dan tidak masuk akal untuk memuat kapasitas DBMS, yang sudah menderita masalah dengan penggalian dan pembaruan data, dan juga tugas-tugas bisnis yang sepele.

Yah, alat pemrograman logika bisnis yang dibangun ke dalam DBMS, jujur, lemah untuk membuat aplikasi normal perusahaan. Mempertahankan logika bisnis dalam T-SQL / PL-SQL adalah hal yang menyakitkan. Bukan tanpa alasan bahwa bahasa OOP tersebar luas di antara aplikasi perusahaan: C #, Java, Anda tidak perlu melangkah jauh untuk contoh.



Tampaknya solusi logis: kami menyoroti logika bisnis. Dia akan hidup di servernya, basisnya sendiri, klien sendiri.

Apa yang bisa diperbaiki dalam arsitektur tiga tingkat ini? Arsitektur terlibat dalam lapisan logika bisnis, saya ingin menghindari ini. Logika bisnis tidak ingin tahu apa pun tentang penyimpanan data. UI juga merupakan dunia terpisah di mana ada entitas yang tidak spesifik untuk logika bisnis.

Meningkatkan layer akan membantu. Solusi ini terlihat hampir sempurna, ia memiliki semacam keindahan batin.



Kami memiliki DAL (Lapisan Akses Data) - data dipisahkan dari logika, biasanya repositori CRUD menggunakan ORM, ditambah prosedur tersimpan untuk kueri kompleks. Opsi ini memungkinkan Anda untuk berkembang cukup cepat, dan memiliki kinerja yang dapat diterima.

Logika bisnis dapat menjadi bagian dari layanan atau menjadi lapisan terpisah. Interaksi antar lapisan dapat dilakukan melalui objek transportasi (DTO).

Permintaan dari UI masuk ke layanan, ia berkomunikasi dengan logika bisnis, naik ke DAL untuk mengakses data. Pendekatan ini disebut N-tier, dan memiliki keunggulan yang jelas.

Setiap lapisan memiliki tujuan dan sasaran yang jelas, yang kita, sebagai programmer, sangat sukai. Setiap lapisan beton hanya terlibat dalam bisnisnya sendiri. Layanan dapat ditingkatkan secara horizontal. Pendekatannya jelas bahkan untuk pengembang pemula, seseorang dengan cepat memahami cara kerja sistem. Sangat mudah untuk melacak semua interaksi saat permintaan berubah dari awal hingga selesai.

Konsistensi lain: semua subsistem proyek bekerja dengan data yang sama, Anda tidak perlu khawatir bahwa kami mencatat data di satu tempat dan pengguna tidak melihatnya di bagian lain.

Kue Lapis 1. N-Tier


Di bawah ini adalah contoh fragmen khas dari aplikasi yang dibangun berdasarkan prinsip-prinsip ini. Kami memiliki persyaratan moneter, di sini saya memeriksa model Anemik. Dan ada repositori klasik, bekerja dengan yang melewati ORM.



Ini adalah layanan khas, mereka juga disebut manajer. Ia bekerja dengan repositori, menerima permintaan, dan memberikan jawaban kepada klien. Dalam layanan ini kami melihat beberapa kebingungan: kami memiliki proses pemrosesan, proses untuk bekerja dengan UI dan proses untuk beberapa unit pengendalian internal, mereka saling berhubungan lemah.

Beginilah bentuk khas dari layanan ini. Misalnya, pendaftaran klaim moneter.



Kami menerima data, melakukan beberapa pemeriksaan bisnis. Kemudian ada pembaruan, dan setelahnya beberapa tindakan pasca, misalnya, mengirim pemberitahuan atau menulis ke log pengguna.

Dalam pendekatan ini, terlepas dari segala keindahannya, ada masalah. Sangat sering dalam aplikasi perusahaan, bebannya asimetris: operasi baca adalah satu atau dua perintah daripada menulis. Sudah ada masalah dengan penskalaan basis data itu sendiri. Tentu saja, ini dilakukan, dan bahkan melalui DBMS pada skala basis data, partisi disebut. Tetapi itu sulit. Jika ini dilakukan dengan kualifikasi yang salah atau dilakukan lebih awal dari yang diperlukan, partisi akan gagal.

Misalnya, di salah satu sistem kami volume data mencapai 25 TB, masalah muncul. Kami sendiri mencoba meningkatkan skala, mengundang orang-orang tangguh dari perusahaan terkenal. Mereka melihat dan berkata: kita perlu 14 jam downtime lengkap dari pangkalan. Kami berpikir dan berkata: kawan, itu tidak akan berhasil, bisnis tidak akan menerimanya.

Selain volume basis data, jumlah metode dalam layanan dan repositori bertambah. Misalnya, dalam layanan untuk klaim moneter ada lebih dari seratus metode. Sulit untuk mempertahankan, ada konflik konstan ketika menggabungkan permintaan, review kode lebih sulit untuk dilakukan. Dan jika kita mempertimbangkan bahwa prosesnya berbeda, kelompok pengembang yang berbeda mengerjakannya, maka tugas melacak semua perubahan yang terkait dengan masalah menjadi sakit kepala nyata.

Puff pastry 2. CQRS


Jadi apa yang harus dilakukan? Ada solusi yang ditemukan di Roma kuno: memecah belah dan memerintah.



Seperti yang mereka katakan, semua yang baru sudah lama terlupakan. Kembali pada tahun 1988, Bertrand Meyer merumuskan prinsip pemrograman CQS imperatif - pemisahan command-query - untuk bekerja dengan objek. Semua metode jelas dibagi menjadi dua jenis. Yang pertama - Permintaan - permintaan yang mengembalikan hasil tanpa mengubah status objek. Artinya, ketika Anda melihat persyaratan moneter klien, tidak ada yang harus menulis ke database bahwa klien telah melihat ini dan itu, seharusnya tidak ada efek samping dalam permintaan.

Yang kedua - Perintah - perintah yang mengubah keadaan suatu objek tanpa mengembalikan data. Artinya, Anda memesan sesuatu untuk diubah, dan sebagai imbalannya jangan menunggu laporan 10 ribu baris.



Di sini, model data baca jelas dipisahkan dari model tulis. Sebagian besar logika bisnis berfungsi pada operasi tulis. Membaca dapat bekerja pada representasi terwujud atau secara umum atas dasar yang berbeda. Mereka dapat dibagi dan disinkronkan melalui acara atau layanan internal. Ada banyak pilihan.

CQRS tidak rumit. Kita harus dengan jelas membedakan tim yang mengubah keadaan sistem, tetapi tidak mengembalikan apa pun. Di sini pendekatannya mungkin lebih seimbang. Tidak terlalu menakutkan jika perintah mengembalikan hasil eksekusi: kesalahan atau, misalnya, pengidentifikasi entitas yang dibuat, maka tidak ada kejahatan dalam hal ini. Penting bahwa tim tidak bekerja dengan permintaan, tidak boleh mencari data dan mengembalikan entitas bisnis.

Permintaan - semuanya sederhana di sana. Itu tidak mengubah kondisi sehingga tidak ada efek samping. Ini berarti bahwa jika kita memanggil permintaan dua kali berturut-turut, dan tidak ada perintah lain, keadaan objek dalam kedua kasus harus tetap identik. Ini memungkinkan Anda untuk memparalelkan kueri. Menariknya, model terpisah untuk kueri tidak diperlukan untuk pekerjaan, karena tidak ada gunanya menggambar logika bisnis dari model domain untuk ini.

Proyek CQRS kami


Inilah yang ingin kami lakukan dalam proyek kami:



Aplikasi kami yang ada telah beroperasi sejak 2006, ia memiliki arsitektur berlapis klasik. Kuno tapi masih berfungsi. Tidak ada yang ingin mengubahnya dan bahkan tidak tahu harus menggantikannya dengan apa. Momen datang ketika perlu untuk mengembangkan sesuatu yang baru, dari awal secara praktis. Pada 2011-2012, Event Sourcing dan CQRS adalah topik yang sangat modis. Kami pikir itu keren, sehingga kami bisa menyimpan keadaan asli objek dan kejadian yang mengarah padanya.

Artinya, kita tidak memperbarui objek. Ada kondisi asli dan di sebelahnya adalah apa yang diterapkan padanya. Dalam hal ini, ada nilai tambah yang besar - kita dapat memulihkan keadaan suatu objek kapan saja dalam sejarah. Padahal, majalah itu tidak lagi dibutuhkan. Saat kami menyimpan acara, kami memahami apa yang sebenarnya terjadi. Artinya, bukan hanya nilai klien di sel "alamat" telah diperbarui, kami akan mencatat peristiwa yang tepat, misalnya, perpindahan klien.

Jelas bahwa skema semacam itu bekerja lambat ketika menerima data, jadi kami memiliki basis data terpisah dengan representasi materi untuk seleksi. Yah, dan sinkronisasi acara: dengan setiap kedatangan acara dengan perubahan status, publikasi terjadi. Secara teori, semuanya tampak baik-baik saja, tapi ... Saya belum pernah bertemu orang yang sepenuhnya menyadari ini pada produksi, pada beban tinggi dengan konsistensi bisnis yang dapat diterima.



Skema dapat dikembangkan lebih lanjut jika penangan dan perintah / permintaan dipisahkan. Di sini, sebagai contoh, kami memiliki tim - klaim moneter terdaftar: ada tanggal, jumlah, pelanggan, dan bidang lainnya.

Kami membatasi prosesor registrasi klaim moneter yang hanya dapat menerima tim kami (di mana TCommand: ICommand). Kami dapat menulis penangan tanpa mengubah yang lama, hanya dengan menambahkan persyaratan yang kompleks. Misalnya, perbarui tanggal terlebih dahulu, kemudian tulis nilainya, dan di sini Anda mengirim pemberitahuan kepada klien - semua ini ditulis dalam penangan yang berbeda per perintah.

Bagaimana kita menyebabkan semua ini? Ada operator yang tahu di mana ia menyimpan semua penangan ini.



Dispatcher diteruskan (misalnya, melalui wadah DI) ke API. Dan ketika perintah itu tiba, itu hanya mengeksekusi. Dia tahu di mana wadah itu, di mana tim berada, dan mengeksekusi mereka. Dengan permintaan - sama.

Apa masalah dengan skema seperti itu: semua interaksi menjadi kurang jelas. Kami membangun hierarki pada jenis yang terdaftar dalam wadah, dan kemudian menanggapi perintah / permintaan kami. Ini membutuhkan desain arsitektur yang sangat jelas. Tindakan apa pun dengan satu metode dengan satu parameter tidak lagi terbatas. Anda menulis perintah, menulis pawang, mendaftar dalam wadah. Jumlah overhead meningkat. Dalam proyek besar, masalah muncul dengan navigasi dasar. Kami memutuskan untuk pergi dengan cara yang lebih klasik.

Untuk komunikasi asinkron, bus layanan Rebus digunakan.



Untuk tugas-tugas sederhana, itu sudah lebih dari cukup.

CQRS membuat Anda mendekati kode sedikit berbeda, berkonsentrasi pada proses, karena semua tindakan dilahirkan sebagai bagian dari proses. Kami mengalokasikan repositori untuk permintaan, secara terpisah membuat pemrosesan perintah terkait, dan secara terpisah memproses permintaan terkait. Untuk membaca, kami tidak menggunakan repositori terpisah, kami hanya bekerja dengan ORM dalam tim.



Di sini, misalnya, adalah metode dari mana segala sesuatu yang berlebihan dibuang. Dalam tim registrasi klaim uang, kami mendaftarkan permintaan dan mempublikasikan acara di bus bahwa klaim uang terdaftar.



Siapa pun yang tertarik dengan ini akan bereaksi padanya. Misalnya, otentikasi dan pencatatan pengguna akan berfungsi di sana.

Berikut ini contoh permintaan. Semuanya menjadi sederhana juga: kita membaca dan memberikan ke repositori.



Saya ingin tinggal secara terpisah di Rebus.Saga. Ini adalah pola yang memungkinkan Anda untuk memecah transaksi bisnis menjadi aksi atom. Ini memungkinkan Anda untuk memblokir tidak sekaligus, tetapi secara bertahap dan pada gilirannya.



Elemen pertama mengambil tindakan dan mengirim pesan, pelanggan kedua meresponsnya, memenuhinya, mengirimkan pesannya, yang sudah ditanggapi oleh bagian ketiga sistem. Jika semuanya berakhir dengan baik, Saga membuat pesannya sendiri dari tipe yang ditentukan, yang akan ditanggapi oleh pelanggan lain.

Mari kita lihat seperti apa kelas untuk memproses klaim uang dalam kasus ini. Semuanya jelas: ada perintah, ada permintaan yang berhubungan dengan proses registrasi, yah, bus dengan log.



Dalam hal ini, ada satu penangan. Ketika suatu peristiwa terjadi dan sebuah tim datang untuk mendaftarkan klaim moneter, ia menanggapinya. Di dalam, semuanya sama seperti sebelumnya, tetapi kekhasannya adalah ada proses pengelompokan.



Karena itu, ini menjadi sedikit lebih mudah, lebih sedikit perubahan pada setiap file.

Kesimpulan


Apa yang perlu Anda ingat ketika bekerja dengan CQRS? Anda memerlukan pendekatan yang lebih baik untuk mendesain, karena menulis ulang prosesnya sedikit lebih rumit. Ada overhead kecil, lebih banyak kelas telah menjadi, tetapi ini tidak kritis. Kode menjadi kurang terhubung, namun, itu bukan karena CQRS, tetapi karena transisi ke bus. Tapi CQRS yang mendorong kami untuk menggunakan interaksi acara ini. Kode menjadi lebih sering ditambahkan daripada diubah. Ada lebih banyak kelas, tetapi mereka sekarang lebih terspesialisasi.

Apakah semua orang perlu meninggalkan semuanya dan secara besar-besaran beralih ke CQRS? Tidak, Anda perlu melihat skenario mana yang paling cocok untuk proyek tertentu. Misalnya, jika subsistem Anda berfungsi dengan direktori, CQRS tidak diperlukan, pendekatan layered klasik memberikan hasil yang lebih sederhana dan nyaman.

Versi lengkap kinerja di Panda Meetup tersedia di bawah ini.


Jika Anda ingin mempelajari lebih dalam topik ini, masuk akal untuk mempelajari sumber daya ini:

Gaya arsitektur CQRS - dari Microsoft

Blog Alexander Bendyu

Contoh Universitas Contoso dengan CQRS, MediatR, AutoMapper, dan lainnya - oleh Jimmy Bogard

CQRS - oleh Martin Fowler

Rebus

Source: https://habr.com/ru/post/id440172/


All Articles