Pola data cloud-native

Tip

Konten ini adalah kutipan dari eBook, Merancang Aplikasi .NET Cloud Native untuk Azure, tersedia di .NET Docs atau sebagai PDF gratis yang dapat diunduh yang dapat dibaca secara offline.

Cloud Native .NET apps for Azure eBook cover thumbnail.

Seperti yang telah kita lihat di seluruh buku ini, pendekatan cloud-native mengubah cara Anda merancang, menyebarkan, dan mengelola aplikasi. Ini juga mengubah cara Anda mengelola dan menyimpan data.

Gambar 5-1 membedakan perbedaan.

Data storage in cloud-native applications

Gambar 5-1. Manajemen data dalam aplikasi cloud-native

Pengembang berpengalaman akan dengan mudah mengenali arsitektur di sisi kiri gambar 5-1. Dalam aplikasi monolitik ini, komponen layanan bisnis berkolokasi bersama dalam tingkat layanan bersama, berbagi data dari database relasional tunggal.

Dalam banyak hal, database tunggal membuat manajemen data tetap sederhana. Mengkueri data di beberapa tabel sangat mudah. Perubahan pada pembaruan data bersama-sama atau semuanya diputar kembali. Transaksi ACID menjamin konsistensi yang kuat dan langsung.

Merancang untuk cloud-native, kami mengambil pendekatan yang berbeda. Di sisi kanan Gambar 5-1, perhatikan bagaimana fungsionalitas bisnis dipisahkan menjadi layanan mikro kecil dan independen. Setiap layanan mikro merangkum kemampuan bisnis tertentu dan datanya sendiri. Database monolitik diurai menjadi model data terdistribusi dengan banyak database yang lebih kecil, masing-masing selaras dengan layanan mikro. Saat mendapatkan inspirasi, kami muncul dengan desain yang memperlihatkan database per layanan mikro.

Database per layanan mikro, mengapa?

Database per layanan mikro ini memberikan banyak manfaat, terutama untuk sistem yang harus berkembang dengan cepat dan mendukung skala besar. Dengan model ini...

  • Data domain dirangkum dalam layanan
  • Skema data dapat berkembang tanpa berdampak langsung pada layanan lain
  • Setiap penyimpanan data dapat menskalakan secara independen
  • Kegagalan penyimpanan data dalam satu layanan tidak akan berdampak langsung pada layanan lain

Memisahkan data juga memungkinkan setiap layanan mikro mengimplementasikan jenis penyimpanan data yang paling dioptimalkan untuk beban kerja, kebutuhan penyimpanan, dan pola baca/tulisnya. Pilihannya termasuk penyimpanan data relasional, dokumen, nilai kunci, dan bahkan berbasis grafik.

Gambar 5-2 menyajikan prinsip persistensi poliglot dalam sistem cloud-native.

Polyglot data persistence

Gambar 5-2. Persistensi data poliglot

Perhatikan pada gambar sebelumnya bagaimana setiap layanan mikro mendukung jenis penyimpanan data yang berbeda.

  • Layanan mikro katalog produk mengonsumsi database relasional untuk mengakomodasi struktur relasional yang kaya dari data yang mendasarinya.
  • Layanan mikro keranjang belanja mengonsumsi cache terdistribusi yang mendukung penyimpanan data kunci-nilai sederhananya.
  • Layanan mikro pemesanan menggunakan database dokumen NoSql untuk operasi tulis bersama dengan penyimpanan kunci/nilai yang sangat didenormalisasi untuk mengakomodasi volume tinggi operasi baca.

Meskipun database relasional tetap relevan untuk layanan mikro dengan data kompleks, database NoSQL telah mendapatkan popularitas yang cukup besar. Database tersebut memberikan skala besar dan ketersediaan tinggi. Sifat tanpa skemanya memungkinkan pengembang menjauh dari arsitektur kelas data yang diketik dan ORM yang membuat perubahan menjadi mahal dan memakan waktu. Kita membahas database NoSQL nanti di bab ini.

Meskipun merangkum data menjadi layanan mikro terpisah dapat meningkatkan ketangkasan, performa, dan skalabilitas, itu juga menghadirkan banyak tantangan. Di bagian berikutnya, kita membahas tantangan ini bersama dengan pola dan praktik untuk membantu mengatasinya.

Kueri lintas layanan

