Pola Sumber Kejadian
Alih-alih menyimpan hanya status data saat ini dalam database relasional, simpan serangkaian tindakan lengkap yang diambil pada objek di penyimpanan khusus tambahan. Penyimpanan bertindak sebagai sistem rekaman dan dapat digunakan untuk mewujudkan objek domain. Pendekatan ini dapat meningkatkan performa, skalabilitas, dan auditabilitas dalam sistem yang kompleks.
Penting
Sumber peristiwa adalah pola kompleks yang meresap melalui seluruh arsitektur dan memperkenalkan trade-off untuk mencapai peningkatan performa, skalabilitas, dan auditabilitas. Setelah sistem Anda menjadi sistem sumber peristiwa, semua keputusan desain di masa depan dibatasi oleh fakta bahwa ini adalah sistem sumber peristiwa. Ada biaya tinggi untuk bermigrasi ke atau dari sistem sumber peristiwa. Pola ini paling cocok untuk sistem di mana performa dan skalabilitas adalah persyaratan utama. Kompleksitas yang ditambahkan sumber peristiwa ke sistem tidak dibenarkan untuk sebagian besar sistem.
Konteks dan masalah
Sebagian besar aplikasi bekerja dengan data, dan pendekatan umumnya adalah agar aplikasi menyimpan status data terbaru dalam database relasional, menyisipkan atau memperbarui data sesuai kebutuhan. Misalnya, dalam model buat, baca, perbarui, dan hapus (CRUD) tradisional, proses data umumnya adalah membaca data dari penyimpanan, membuat beberapa modifikasi, dan memperbarui status data saat ini dengan nilai baru—sering kali dengan menggunakan transaksi yang mengunci data.
Pendekatan CRUD mudah dan cepat untuk sebagian besar skenario. Namun, dalam sistem beban tinggi, pendekatan ini memiliki beberapa tantangan:
Performa: Saat sistem diskalakan, performa akan menurun karena ketidakcocokan untuk sumber daya dan masalah penguncian.
Skalabilitas: Sistem CRUD sinkron dan blok operasi data pada pembaruan. Hal ini dapat menyebabkan hambatan dan latensi yang lebih tinggi ketika sistem sedang dimuat.
Auditabilitas: Sistem CRUD hanya menyimpan status data terbaru. Kecuali ada mekanisme audit yang mencatat detail setiap operasi dalam log terpisah, riwayat akan hilang.
Solusi
Pola Event Sourcing mendefinisikan pendekatan untuk menangani operasi pada data yang didorong oleh urutan peristiwa, yang masing-masing dicatat di penyimpanan khusus tambahan. Kode aplikasi memunculkan peristiwa yang secara imperatif menjelaskan tindakan yang diambil pada objek. Peristiwa umumnya dikirim ke antrean di mana proses terpisah, penanganan aktivitas, mendengarkan antrean dan mempertahankan peristiwa di penyimpanan peristiwa. Setiap peristiwa mewakili perubahan logis pada objek, seperti AddedItemToOrder
atau OrderCanceled
.
Peristiwa tersebut disimpan di penyimpanan peristiwa yang bertindak sebagai sistem catatan (sumber data otoritatif) tentang status data saat ini. Penanganan aktivitas tambahan dapat mendengarkan peristiwa yang mereka minati dan mengambil tindakan yang sesuai. Konsumen dapat, misalnya, memulai tugas yang menerapkan operasi dalam peristiwa ke sistem lain, atau melakukan tindakan terkait lainnya yang diperlukan untuk menyelesaikan operasi. Perhatikan bahwa kode aplikasi yang menghasilkan peristiwa dipisahkan dari sistem yang berlangganan ke peristiwa.
Kapan saja, aplikasi dapat membaca riwayat peristiwa. Anda kemudian dapat menggunakan peristiwa untuk mewujudkan status entitas saat ini dengan memutar kembali dan mengkonsumsi semua peristiwa yang terkait dengan entitas tersebut. Proses ini dapat terjadi sesuai permintaan untuk mewujudkan objek domain saat menangani permintaan.
Karena relatif mahal untuk membaca dan memutar ulang peristiwa, aplikasi biasanya menerapkan tampilan materialisasi, proyeksi baca-saja dari penyimpanan peristiwa yang dioptimalkan untuk kueri. Misalnya, sistem dapat mempertahankan tampilan materialisasi dari semua pesanan pelanggan yang digunakan untuk mengisi UI. Saat aplikasi menambahkan pesanan baru, menambahkan atau menghapus item pada pesanan, atau menambahkan informasi pengiriman, peristiwa dinaikkan dan handler memperbarui tampilan materialisasi.
Gambar menunjukkan gambaran umum pola, termasuk beberapa implementasi umum dengan pola, termasuk penggunaan antrean, penyimpanan baca-saja, mengintegrasikan peristiwa dengan aplikasi dan sistem eksternal, dan memutar ulang peristiwa untuk membuat proyeksi status entitas tertentu saat ini.
Alur kerja
Berikut ini menjelaskan alur kerja umum untuk pola ini:
- Lapisan presentasi memanggil objek yang bertanggung jawab untuk membaca dari penyimpanan baca-saja. Data yang dikembalikan digunakan untuk mengisi UI.
- Lapisan presentasi memanggil handler perintah untuk melakukan tindakan seperti membuat ke cart, atau menambahkan item ke kelir.
- Handler perintah memanggil penyimpanan peristiwa untuk mendapatkan peristiwa historis untuk entitas. Misalnya, ini dapat mengambil semua peristiwa kelir. Peristiwa tersebut diputar kembali di objek untuk mewujudkan status entitas saat ini, sebelum tindakan apa pun terjadi.
- Logika bisnis dijalankan dan peristiwa dimunculkan. Dalam sebagian besar implementasi, peristiwa didorong ke antrean atau topik untuk memisahkan produsen peristiwa dan konsumen peristiwa.
- Penanganan aktivitas mendengarkan peristiwa yang mereka minati dan melakukan tindakan yang sesuai untuk handler tersebut. Beberapa tindakan penanganan aktivitas umum adalah:
- Menulis peristiwa ke penyimpanan peristiwa
- Memperbarui penyimpanan baca-saja yang dioptimalkan untuk kueri
- Mengintegrasikan dengan sistem eksternal
Keuntungan pola
Pola Event Sourcing memberikan keuntungan berikut:
Peristiwa tidak berubah dan dapat disimpan menggunakan operasi khusus tambahan. Antarmuka pengguna, alur kerja, atau proses yang memulai suatu peristiwa dapat berlanjut, dan tugas yang menangani peristiwa dapat berjalan di latar belakang. Proses ini, dikombinasikan dengan fakta bahwa tidak ada pertikaian selama pemrosesan transaksi, dapat sangat meningkatkan performa dan skalabilitas untuk aplikasi, terutama untuk lapisan presentasi.
Peristiwa adalah objek sederhana yang menjelaskan beberapa tindakan yang terjadi, bersama dengan data terkait yang diperlukan untuk menjelaskan tindakan yang diwakili oleh peristiwa. Peristiwa tidak secara langsung memperbarui penyimpanan data. Peristiwa hanya direkam untuk penanganan pada waktu yang tepat. Menggunakan peristiwa dapat menyederhanakan implementasi dan manajemen.
Peristiwa biasanya memiliki arti bagi seorang ahli domain, sedangkan ketidakcocokan impedansi objek-relasional dapat membuat tabel database yang kompleks menjadi sulit dipahami. Tabel adalah konstruksi buatan yang mewakili status sistem saat ini, bukan peristiwa yang terjadi.
Sumber peristiwa dapat membantu mencegah pembaruan bersamaan menyebabkan konflik karena menghindari persyaratan untuk memperbarui objek secara langsung di penyimpanan data. Namun, model domain masih harus dirancang untuk melindungi diri dari permintaan yang mungkin mengakibatkan status tidak konsisten.
Penyimpanan peristiwa khusus tambahan menyediakan jejak audit yang dapat digunakan untuk memantau tindakan yang diambil terhadap penyimpanan data. Ini dapat meregenerasi status saat ini sebagai tampilan atau proyeksi materialisasi dengan memutar ulang peristiwa kapan saja, dan dapat membantu dalam pengujian dan penelusuran kesalahan sistem. Selain itu, persyaratan untuk menggunakan kompensasi peristiwa untuk membatalkan perubahan dapat memberikan riwayat perubahan yang dibalik. Kemampuan ini tidak akan terjadi jika model menyimpan status saat ini. Daftar peristiwa juga dapat digunakan untuk menganalisis performa aplikasi dan untuk mendeteksi tren perilaku pengguna. Atau, ini dapat digunakan untuk mendapatkan informasi bisnis berguna lainnya.
Penangan perintah menaikkan peristiwa, dan tugas melakukan operasi sebagai respons terhadap peristiwa tersebut. Pemisahan tugas dari peristiwa ini memberikan fleksibilitas dan ekstensibilitas. Tugas tahu tentang jenis peristiwa dan data peristiwa, tetapi bukan tentang operasi yang memicu peristiwa. Selain itu, beberapa tugas dapat menangani setiap peristiwa. Hal ini memungkinkan integrasi yang mudah dengan layanan dan sistem lain yang hanya mendengarkan peristiwa baru yang diangkat oleh penyimpanan acara. Namun, peristiwa sumber peristiwa cenderung tingkat yang sangat rendah, dan mungkin perlu untuk menghasilkan peristiwa integrasi tertentu sebagai gantinya.
Sumber peristiwa biasanya dikombinasikan dengan pola CQRS dengan melakukan tugas manajemen data sebagai respons terhadap peristiwa, dan dengan mewujudkan tampilan dari peristiwa yang disimpan.
Masalah dan pertimbangan
Pertimbangkan poin-poin berikut saat memutuskan cara menerapkan pola ini:
Konsistensi akhir - Sistem hanya akan pada akhirnya konsisten saat membuat tampilan materialisasi atau menghasilkan proyeksi data dengan memutar ulang peristiwa. Ada beberapa keterlambatan antara aplikasi yang menambahkan peristiwa ke penyimpanan peristiwa sebagai akibat dari menangani permintaan, peristiwa yang diterbitkan, dan konsumen peristiwa yang menanganinya. Selama periode ini, peristiwa baru yang menggambarkan perubahan lebih lanjut pada entitas mungkin telah tiba di penyimpanan peristiwa. Pelanggan Anda harus baik-baik saja dengan fakta bahwa data pada akhirnya konsisten dan sistem harus dirancang untuk memperhitungkan konsistensi akhir dalam skenario ini.
Catatan
Lihat Primer Konsistensi Data untuk informasi tentang konsistensi akhirnya.
Peristiwa penerapan versi - Penyimpanan peristiwa adalah sumber informasi permanen, sehingga data peristiwa tidak boleh diperbarui. Satu-satunya cara untuk memperbarui entitas atau membatalkan perubahan adalah dengan menambahkan peristiwa kompensasi ke penyimpanan peristiwa. Jika skema (daripada data) peristiwa yang dipertahankan perlu berubah, mungkin selama migrasi, mungkin sulit untuk menggabungkan peristiwa yang ada di penyimpanan dengan versi baru. Aplikasi Anda harus mendukung perubahan pada struktur peristiwa. Ini dapat dilakukan dengan beberapa cara.
- Pastikan penanganan aktivitas Anda mendukung semua versi peristiwa. Ini bisa menjadi tantangan untuk dipertahankan dan diuji. Ini mengharuskan penerapan stempel versi pada setiap versi skema peristiwa untuk mempertahankan format peristiwa lama dan baru.
- Terapkan penanganan aktivitas untuk menangani versi peristiwa tertentu. Ini bisa menjadi tantangan pemeliharaan dalam perubahan perbaikan bug tersebut mungkin harus dilakukan di beberapa handler. Ini mengharuskan penerapan stempel versi pada setiap versi skema peristiwa untuk mempertahankan format peristiwa lama dan baru.
- Perbarui peristiwa historis ke skema baru saat skema baru diterapkan. Ini memutus kekekalan peristiwa.
Pemesanan peristiwa - Aplikasi multi-utas dan beberapa instans aplikasi mungkin menyimpan peristiwa di penyimpanan peristiwa. Konsistensi peristiwa di penyimpanan peristiwa sangat penting, seperti urutan peristiwa yang mempengaruhi entitas tertentu (urutan perubahan terjadi pada entitas memengaruhi status saat ini). Menambahkan stempel waktu ke setiap acara dapat membantu menghindari masalah. Praktik umum lainnya adalah membuat anotasi setiap peristiwa yang dihasilkan dari permintaan dengan pengidentifikasi bertambah bertahap. Jika dua tindakan mencoba menambahkan peristiwa untuk entitas yang sama secara bersamaan, penyimpanan peristiwa dapat menolak peristiwa yang cocok dengan pengidentifikasi entitas dan pengidentifikasi peristiwa yang ada.
Mengkueri peristiwa - Tidak ada pendekatan standar, atau mekanisme yang ada seperti kueri SQL, untuk membaca peristiwa untuk mendapatkan informasi. Satu-satunya data yang dapat diekstraksi adalah stream peristiwa menggunakan pengidentifikasi peristiwa sebagai kriteria. ID peristiwa biasanya memetakan ke entitas individual. Status entitas saat ini hanya dapat ditentukan dengan memutar ulang semua peristiwa yang berhubungan terhadap status asli entitas tersebut.
Biaya membuat ulang status untuk entitas - Panjang setiap aliran peristiwa memengaruhi pengelolaan dan pembaruan sistem. Jika stream besar, pertimbangkan untuk membuat snapshot pada interval tertentu seperti jumlah peristiwa tertentu. Status entitas saat ini dapat diperoleh dari snapshot dan dengan memutar ulang peristiwa apa pun yang terjadi setelah titik waktu tersebut. Untuk informasi selengkapnya tentang membuat snapshot data, lihat Replikasi Snapshot Subordinat Utama.
Konflik - Meskipun sumber peristiwa meminimalkan kemungkinan pembaruan yang bertentangan pada data, aplikasi harus tetap dapat menangani inkonsistensi yang dihasilkan dari konsistensi akhir dan kurangnya transaksi. Misalnya, peristiwa yang menunjukkan pengurangan inventaris stok mungkin tiba di penyimpanan data saat pesanan untuk item tersebut sedang ditempatkan. Situasi ini menghasilkan persyaratan untuk mendamaikan dua operasi, baik dengan memberi tahu pelanggan atau dengan membuat pesanan balik.
Kebutuhan akan idempotensi - Publikasi peristiwa mungkin setidaknya sekali, sehingga konsumen peristiwa harus idempotensi. Mereka tidak boleh mengajukan permohonan kembali pembaruan yang dijelaskan dalam suatu peristiwa jika peristiwa ditangani lebih dari sekali. Beberapa instans konsumen dapat memelihara dan menggabungkan properti entitas, seperti jumlah total pesanan yang dilakukan. Hanya satu yang harus berhasil dalam meningkatkan agregat, ketika peristiwa yang ditempatkan pesanan terjadi. Meskipun hasil ini bukan karakteristik utama sumber peristiwa, ini adalah keputusan implementasi yang biasa.
Logika melingkar - Perhatikan skenario di mana pemrosesan satu peristiwa melibatkan pembuatan satu atau beberapa peristiwa baru karena ini dapat menyebabkan perulangan tak terbatas.
Kapan menggunakan pola ini
Gunakan pola ini dalam skenario berikut:
Ketika Anda ingin menangkap niat, tujuan, atau alasan dalam data. Misalnya, perubahan pada entitas pelanggan dapat ditangkap sebagai serangkaian jenis peristiwa tertentu, seperti Beranda yang dipindahkan, Akun yang ditutup, atau Penonaktifan.
Ketika sangat penting untuk meminimalkan atau sepenuhnya menghindari terjadinya pembaruan data yang bertentangan.
Saat Anda ingin merekam peristiwa yang terjadi, untuk memutar ulang peristiwa tersebut untuk memulihkan status sistem, mengembalikan perubahan, atau menyimpan riwayat dan log audit. Misalnya, ketika tugas melibatkan beberapa langkah, Anda mungkin perlu menjalankan tindakan untuk mengembalikan pembaruan lalu memutar ulang beberapa langkah untuk membawa data kembali ke status yang konsisten.
Saat Anda menggunakan peristiwa. Ini adalah fitur alami dari pengoperasian aplikasi, dan membutuhkan sedikit upaya pengembangan atau implementasi ekstra.
Saat Anda perlu memisahkan proses input, atau memperbarui data dari tugas yang diperlukan untuk menerapkan tindakan ini. Perubahan ini mungkin untuk meningkatkan performa UI, atau untuk mendistribusikan peristiwa ke pendengar lain yang mengambil tindakan saat peristiwa terjadi. Misalnya, Anda dapat mengintegrasikan sistem penggajian dengan situs web pengiriman pengeluaran. Peristiwa yang dimunculkan oleh penyimpanan peristiwa sebagai respons terhadap pembaruan data yang dibuat di situs web akan digunakan oleh situs web dan sistem penggajian.
Saat Anda ingin fleksibilitas dapat mengubah format model terwujud dan data entitas jika persyaratan berubah, atau—saat digunakan dengan CQRS—Anda perlu menyesuaikan model baca atau tampilan yang mengekspos data.
Ketika digunakan dengan CQRS, dan konsistensi akhirnya dapat diterima saat model baca diperbarui, atau dampak performa entitas dan data rehidrasi dari aliran peristiwa dapat diterima.
Pola ini mungkin tidak berguna dalam situasi berikut:
Aplikasi yang tidak memerlukan skala atau performa hiper.
Domain kecil atau sederhana, sistem yang memiliki sedikit atau tidak ada logika bisnis, atau sistem nondomain yang secara alami berfungsi dengan baik dengan mekanisme manajemen data CRUD tradisional.
Sistem tempat konsistensi dan pembaruan real-time untuk tampilan data diperlukan.
Sistem di mana hanya ada kemunculan rendah pembaruan yang bertentangan pada data yang mendasar. Misalnya, sistem yang sebagian besar menambahkan data, bukan memperbaruinya.
Desain beban kerja
Arsitek harus mengevaluasi bagaimana pola Sumber Peristiwa dapat digunakan dalam desain beban kerja mereka untuk mengatasi tujuan dan prinsip yang tercakup dalam pilar Azure Well-Architected Framework. Contohnya:
Pilar | Bagaimana pola ini mendukung tujuan pilar |
---|---|
Keputusan desain keandalan membantu beban kerja Anda menjadi tahan terhadap kerusakan dan untuk memastikan bahwa keputusan tersebut pulih ke status berfungsi penuh setelah kegagalan terjadi. | Karena menangkap riwayat perubahan dalam proses bisnis yang kompleks, itu dapat memfasilitasi rekonstruksi status jika Anda perlu memulihkan penyimpanan status. - Pemartisian data RE:06 - RE:09 Pemulihan bencana |
Efisiensi Performa membantu beban kerja Anda memenuhi tuntutan secara efisien melalui pengoptimalan dalam penskalaan, data, kode. | Pola ini, biasanya dikombinasikan dengan CQRS, desain domain yang sesuai, dan rekam jepret strategis, dapat meningkatkan performa beban kerja karena operasi atom tambahan saja dan penghindaman penguncian database untuk penulisan dan bacaan. - Performa DATA PE:08 |
Seperti halnya keputusan desain apa pun, pertimbangkan tradeoff terhadap tujuan pilar lain yang mungkin diperkenalkan dengan pola ini.
Contoh
Sistem manajemen konferensi perlu melacak jumlah pemesanan yang telah selesai untuk konferensi. Dengan cara ini dapat memeriksa apakah ada kursi yang masih tersedia, ketika calon peserta mencoba membuat pemesanan. Sistem ini dapat menyimpan jumlah total pemesanan untuk konferensi setidaknya dalam dua cara:
Sistem dapat menyimpan informasi tentang jumlah total pemesanan sebagai entitas terpisah dalam database yang menyimpan informasi pemesanan. Karena pemesanan dilakukan atau dibatalkan, sistem dapat menambah atau mengurangi jumlah ini sebagaimana mestinya. Pendekatan ini sederhana secara teori, tetapi dapat menyebabkan masalah skalabilitas jika sejumlah besar peserta mencoba untuk memesan seat selama periode waktu yang singkat. Misalnya, pada hari terakhir atau lebih sebelum periode pemesanan ditutup.
Sistem dapat menyimpan informasi tentang pemesanan dan pembatalan sebagai peristiwa yang diadakan di penyimpanan peristiwa. Kemudian, sistem bisa menghitung jumlah seat yang tersedia dengan memutar ulang peristiwa ini. Pendekatan ini bisa lebih skalabel karena kekebalan peristiwa. Sistem hanya perlu dapat membaca data dari penyimpanan peristiwa, atau menambahkan data ke penyimpanan peristiwa. Informasi peristiwa tentang pemesanan dan pembatalan tidak pernah dimodifikasi.
Diagram berikut menggambarkan bagaimana subsistem reservasi seat dari sistem manajemen konferensi dapat diimplementasikan menggunakan sumber peristiwa.
Urutan tindakan untuk memesan dua seat adalah sebagai berikut:
Antarmuka pengguna mengeluarkan perintah untuk memesan seat untuk dua peserta. Perintah ini ditangani oleh penangan perintah terpisah. Bagian logika yang dipisahkan dari antarmuka pengguna dan bertanggung jawab untuk menangani permintaan yang diposting sebagai perintah.
Entitas yang berisi informasi tentang semua reservasi untuk konferensi dibangun dengan mengkueri peristiwa yang menjelaskan pemesanan dan pembatalan. Entitas ini disebut
SeatAvailability
, dan terkandung dalam model domain yang mengekspos metode untuk mengkueri dan memodifikasi data di entitas.Beberapa pengoptimalan yang perlu dipertimbangkan adalah menggunakan rekam jepret (sehingga Anda tidak perlu mengkueri dan memutar ulang daftar lengkap peristiwa untuk mendapatkan status entitas saat ini), dan mempertahankan salinan entitas yang di-cache dalam memori.
Penangan perintah memanggil metode yang diekspos oleh model domain untuk membuat reservasi.
Entitas
SeatAvailability
menaikkan peristiwa yang berisi jumlah seat yang dipesan. Lain kali entitas menerapkan peristiwa, semua reservasi akan digunakan untuk menghitung berapa banyak seat yang tersisa.Sistem menambahkan peristiwa baru ke daftar peristiwa di penyimpanan peristiwa.
Jika pengguna membatalkan seat, sistem mengikuti proses yang sama kecuali penangan perintah mengeluarkan perintah yang menghasilkan peristiwa pembatalan seat dan menambahkannya ke penyimpanan peristiwa.
Selain menyediakan lebih banyak cakupan untuk skalabilitas, menggunakan penyimpanan acara juga memberikan riwayat lengkap, atau jejak audit, dari pemesanan dan pembatalan untuk konferensi. Peristiwa di penyimpanan peristiwa adalah rekaman yang akurat. Tidak perlu mempertahankan agregat dengan cara lain karena sistem dapat dengan mudah memutar ulang peristiwa dan memulihkan status ke titik waktu mana pun.
Langkah berikutnya
Primer Konsistensi Data. Saat Anda menggunakan sumber peristiwa dengan penyimpanan baca terpisah atau tampilan materialisasi, data baca tidak akan segera konsisten. Sebaliknya, data hanya akan pada akhirnya konsisten. Artikel ini merangkum masalah seputar menjaga konsistensi atas data terdistribusi.
Panduan Pemartisian Data. Data sering dipartisi saat Anda menggunakan sumber peristiwa untuk meningkatkan skalabilitas, mengurangi ketidakcocokan, dan mengoptimalkan performa. Artikel ini menjelaskan cara membagi data menjadi partisi diskrit, dan masalah yang dapat muncul.
Blog Martin Fowler:
Sumber daya terkait
Pola dan panduan berikut mungkin relevan saat menerapkan pola ini:
Pola Command and Query Responsibility Segregation (CQRS). Penyimpanan tulis yang menyediakan sumber informasi permanen untuk implementasi CQRS sering didasarkan pada implementasi pola Event Sourcing. Menjelaskan cara memisahkan operasi yang membaca data dalam aplikasi dari operasi yang memperbarui data dengan menggunakan antarmuka terpisah.
Pola Tampilan Terwujud. Penyimpanan data yang digunakan dalam sistem yang didasarkan pada sumber peristiwa biasanya tidak cocok untuk kueri yang efisien. Sebaliknya, pendekatan umumnya adalah menghasilkan tampilan data yang telah diisi sebelumnya secara berkala, atau ketika data berubah.
Pola Transaksi Kompensasi. Data yang ada di penyimpanan sumber peristiwa tidak diperbarui. Sebaliknya, entri baru ditambahkan bahwa transisi status entitas ke nilai baru. Untuk membalikkan perubahan, mengkompensasi entri digunakan karena tidak dimungkinkan untuk membalikkan perubahan sebelumnya. Menjelaskan cara membatalkan pekerjaan yang dilakukan oleh operasi sebelumnya.