Menyemai basis data layanan mikro

10

Layanan yang diberikan (CMS) yang mengontrol model (Produk, mari kita asumsikan satu-satunya bidang yang dimilikinya adalah id, judul, harga) dan layanan B (Pengiriman) dan C (Email) yang harus menampilkan model yang diberikan apa yang harus pendekatan untuk menyinkronkan informasi model yang diberikan di seluruh layanan tersebut dalam pendekatan sumber acara? Mari kita asumsikan bahwa katalog produk jarang berubah (tetapi tidak berubah) dan bahwa ada admin yang dapat mengakses data pengiriman dan email sangat sering (contoh fungsi adalah: B: display titles of products the order containeddan C:) display content of email about shipping that is going to be sent. Setiap layanan memiliki DB sendiri.

Solusi 1

Kirim semua informasi yang diperlukan tentang Produk dalam acara - ini berarti struktur berikut untuk order_placed:

{
    order_id: [guid],
    product: {
        id: [guid],
        title: 'Foo',
        price: 1000
    }
}

Pada layanan B dan C informasi produk disimpan dalam productatribut JSON di atas ordersmeja

Dengan demikian, untuk menampilkan informasi yang diperlukan hanya data yang diambil dari acara yang digunakan

Masalah : tergantung pada informasi apa yang perlu disajikan dalam B dan C, jumlah data dalam acara dapat bertambah. B dan C mungkin tidak memerlukan informasi yang sama tentang Produk, tetapi acara harus mengandung keduanya (kecuali jika kami memisahkan acara menjadi dua). Jika data yang diberikan tidak ada dalam acara yang diberikan, kode tidak dapat menggunakannya - jika kami akan menambahkan opsi warna ke Produk yang diberikan, untuk pesanan yang ada dalam B dan C, produk yang diberikan tidak akan berwarna kecuali jika kami memperbarui acara dan kemudian memutarnya kembali. .

Solusi 2

Kirim hanya panduan produk dalam acara - ini berarti struktur berikut untuk order_placed:

{
    order_id: [guid],
    product_id: [guid]
}

Pada layanan, informasi produk B dan C disimpan dalam product_idatribut di atas ordersmeja

Informasi produk diambil oleh layanan B dan C bila diperlukan dengan melakukan panggilan API ke A/product/[guid]titik akhir

Masalah : ini membuat B dan C tergantung pada A (setiap saat). Jika skema perubahan Produk pada A, perubahan harus dilakukan pada semua layanan yang bergantung padanya (tiba-tiba)

Solusi 3

Kirim hanya panduan produk dalam acara - ini berarti struktur berikut untuk order_placed:

{
    order_id: [guid],
    product_id: [guid]
}

Pada layanan informasi produk B dan C disimpan dalam productstabel; masih ada di product_idatas ordersmeja, tetapi ada replikasi productsdata antara A, B dan C; B dan C mungkin mengandung informasi berbeda tentang Produk daripada A

Informasi produk diunggulkan ketika layanan B dan C dibuat dan diperbarui setiap kali informasi tentang Produk berubah dengan melakukan panggilan ke A/productendpoint (yang menampilkan informasi yang diperlukan dari semua produk) atau dengan melakukan akses DB langsung ke A dan menyalin informasi produk yang diperlukan yang diperlukan untuk diberikan layanan.

Masalah : ini membuat B dan C tergantung pada A (saat penyemaian). Jika skema perubahan Produk pada A, perubahan harus dilakukan pada semua layanan yang bergantung padanya (saat penyemaian)


Dari pemahaman saya, pendekatan yang benar adalah dengan solusi 1, dan memperbarui riwayat peristiwa per logika tertentu (jika katalog Produk tidak berubah dan kami ingin menambahkan warna untuk ditampilkan, kami dapat dengan aman memperbarui riwayat untuk mendapatkan keadaan saat ini) Produk dan mengisi data yang hilang dalam acara tersebut) atau melayani tidak adanya data yang diberikan (jika katalog Produk telah berubah dan kami ingin menambahkan warna untuk ditampilkan, kami tidak dapat memastikan apakah pada titik waktu di masa lalu Produk yang diberikan memiliki warna atau tidak - kita dapat mengasumsikan bahwa semua Produk dalam katalog sebelumnya berwarna hitam dan diperuntukkan dengan memperbarui acara atau kode)

