Event-Driven Architecture

Eko Kurniawan Khannedy
Blibli.com Tech Blog
10 min readJul 24, 2018

Pada artikel Arsitektur Backend di Blibli.com, saya membahas tentang masa transisi yang Blibli.com lakukan dari sistem Monolith, API-Driven sampai ke Event-Driven. Pada artikel ini saya akan jelaskan lebih detail tentang apa itu arsitektur Event-Driven, dan sekilas tentang apa yang Blibli.com lakukan untuk mengimplementasikan arsitektur ini.

Arsitektur Event-Driven sebenarnya adalah solusi lama yang kembali populer. Sejak dahulu, arsitektur Event-Driven sudah ada, hanya saja dengan istilah yang berbeda, namun sekarang arsitektur Event-Driven kembali populer dengan meledaknya penggunakan arsitektur Microservices dimana-mana. Sebelum kita bahas tentang arsitektur Event-Driven, ada baiknya kita bahas sekilas tentang permasalahan yang terjadi sehingga membuat arsitektur Event-Driven menjadi populer.

Event-Driven Architecture

1. Dimulai dari Monolith

Hampir semua perusahaan akan memulai membangun sistem-nya dengan arsitektur Monolith terlebih dahulu, selain karena lebih mudah, arsitektur Monolith juga lebih cepat dibuat jika untuk pertama kali.

Anggap saja kita memiliki sebuah sistem ECommerce, pada sistem Monolith, semua aksi membuat pesanan, membuat pembayaran, mengirim email, manajemen pelanggan, manajemen barang, manajemen kategori, dan semuanya dibuat dalam satu sistem Monolith.

Arsitektur Monolith

Dengan demikian jika terdapat aksi pembuatan pesanan baru seperti pada gambar diatas. Proses pembuatan pesanan, pembuatan pembayaran dan pengiriman email untuk pesanan tersebut akan dilakukan oleh sistem Monolith kita.

Jika tidak ada masalah pada sistem Monolith yang kita miliki, seharusnya kita tidak perlu melakukan pembuatan ulang sistem dengan arsitektur Microservices. Kecuali jika memang sistem Monolith yang kita miliki sudah tidak scale secara sistem dan pengembangan, baru ada baiknya kita buat ulang menggunakan arsitektur Microservices.

2. Dari Arsitektur Monolith ke Microservices

Jika kita sudah mendapat banyak masalah pada sistem Monolith kita, saat ini rata-rata kita akan membuat ulang sistem tersebut menggunakan arsitektur Microservices. Berbeda dengan arsitektur Monolith, pada arsitektur Microservices, kita tidak akan membuat sebuah satu aplikasi raksasa yang bisa menangani semua hal, melainkan kita akan memecahkan kedalam banyak aplikasi sesuai dengan domain-nya masing-masing.

Anggap saja, pada aplikasi ECommerce Monolith kita sebelumnya, kita buat ulang dengan menggunakan arsitektur Microservice, sehingga kita akan membuat beberapa aplikasi yang sesuai dengan domain nya, misal aplikasi pelanggan, aplikasi pemesanan, aplikasi pembayaran, dan yang lainnya.

Arsitektur Microservices

Untuk kasus yang sama sebelumnya, yaitu pembuatan pesanan baru. Maka akan berbeda dengan sistem Monolith. Di arsitektur Microservices, maka tiap proses akan dilakukan oleh domain-nya masing-masing. Misal untuk pembuaan pesanan dilakukan oleh order-service, pembuatan pembayaran dilakukan oleh payment-service dan mengirim email ke pelanggan dilakukan oleh email-service.

Sekarang pertanyaannya, bagaimana cara integrasi antar aplikasi pada arsitektur Microservices? Secara garis besar ada dua jenis cara integrasi di Microservice, yaitu menggunakan :

  • API-Driven Architecture
  • Event-Driven Architecture

Dan sebelum kita bahas tentang Event-Driven Architecture, kita akan bahas terlebih dahulu sekilas tentang API-Driven Architecture.

3. API-Driven Architecture

Dalam pengembangan sistem menggunakan arsitektur API-Driven, hal yang biasa dilakukan adalah, komunikasi antar service akan dilakukan via API call. Bisa menggunakan RESTful, RPC atau sejenisnya. Kurang lebih komunikasi akan dilakukan secara synchronous.

API-Driven Development