Meskipun layanan mikro bersifat independen dan fokus pada kemampuan fungsional tertentu, seperti inventori, pengiriman, atau pemesanan, layanan mikro sering memerlukan integrasi dengan layanan mikro lainnya. Sering kali integrasi melibatkan satu layanan mikro yang mengkueri yang lain untuk data. Gambar 5-3 menunjukkan skenario ini.

Querying across microservices

Gambar 5-3. Mengkueri di seluruh layanan mikro

Pada gambar sebelumnya, kita melihat layanan mikro keranjang belanja yang menambahkan item ke keranjang belanja pengguna. Meskipun penyimpanan data untuk layanan mikro ini berisi data item basket dan baris, penyimpanan data tersebut tidak mempertahankan data produk atau harga. Sebaliknya, item data tersebut dimiliki oleh katalog dan layanan mikro harga. Aspek ini menghadirkan masalah. Bagaimana cara layanan mikro keranjang belanja menambahkan produk ke keranjang belanja pengguna ketika tidak memiliki data produk atau harga di database-nya?

Salah satu opsi yang dibahas dalam Bab 4 adalah panggilan HTTP langsung dari keranjang belanja ke katalog dan layanan mikro harga. Namun, pada bab 4, kita mengatakan HTTP sinkron memanggil beberapa layanan mikro bersama-sama, mengurangi otonominya, dan mengurangi manfaat arsitekturnya.

Kami juga dapat menerapkan pola balasan permintaan dengan antrean masuk dan keluar terpisah untuk setiap layanan. Namun, pola ini rumit dan memerlukan pipa untuk menghubungkan pesan permintaan dan respons. Meskipun ini memisahkan panggilan layanan mikro backend, layanan panggilan masih harus menunggu panggilan selesai secara sinkron. Kemacetan jaringan, kesalahan sementara, atau layanan mikro overload serta dapat mengakibatkan operasi yang berjalan lama dan bahkan gagal.

Sebaliknya, pola yang diterima secara luas untuk menghapus dependensi lintas layanan adalah Pola Tampilan Terwujud, yang ditunjukkan pada Gambar 5-4.

Materialized view pattern

Gambar 5-4. Pola Tampilan Terwujud

Dengan pola ini, Anda menempatkan tabel data lokal (dikenal sebagai model baca) di layanan ke keranjang belanja. Tabel ini berisi salinan denormalisasi data yang diperlukan dari produk dan layanan mikro harga. Menyalin data langsung ke layanan mikro keranjang belanja menghilangkan kebutuhan akan panggilan lintas layanan yang mahal. Dengan data lokal ke layanan, Anda meningkatkan waktu respons dan keandalan layanan. Selain itu, memiliki salinan data sendiri membuat layanan keranjang belanja lebih tangguh. Jika layanan katalog menjadi tidak tersedia, itu tidak akan berdampak langsung pada layanan keranjang belanja. Keranjang belanja dapat terus beroperasi dengan data dari penyimpanannya sendiri.

Tangkapan dengan pendekatan ini adalah bahwa Anda sekarang memiliki data duplikat dalam sistem Anda. Namun, menduplikasi data secara strategis dalam sistem cloud-native adalah praktik yang dibentuk dan tidak dianggap sebagai anti-pola, atau praktik yang buruk. Ingatlah bahwa satu dan hanya satu layanan yang dapat memiliki himpunan data dan memiliki otoritas atasnya. Anda harus menyinkronkan model baca saat sistem rekaman diperbarui. Sinkronisasi biasanya diimplementasikan melalui olahpesan asinkron dengan pola terbitkan/berlangganan, seperti yang ditunjukkan pada Gambar 5.4.

Transaksi terdistribusi

Meskipun mengkueri data di seluruh layanan mikro sulit, mengimplementasikan transaksi di beberapa layanan mikro bahkan lebih kompleks. Tantangan inheren untuk menjaga konsistensi data di seluruh sumber data independen di layanan mikro yang berbeda tidak dapat disepelekan. Kurangnya transaksi terdistribusi dalam aplikasi cloud-native berarti Anda harus mengelola transaksi terdistribusi secara terprogram. Anda berpindah dari dunia konsistensi langsung ke konsistensi akhir.

Gambar 5-5 menunjukkan masalahnya.

Transaction in saga pattern