eithed
sumber
Sehubungan dengan updating event history- Dalam peristiwa sumber sejarah acara adalah sumber kebenaran Anda dan tidak boleh diubah tetapi hanya maju. Jika acara berubah, Anda dapat menggunakan versi acara atau solusi serupa tetapi saat memutar ulang acara hingga titik waktu tertentu, keadaan data harus seperti saat itu.
Tidak
Berkenaan dengan menyimpan data (skema, dll.) Untuk permintaan dan bidang yang ditambahkan / dihapus dll. Kami menggunakan cosmosDB untuk menyimpan data dalam JSON seperti saat itu. Satu-satunya hal yang perlu versi kemudian adalah peristiwa dan / atau perintah. Anda juga perlu memperbarui kontrak titik akhir dan objek nilai yang berisi data menanggapi kueri dari klien (web, seluler, dll ...). Data lama yang tidak memiliki bidang akan memiliki nilai default atau kosong, yang pernah cocok dengan bisnis tetapi riwayat acara tetap bijaksana dan hanya bergerak maju.
Tidak
@Bukan updating event historymaksud saya: pergi melalui semua peristiwa, menyalinnya dari satu aliran (v1) ke aliran lain (v2) untuk mempertahankan skema acara yang konsisten.
eithed
Sebagai tambahan, dalam ranah perdagangan / e-niaga, Anda mungkin ingin menangkap harga sebagaimana dinyatakan mengingat harga sering berubah. Harga yang ditampilkan kepada pengguna mungkin berbeda pada saat pesanan yang sebenarnya diambil. Ada beberapa cara untuk menyelesaikan masalah, tetapi itu adalah salah satu yang harus dipertimbangkan.
CPerson
@CPerson yup - harga bisa menjadi salah satu atribut yang dilewatkan dalam acara itu sendiri. Di sisi lain, URL untuk gambar dapat ada dalam acara (mewakili maksud display image at the point when purchase was made) atau tidak bisa (mewakili maksud display current image as it within catalog)
eithed

Jawaban:

3

Solusi # 3 sangat dekat dengan ide yang tepat.

Cara untuk berpikir tentang ini: B dan C masing-masing menyimpan salinan "lokal" dari data yang mereka butuhkan. Pesan yang diproses di B (dan juga di C) menggunakan informasi yang di-cache secara lokal. Demikian juga, laporan dihasilkan menggunakan informasi yang di-cache secara lokal.

Data direplikasi dari sumber ke cache melalui API yang stabil. B dan C bahkan tidak perlu menggunakan API yang sama - mereka menggunakan protokol pengambilan apa pun yang sesuai dengan kebutuhan mereka. Akibatnya, kami mendefinisikan kontrak - protokol dan skema pesan - yang membatasi penyedia dan konsumen. Kemudian setiap konsumen untuk kontrak itu dapat terhubung ke pemasok mana pun. Perubahan mundur yang tidak kompatibel memerlukan kontrak baru.

Layanan memilih strategi pembatalan cache yang sesuai untuk kebutuhan mereka. Ini mungkin berarti menarik perubahan dari sumber pada jadwal reguler, atau sebagai tanggapan terhadap pemberitahuan bahwa hal-hal mungkin telah berubah, atau bahkan "sesuai permintaan" - bertindak sebagai cache yang dibaca, kembali ke salinan data yang tersimpan saat sumbernya tidak tersedia.

Ini memberi Anda "otonomi", dalam arti bahwa B dan C dapat terus memberikan nilai bisnis ketika A sementara tidak tersedia.

Bacaan yang disarankan: Data di Luar, Data di Dalam , Pat Helland 2005.

VoiceOfUnreason
sumber
Yup, saya sepenuhnya setuju dengan apa yang telah Anda tulis di sini dan solusi 3 adalah solusi goto yang telah saya terapkan, namun, itu bukan pendekatan sumber acara, karena, jika kita akan memutar ulang acara, kami tidak selalu ingin menggunakan status Produk saat ini; kami ingin menggunakan negara seperti di titik acara. Tentu saja, ini mungkin baik-baik saja (tergantung kebutuhan bisnis). Namun, jika kita ingin melacak perubahan pada katalog, yang memerlukan event sourcing juga, dan tergantung berapa banyak data itu, kita mungkin lebih baik kembali ke solusi 1.
eithed
1
Saya pikir Anda sudah mendapatkannya dengan solusi # 3. Jika Anda perlu konsistensi replay dengan katalog, sumber acara juga. Anda hanya perlu memutar ulang saat memasang kembali, yang mungkin saat startup - setelah Anda bangun, Anda hanya perlu melihat acara baru, sehingga jumlah data mungkin bukan masalah nyata. Namun, bahkan kemudian Anda memiliki opsi (jika perlu) menggunakan pos-pos pemeriksaan, yaitu "di sini adalah keadaan pada peristiwa 1.000", jadi Anda mengambil itu dan sekarang Anda hanya perlu memutar ulang peristiwa 1,001-ke-saat ini daripada seluruh riwayat .
Mike B.
2