Pada kasus pembuatan pesanan, misal request dari pengguna akan dikirim ke order-service. Selanjutnya order-service akan melakukan API call ke payment-service untuk membuat pembayaran untuk pesanan tersebut, dan terakhir order-service akan membuat API call ke email-service untuk mengirim email ke pelanggan tentang detail pesanannya.

3.1 Orchestration Pattern

API-Driven Architecture biasanya disebut dengan Orchestration Pattern, yang dalam artian akan ada satu service yang berperan sebagai orkestrator. Service orkestrator ini bertanggung jawab terhadap jalannya alur bisnis pada aksi tersebut. Pada kasus pembuatan pesanan, order-service adalah orkestrator nya. Sedangkan payment-service dan email-service hanya mengikuti apa yang diperintahkan oleh order-service.

Orchestration Pattern

Kekurangan menggunakan Orchestration Pattern adalah, saat terjadi perubahan alur bisnis, misal ada service baru dengan nama risk-service untuk melakukan kalkulasi risk setiap pesanan, maka secara otomatis risk-service harus di panggil oleh service orkestrator nya. Dalam artian, setiap ada yang membutuhkan data pesanan, order-service harus melakukan perubahan code.

Belum lagi permasalahan dengan latency, setiap API call terhadap sistem lain yang dilakukan, maka secara otomatis akan menambah latency terhadap waktu response si service orkestrator. Makin banyak API call yang harus dilakukan, secara langsung akan meningkatkan response time si API call, sehingga makin lama, Orchestration Pattern tidak mudah untuk di-scale.

Selain itu, service orkestrator akan sangat bergantung terhadap service yang lain yang dia panggi. Pada kasus kita, order-service sangat bergantung terhadap payment-service, risk-service, dan email-service. Jika terjadi kesalahan pada service yang dia panggil, otomatis akan membuat service orkestrator menjadi tidak stabil.

4. Event-Driven Architecture

Solusi lain untuk membangun sistem Microservices, selain arsitektur API-Driven adalah Event-Driven. Berbeda dengan API-Driven, pada arsitektur Event-Driven, komunikasi akan berjalan menggunakan message event. Maksudnya apa?

Event-Driven Architecture

Misal pada kasus pembuatan pesanan, saat order-service menerima request dari pengguna, order-service akan menyimpan data pesanan tersebut di database-nya, selanjutnya order-service akan mengirim pesan berupa event notification. Biasanya event notification dikirim ke message broker, seperti Kafka atau RabbitMQ misalnya.

Siapapun yang membutuhkan data pesanan tersebut, maka service tersebut harus mengambil data tersebut dari message broker. Pada kasus kita, payment-service akan menerima data pesanan dari message broker, dan secara otomatis membuat data pembayaran untuk pesanan tersebut. Begitupula dengan email-service ketika menerima data pesanan dari message broker, maka email-service akan mengirimkan email ke pelanggan.

4.1 Choreography Pattern

Arsitektur Event-Driven biasa kita sebut juga dengan Choreography Pattern, ini kebalikan dari Orchestration Pattern, dimana di Choreography Pattern, semua sistem harus mengetahui apa yang harus dia kerjakan ketika ada sesuatu terjadi. Dalam artian, pada Choreography Pattern, semua service harus pintar bertindak sesuai domain-nya, tidak ada lagi service yang bertugas sebagai orkestrator yang menyuruh service lain untuk melakukan sesuatu.

Choreography Pattern

Salah satu keuntungan menggunakan Choreography Pattern adalah, misal pada kasus kita, jika ada service baru yaitu risk-service membutuhkan data pesanan, maka order-service tidak perlu melakukan API call lagi, bahkan tidak perlu melakukan apa-apa lagi. Risk-service hanya perlu mengambil data pesanan tersebut dari message broker.

Dengan ini, secara pengembangan akan lebih scale, karena tidak perlu ada perubahan kode di order-service, bahkan waktu response pada order-service pun tidak akan ikut terbebani dengan banyaknya service yang mengambil data pesanan.

Sekarang pertanyaannya, bagaimana mekanisme cara service lain mengambil data yang dibutuhkan? Misal pada kasus ini, bagaimana payment-service, email-service dan risk-service mendapatkan seluruh data pesanan?

Bad Part — Event Notification

Hal yang biasa dilakukan pada arsitektur Event-Driven adalah, service yang menerima data akan melakukan API call untuk mendapatkan detail data tersebut ke service yang mengirim message event tersebut. Pada kasus ini, payment-service, risk-service dan email-service akan melakukan API call ke order-service.