Gambar 5-5. Mengimplementasikan transaksi di seluruh layanan mikro

Pada gambar sebelumnya, lima layanan mikro independen berpartisipasi dalam transaksi terdistribusi yang membuat pesanan. Setiap layanan mikro mempertahankan penyimpanan datanya sendiri dan mengimplementasikan transaksi lokal untuk penyimpanannya. Untuk membuat pesanan, transaksi lokal untuk setiap layanan mikro individu harus berhasil, atau semua harus membatalkan dan mengembalikan operasi. Meskipun dukungan transaksional bawaan tersedia di dalam masing-masing layanan mikro, tidak ada dukungan untuk transaksi terdistribusi yang akan mencakup kelima layanan untuk menjaga data tetap konsisten.

Sebagai gantinya, Anda harus membangun transaksi terdistribusi ini secara terprogram.

Pola populer untuk menambahkan dukungan transaksi terdistribusi adalah pola Saga. Ini diimplementasikan dengan mengelompokkan transaksi lokal bersama-sama secara terprogram dan secara berurutan memanggil masing-masing transaksi. Jika salah satu transaksi lokal gagal, Saga membatalkan operasi dan memanggil serangkaian transaksi kompensasi. Transaksi kompensasi membatalkan perubahan yang dilakukan oleh transaksi lokal sebelumnya dan memulihkan konsistensi data. Gambar 5-6 menunjukkan transaksi yang gagal dengan pola Saga.

Roll back in saga pattern

Gambar 5-6. Mengembalikan transaksi

Pada gambar sebelumnya, operasi Perbarui Inventaris telah gagal di layanan mikro Inventaris. Saga memanggil sekumpulan transaksi kompensasi (berwarna merah) untuk menyesuaikan jumlah inventaris, membatalkan pembayaran dan pesanan, serta mengembalikan data untuk setiap layanan mikro kembali ke keadaan yang konsisten.

Pola saga biasanya dikoreografikan sebagai rangkaian peristiwa terkait, atau diatur sebagai seperangkat perintah terkait. Dalam Bab 4, kita membahas pola agregator layanan yang akan menjadi fondasi untuk implementasi saga yang diorkestrasi. Kami juga membahas acara bersama dengan topik Azure Bus Layanan dan Azure Event Grid yang akan menjadi fondasi untuk implementasi saga yang dikoreografi.

Data volume tinggi

Aplikasi cloud-native besar sering mendukung persyaratan data volume tinggi. Dalam skenario ini, teknik penyimpanan data tradisional dapat menyebabkan penyempitan. Untuk sistem kompleks yang disebarkan dalam skala besar, Pemisahan Tanggung Jawab Perintah dan Kueri (CQRS) dan Sumber Peristiwa dapat meningkatkan performa aplikasi.

CQRS

CQRS, adalah pola arsitektur yang dapat membantu memaksimalkan performa, skalabilitas, dan keamanan. Pola memisahkan operasi yang membaca data dari operasi yang menulis data.

Untuk skenario normal, model entitas dan objek repositori data yang sama digunakan untuk kedua operasi baca dan tulis.

Namun, skenario data volume tinggi dapat memperoleh manfaat dari model dan tabel data terpisah untuk baca dan tulis. Untuk meningkatkan performa, operasi baca dapat mengkueri representasi data yang sangat didenormalisasi untuk menghindari gabungan tabel berulang yang mahal dan kunci tabel. Operasi tulis, yang dikenal sebagai perintah, akan diperbarui terhadap representasi data yang sepenuhnya dinormalisasi yang akan menjamin konsistensi. Anda kemudian perlu menerapkan mekanisme untuk menjaga kedua representasi tetap sinkron. Biasanya, setiap kali tabel tulis dimodifikasi, tabel tersebut menerbitkan peristiwa yang mereplikasi modifikasi ke tabel baca.

Gambar 5-7 menunjukkan implementasi pola CQRS.

Command and Query Responsibility Segregation

Gambar 5-7. Implementasi CQRS

Pada gambar sebelumnya, perintah terpisah dan model kueri diimplementasikan. Setiap operasi penulisan data disimpan ke penyimpanan tulis, lalu disebarluaskan ke penyimpanan baca. Perhatikan dengan cermat bagaimana proses penyebaran data beroperasi pada prinsip konsistensi akhir. Model baca akhirnya disinkronkan dengan model tulis, tetapi mungkin ada beberapa kelambatan dalam prosesnya. Kita membahas konsistensi akhir di bagian berikutnya.