Ada dua hal yang sulit dalam Ilmu Komputer, dan salah satunya adalah pembatalan cache.

Solusi 2 benar-benar posisi default saya, dan Anda seharusnya hanya mempertimbangkan untuk mengimplementasikan caching jika Anda mengalami salah satu dari skenario berikut:

  1. Panggilan API ke Layanan A menyebabkan masalah kinerja.
  2. Biaya Layanan A turun dan tidak dapat mengambil data penting bagi bisnis.

Masalah kinerja adalah pendorong utama. Ada banyak cara untuk menyelesaikan # 2 yang tidak melibatkan caching, seperti memastikan Layanan A sangat tersedia.

Caching menambah kompleksitas yang signifikan pada sistem, dan dapat membuat kasus tepi yang sulit dipikirkan, dan bug yang sangat sulit untuk ditiru. Anda juga harus mengurangi risiko menyediakan data basi ketika ada data yang lebih baru, yang bisa jauh lebih buruk dari perspektif bisnis daripada (misalnya) menampilkan pesan bahwa "Layanan A sedang down - silakan coba lagi nanti."

Dari artikel yang luar biasa ini oleh Udi Dahan:

Ketergantungan ini merayap perlahan pada Anda, mengikat tali sepatu Anda bersama-sama, secara bertahap memperlambat laju pengembangan, merusak stabilitas basis kode Anda di mana perubahan pada satu bagian sistem merusak bagian lain. Ini adalah kematian yang lambat dengan seribu luka, dan sebagai hasilnya tidak ada yang tahu pasti keputusan besar apa yang kami buat yang menyebabkan semuanya menjadi sangat buruk.

Selain itu, Jika Anda memerlukan kueri data waktu produk secara berkala, ini harus ditangani dengan cara data disimpan dalam basis data Produk (mis. Tanggal mulai / berakhir), harus dengan jelas diekspos dalam API (tanggal efektif perlu menjadi input untuk panggilan API untuk meminta data).

Phil Sandler
sumber
1
@SavvasKleanthous "Jaringan ini dapat diandalkan" adalah salah satu kesalahan komputasi terdistribusi. Tetapi respon terhadap kesalahan itu seharusnya tidak menjadi "cache setiap bit data dari setiap layanan di setiap layanan lainnya" (Saya menyadari itu agak hiperbola). Berharap bahwa layanan mungkin tidak tersedia dan berurusan dengan itu sebagai kondisi kesalahan. Jika Anda memiliki situasi yang jarang di mana Layanan A turun memiliki dampak bisnis yang besar, maka (hati-hati!) Pertimbangkan opsi lain.
Phil Sandler
1
@SavvasKleanthous juga mempertimbangkan (seperti yang saya sebutkan dalam jawaban saya) bahwa mengembalikan data basi dalam banyak kasus bisa jauh lebih buruk daripada melempar kesalahan.
Phil Sandler
1
@ditujukan saya merujuk pada komentar ini: "Jika, bagaimanapun kami ingin melacak perubahan pada katalog, itu membutuhkan event yang mencari sumber juga". Bagaimanapun, Anda memiliki ide yang tepat - layanan Produk harus bertanggung jawab untuk melacak perubahan dari waktu ke waktu, bukan layanan hilir.
Phil Sandler
1
Selain itu, menyimpan data yang Anda amati, meskipun memiliki beberapa kesamaan dengan caching, itu tidak menunjukkan masalah yang sama. Lebih khusus, pembatalan tidak diperlukan; Anda mendapatkan versi baru data saat itu terjadi. Apa yang Anda alami adalah konsistensi yang tertunda. Namun, bahkan menggunakan permintaan web ada jendela ketidakkonsistenan (meskipun kecil).
Savvas Kleanthous
1
@SavvasKleanthous Dalam hal apa pun, poin utama saya adalah tidak mencoba untuk memecahkan masalah yang belum ada, terutama dengan solusi yang membawa masalah dan risiko mereka sendiri. Opsi 2 adalah solusi paling sederhana dan itu harus menjadi pilihan default sampai saat itu tidak memenuhi persyaratan bisnis . Jika Anda berpikir memilih solusi paling sederhana yang dapat bekerja adalah (seperti yang Anda katakan) "sangat buruk", maka saya pikir kami hanya tidak setuju.
Phil Sandler
2