Artinya, setiap ada pesan yang dikirim ke message broker, order-service akan di bombardir dengan API call dari semua service. Satu request yang diterima dari pelanggan ke order-service akan menyebabkan tiga API call ke order-service. Semakin banyak service yang menginginkan data pesanan, semakin banyak yang akan melakukan API call ke order-service.

Jika diperhatikan, sekarang order-service sudah tidak ketergantungan lagi dengan service lain. Namun justru payment-service, risk-service dan email-service sekarang menjadi tergantung dengan order-service. Lantas bagaimana kita menyelesaikan permasalahan ini?

4.2 Event-Carried State Transfer

Solusi yang dapat kita lakukan untuk permasalahan pada arsitektur Event-Driven diatas adalah, dengan menggunakan Event-Carried State Transfer. Sederhananya, Event-Carried State Transfer adalah menjadikan message event yang dikirim oleh service berisikan pesan penuh. Pada kasus kita, berarti isi event yang dikirim oleh order-service berisikan semua data pesanan plus detail pesanannya, seperti barang yang dipesan, alamat pengiriman, alamat tagihan dan identitas pelanggan yang melakukan pemesanan.

Event-Carried State Transfer

Event-Carried State Transfer akan memaksa service yang mengambil data dari message broker untuk menyimpan data tersebut. Hmmm, sedikit terkesan buruk, tapi tidak sepenuhnya buruk, karena yang disimpan hanyalah data yang memang dibutuhkan saja, misal; Payment-service hanya butuh menyimpan data nomor pesanan dan total harga. Risk-service hanya butuh menyimpan no pesanan, total harga, identitas pelanggan dan alamat pengiriman. Email-service bahkan mungkin hanya butuh menyimpan data no pesanan, hanya untuk penanda apakah sudah pernah mengirim email atau belum.

Dengan Event-Carried State Transfer, tidak perlu lagi ada API call ke service yang mengirim data, dengan begitu permasalahan bombardir API call dari service yang membutuhkan data tidak terjadi lagi. Begitu juga tidak ada lagi service yang tergantung dengan service lainnya.

4.3 Keuntungan Menggunakan Event-Carried State Transfer

Seperti yang telah dijelaskan diatas, dengan menggunakan Event-Carried State Transfer, kita mendapatkan beberapa keuntungan, diantaranya :

  • Tidak ketergantungan, tiap service tidak akan tergantung dengan service yang lain lagi. Tak ada lagi saling tunggu antar team saat development, tidak ada lagi satu service bermasalah gara-gara service lainnya bermasalah.
  • High Availability, dikarenakan tiap service sekarang berdiri sendiri tanpa ketergantungan dengan service lain, maka secara otomatis jika ada service yang bermasalah atau mati, service lain tidak akan ikut bermasalah atau mati.
  • Menurunkan traffic ke si pengirim pesan event, karena tidak perlu lagi ada API call ke service yang mengirimkan pesan event.
  • Menurunkan waktu response, karena tidak perlu melakukan API call ke service lain, sehingga membuat waktu response service lebih bagus.

4.4 Kerugian Menggunakan Event-Carried State Transfer

Ada keuntungan, pasti juga ada kerugian saat kita menggunakan Event-Carried State Transfer, atau bahkan menggunakan arsitektur Event-Driven pada umumnya, diantaranya :

  • Duplikasi data, karena data service pengirim di duplikasi di service yang menerima, maka menyebabkan media penyimpanan menjadi lebih besar.
  • Eventual consistent. Event-Driven kadang tidak se-realtime menggunakan API-Call, akan ada kemungkinan terjadi delay saat service mengirim data dan menerima data, sehingga kemungkinan data tidak konsisten bisa terjadi, walaupun mungkin hanya dalam hitungan detik.
  • Alur bisnis yang berceceran. Pada kasus kita, jika menggunakan API-Driven untuk melihat alur bisnis, kita hanya perlu melihatnya di order-service saja. Namun saat menggunakan Event-Driven, kita tidak bisa melihat alur bisnis secara jelas lagi, karena semua alur bisnis sudah didistribusikan di semua service. Hal ini akan sulit bagi anggota tim baru untuk melihat alus bisnis secara gambaran besar nya.

5. Tantangan di Event-Driven Architecture