Pemisahan ini memungkinkan pembacaan dan penulisan untuk diskalakan secara independen. Operasi baca menggunakan skema yang dioptimalkan untuk kueri, sementara penulisan menggunakan skema yang dioptimalkan untuk pembaruan. Kueri baca bertentangan dengan data yang didenormalisasi, sementara logika bisnis yang kompleks dapat diterapkan ke model tulis. Selain itu, Anda mungkin memberlakukan keamanan yang lebih ketat pada operasi tulis daripada yang mengekspos bacaan.

Menerapkan CQRS dapat meningkatkan performa aplikasi untuk layanan cloud-native. Namun, itu menghasilkan desain yang lebih kompleks. Terapkan prinsip ini dengan hati-hati dan strategis ke bagian aplikasi cloud-native Anda yang akan mendapat manfaat darinya. Untuk informasi lebih lanjut tentang CQRS, lihat buku Microsoft Layanan Mikro .NET: Arsitektur untuk Aplikasi .NET dalam Kontainer.

Sumber peristiwa

Pendekatan lain untuk mengoptimalkan skenario data volume tinggi melibatkan Sumber Peristiwa.

Sistem biasanya menyimpan status entitas data saat ini. Jika pengguna mengubah nomor telepon mereka, misalnya, rekaman pelanggan diperbarui dengan nomor baru. Kita selalu tahu status entitas data saat ini, tetapi setiap pembaruan menimpa status sebelumnya.

Dalam kebanyakan kasus, model ini berfungsi dengan baik. Namun, dalam sistem volume tinggi, overhead dari penguncian transaksi dan operasi pembaruan yang sering dapat memengaruhi performa database, responsivitas, dan membatasi skalabilitas.

Sumber Peristiwa membutuhkan pendekatan yang berbeda untuk menangkap data. Setiap operasi yang memengaruhi data dipertahankan ke penyimpanan peristiwa. Alih-alih memperbarui status rekaman data, kita menambahkan setiap perubahan ke daftar berurutan peristiwa sebelumnya - mirip dengan ledger akuntan. Penyimpanan Peristiwa menjadi sistem rekaman untuk data. Ini digunakan untuk menyebarluaskan berbagai tampilan materialisasi dalam konteks terikat layanan mikro. Gambar 5.8 menunjukkan polanya.

Event Sourcing

Gambar 5-8. Sumber Peristiwa

Pada gambar sebelumnya, perhatikan bagaimana setiap entri (berwarna biru) untuk keranjang belanja pengguna ditambahkan ke penyimpanan peristiwa yang mendasarinya. Dalam tampilan materialisasi yang berdampingan, sistem memproyeksikan status saat ini dengan memutar ulang semua peristiwa yang terkait dengan setiap keranjang belanja. Tampilan ini, atau model baca, kemudian diekspos kembali ke antarmuka pengguna. Peristiwa juga dapat diintegrasikan dengan sistem dan aplikasi eksternal atau dikueri untuk menentukan status entitas saat ini. Dengan pendekatan ini, Anda mempertahankan riwayat. Anda tidak hanya mengetahui status entitas saat ini, tetapi juga bagaimana Anda mencapai status ini.

Secara mekanis, sumber peristiwa menyederhanakan model tulis. Tidak ada pembaruan atau penghapusan. Menambahkan setiap entri data sebagai peristiwa yang tidak dapat diubah meminimalkan konflik ketidakcocokan, penguncian, dan konkurensi yang terkait dengan database relasional. Membangun model baca dengan pola tampilan materialisasi memungkinkan Anda memisahkan tampilan dari model tulis dan memilih penyimpanan data terbaik untuk mengoptimalkan kebutuhan antarmuka pengguna aplikasi Anda.

Untuk pola ini, pertimbangkan penyimpanan data yang secara langsung mendukung sumber peristiwa. Azure Cosmos DB, MongoDB, Cassandra, CouchDB, dan RavenDB adalah kandidat yang baik.

Seperti semua pola dan teknologi, terapkan secara strategis dan bila diperlukan. Meskipun sumber peristiwa dapat memberikan peningkatan performa dan skalabilitas, ini datang dengan mengorbankan kompleksitas dan kurva pembelajaran.