Sangat sulit untuk mengatakan bahwa satu solusi lebih baik daripada solusi lainnya. Memilih satu di antara Solusi # 2 dan # 3 tergantung pada faktor-faktor lain (durasi cache, toleransi konsistensi, ...)

2 sen saya:

Pembatalan cache mungkin sulit tetapi pernyataan masalah menyebutkan bahwa katalog produk jarang berubah. Fakta ini membuat data produk kandidat yang baik untuk caching

Solusi # 1 (NOK)

  • Data digandakan di banyak sistem

Solusi # 2 (OK)

  • Menawarkan konsistensi yang kuat
  • Hanya berfungsi ketika layanan produk sangat tersedia dan menawarkan kinerja yang baik
  • Jika layanan email menyiapkan ringkasan (dengan banyak produk), maka waktu respons keseluruhan bisa lebih lama

Solusi # 3 (Kompleks tetapi disukai)

  • Lebih suka pendekatan API daripada akses DB langsung untuk mengambil informasi produk
  • Layanan yang memakan daya tahan tidak terpengaruh ketika layanan produk turun
  • Mengkonsumsi aplikasi (pengiriman dan layanan email) segera mengambil detail produk setelah suatu acara diterbitkan. Kemungkinan layanan produk turun dalam beberapa milidetik ini sangat jauh.
Sudhir
sumber
1

Secara umum, saya akan sangat menyarankan terhadap opsi 2 karena kopling sementara antara kedua layanan (kecuali komunikasi antara layanan ini sangat stabil, dan tidak terlalu sering). Penggabungan sementara adalah apa yang Anda gambarkan sebagai this makes B and C dependant upon A (at all times), dan berarti bahwa jika A turun atau tidak dapat dijangkau dari B atau C, B dan C tidak dapat memenuhi fungsinya.

Saya pribadi percaya bahwa kedua opsi 1 dan 3 memiliki situasi di mana mereka adalah opsi yang valid.

Jika komunikasi antara A dan B & C sangat tinggi, atau jumlah data yang diperlukan untuk menghadiri acara cukup besar untuk menjadikannya perhatian, maka opsi 3 adalah pilihan terbaik, karena beban pada jaringan jauh lebih rendah , dan latensi operasi akan berkurang ketika ukuran pesan berkurang. Kekhawatiran lain yang perlu dipertimbangkan di sini adalah:

  1. Stabilitas kontrak: jika kontrak meninggalkan pesan A sering berubah, maka menempatkan banyak properti dalam pesan akan menghasilkan banyak perubahan pada konsumen. Namun, dalam hal ini saya percaya ini bukan masalah besar karena:
    1. Anda menyebutkan bahwa sistem A adalah CMS. Ini berarti bahwa Anda bekerja pada domain yang stabil dan karena itu saya tidak percaya Anda akan sering melihat perubahan
    2. Karena B dan C adalah pengiriman dan email, dan Anda menerima data dari A, saya yakin Anda akan mengalami perubahan aditif alih-alih memecahnya, yang aman untuk ditambahkan setiap kali Anda menemukannya tanpa pengerjaan ulang.
  2. Kopling: Ada sangat sedikit atau tidak ada kopling di sini. Pertama karena komunikasi adalah melalui pesan, tidak ada sambungan antara layanan selain layanan temporal pendek selama penyemaian data, dan kontrak operasi itu (yang bukan sambungan yang dapat atau harus Anda coba hindari)

Opsi 1 bukanlah sesuatu yang saya tolak. Ada jumlah yang sama dari kopling, tetapi pengembangan-bijaksana itu harus mudah dilakukan (tidak perlu untuk tindakan khusus), dan stabilitas domain harus berarti bahwa ini tidak akan sering berubah (seperti yang saya sebutkan sebelumnya).

Pilihan lain yang saya sarankan adalah sedikit variasi ke 3, yang bukan untuk menjalankan proses selama start-up, tetapi mengamati acara "ProductAdded dan" ProductDetailsChanged "pada B dan C, ketika ada perubahan dalam katalog produk di A. Ini akan membuat penyebaran Anda lebih cepat (dan jadi lebih mudah untuk memperbaiki masalah / bug jika Anda menemukannya).


Edit 2020-03-03

Saya memiliki urutan prioritas tertentu ketika menentukan pendekatan integrasi:

  1. Berapa biaya konsistensi? Bisakah kita menerima beberapa milidetik ketidakkonsistenan antara hal-hal yang berubah dalam A dan semuanya tercermin dalam B & C?
  2. Apakah Anda memerlukan kueri point-in-time (juga disebut queri temporal)?
  3. Apakah ada sumber kebenaran untuk data? Layanan yang memiliki mereka dan dianggap hulu?
  4. Jika ada pemilik / satu sumber kebenaran apakah itu stabil? Atau apakah kita berharap melihat perubahan yang sering terjadi?