Menggunakan arsitektur Event-Driven sangat bergantung pada kondisi sistem kita sekarang. Jika memang traffic sistem sudah besar, butuh scale secara sistem dan pengembangan, maka ada baiknya kita menggunakan arsitektur Event-Driven. Namun jika masih pada tahap awal pengembangan, ada baiknya tetap menggunakan Monolith, atau API-Driven, atau gabungan antara API-Driven dan Event-Driven. Karena saat kita akan melakukan migrasi secara total ke Event-Driven, akan ada banyak tantangan yang harus kita hadapi.

5.1 Asynchronous

Event-Driven menuntut kita untuk melakukan komunikasi antar sistem secara Asynchronous. Tidak ada lagi komunikasi yang realtime mendapatkan data secara langsung dengan API call. Butuh paradigma berbeda jika terbiasa menggunakan komunikasi synchronous lalu pindah menggunakan Event-Driven sehingga membutuhkan komunikasi asynchronous.

5.2 Message Schema

Kadang di sebuah perusahaan yang sudah menggunakan arsitektur Microservices, tiap tim bisa menentukan teknologi yang diinginkan. Hal ini berimbas pada skema pesan yang mau digunakan dalam arsitektur Event-Driven. Contohnya di Blibli.com, kita menggunakan JSON sebagai format skema pesan yang digunakan sebagai event. Perlu ada kesepakatan agar semua tim bisa menerima pesan event-nya secara mudah, tanpa harus banyak melakukan perubahan pada code-nya.

5.3 Versioning

Menjaga agar pesan pada Event-Driven tetap konsisten antar versi sangatlah penting. Perlu dipastikan bahwa jika ada versi pesan yang baru, format nya tidak akan menghancurkan format pesan sebelumnya. Karena saat format pesan berubah secara drastis, maka penerima pesan yang masih menggunakan format versi sebelumnya, akan bermasalah. Sehingga perlu dipastikan, tiap naik versi, format pesan tidak boleh menghancurkan format pesan sebelumnya.

5.4 Tracing

Permasalahan seputar tracing yang terjadi pada Event-Driven pernah saya bahas pada artikel Distributed Tracing di Blibli.com. Intinya akan lebih sulit untuk melakukan tracing tiap request dari satu service ke service yang lain pada arsitektur Microservices, terutama yang menggunakan Event-Driven.

5.5 Message Order

Urutan pesan event pada arsitektur Event-Driven haruslah konsisten dengan yang diterima oleh service. Jika urutan pesan tidak konsisten, maka bisa berakibat fatal. Anggap saja misal kita melakukan perubahan harga barang sebanyak 3 kali, 1000, 2000, dan 3000. Jika urutan pesan yang diterima oleh service lain tidak konsisten, bisa jadi harga yang seharusnya 3000, malah jadi 1000 atau 2000 karena kesalahan urutan yang tidak konsisten pada event yang diterima.

Di Blibli.com, kita menggunakan garansi yang diberikan oleh Kafka yang menjamin bahwa data yang ada pada partisi yang sama, akan selalu sequential sesuai dengan urutan data di terima oleh Kafka. Oleh karena itu, data yang sama akan selalu dikirim ke partisi yang sama di Blibli.com

5.6 Consumer Complexity

Event-Driven akan membuat service penerima data lebih kompleks dibandingkan jika menggunakan API-Driven. Seperti yang dibahas sebelumnya, hal ini karena alur bisnis didistribusikan di semua service. Jadi tidak terpusat di satu service orkestrator.

5.7 Message Broker

Event-Driven membutuhkan message broker yang handal sebagai jembatannya. Perlu dipastikan bahwa message broker yang digunakan harus stabil, scalable, dan sesuai dengan kebutuhan kita.

6. Event-Driven di Blibli.com

Bagaimana dengan Blibli.com? Apakah Blibli.com sudah sepenuhnya menggunakan Event-Driven? Jawabannya ya dan tidak. Beberapa sistem masih menggunakan API-Driven, beberapa sistem sudah full Event-Driven bahkan beberapa sistem masih ada kombinasi API-Driven dan Event-Driven.

We Are Hiring for Blibli.com Bandung and Jakarta

Tertarik untuk bergabung menjadi keluarga besar Blibli.com Jakarta atau Bandung? Silahkan submit CV Anda ke halaman karir Blibli.com di https://www.blibli.com/page/karir/

Atau jika tertarik ngobrol-ngobrol dulu, Anda bisa hubungi saya Eko Kurniawan Khannedy via email eko.k.khannedy@gdn-commerce.com.

Regards.

--

--