Jika biaya inkonsistensi tinggi, (pada dasarnya data produk dalam A harus konsisten sesegera mungkin dengan produk yang di-cache dalam B dan C), maka Anda tidak dapat menghindari keharusan menerima ketidakberuntungan, dan membuat permintaan sinkron (seperti web / rest request) dari B & C ke A untuk mengambil data. Waspadalah! Ini masih tidak berarti konsisten secara transaksi, tetapi hanya meminimalkan jendela untuk inkonsistensi. Jika Anda benar-benar, secara positif harus segera konsisten, Anda perlu mengubah batas layanan Anda. Namun, saya sangat yakin ini seharusnya tidak menjadi masalah. Dari pengalaman, sebenarnya sangat jarang bahwa perusahaan tidak dapat menerima beberapa detik inkonsistensi, jadi Anda bahkan tidak perlu membuat permintaan sinkron.

Jika Anda memang membutuhkan pertanyaan point-in-time (yang tidak saya perhatikan dalam pertanyaan Anda dan karenanya tidak termasuk di atas, mungkin salah), biaya mempertahankan ini pada layanan hilir sangat tinggi (Anda harus menduplikasi logika proyeksi peristiwa internal di semua layanan hilir) yang membuat keputusan menjadi jelas: Anda harus meninggalkan kepemilikan ke A, dan meminta A ad-hoc atas permintaan web (atau yang serupa), dan A harus menggunakan sumber acara untuk mengambil semua peristiwa yang Anda ketahui tentang pada saat memproyeksikan ke negara, dan mengembalikannya. Saya kira ini mungkin opsi 2 (jika saya mengerti dengan benar?), Tetapi biayanya sedemikian sehingga sementara kopling temporal lebih baik daripada biaya pemeliharaan peristiwa duplciated dan logika proyeksi.

Jika Anda tidak memerlukan titik waktu, dan tidak ada pemilik data yang jelas (yang dalam jawaban awal saya, saya berasumsi ini berdasarkan pertanyaan Anda), maka pola yang sangat masuk akal adalah mengadakan representasi dari produk di setiap layanan secara terpisah. Ketika Anda memperbarui data untuk produk, Anda memperbarui A, B dan C secara paralel dengan membuat permintaan web paralel untuk masing-masing, atau Anda memiliki API perintah yang mengirim beberapa perintah ke masing-masing A, B dan C. B & C menggunakan mereka versi lokal dari data untuk melakukan pekerjaan mereka, yang mungkin basi atau tidak. Ini bukan salah satu dari opsi di atas (walaupun bisa dibuat dekat dengan opsi 3), karena data dalam A, B dan C mungkin berbeda, dan "keseluruhan" produk mungkin merupakan komposisi dari ketiga data sumber.

Mengetahui apakah sumber kebenaran memiliki kontrak stabil berguna karena Anda dapat menggunakannya untuk menggunakan domain / acara internal (atau acara yang Anda simpan di sumber acara Anda sebagai pola penyimpanan di A) untuk integrasi di seluruh A dan layanan B dan C. Jika kontrak stabil Anda dapat mengintegrasikan melalui peristiwa domain. Namun, maka Anda memiliki kekhawatiran tambahan dalam kasus di mana perubahan sering terjadi, atau bahwa kontrak pesan cukup besar yang membuat transportasi menjadi masalah.

Jika Anda memiliki pemilik yang jelas, dengan kontras yang diharapkan stabil, opsi terbaik adalah opsi 1; pesanan akan berisi semua informasi yang diperlukan dan kemudian B dan C akan melakukan fungsinya menggunakan data dalam acara tersebut.

Jika kontrak cenderung berubah, atau sering rusak, mengikuti opsi 3 Anda, itu kembali ke permintaan web untuk mengambil data produk sebenarnya merupakan pilihan yang lebih baik, karena itu jauh lebih mudah untuk mempertahankan beberapa versi. Jadi B akan membuat permintaan pada v3 produk.

Savvas Kleanthous
sumber
Ya, saya setuju. Sementara ProductAddedatau ProductDetailsChangedmenambah kerumitan pelacakan perubahan katalog produk kami perlu menjaga agar data disinkronkan antara database dalam beberapa cara, dalam kasus peristiwa diputar ulang dan kami perlu mengakses data katalog dari masa lalu.
eithed
@ Dengan demikian saya memperbarui jawaban untuk memperluas pada beberapa asumsi yang saya buat.
Savvas Kleanthous