Berkenalan dengan Event

Dalam dunia perangkat lunak, event artinya berita kejadian penting. Pengirim event bisa berupa aplikasi, sistem operasi, perangkat keras, atau web service. Event seringkali membawa data penting, semisal siapa pengirimnya, apa yang memicunya, atau apa perubahan yang terjadi. Contoh:

  • Event datang dari sensor pintu, datanya misal doorId("FrontDoor", "KitchenDoor") dan doorStatus (OPENED, CLOSED, LOCKED).

  • Event ketika mouse diklik, datanya misal mouseKey("Left", "Right", "Scroll")

  • Event ketika berkas selesai diunggah, datanya misal fileSize

  • Event ketika pengguna ingin mendaftarkan diri ke aplikasi web, datanya misal username, displayName, dateOfBirth

Masing-masing event di atas berbeda cara penyampaiannya. Sensor pintu itu berbasis IoT memakai MQTT untuk disampaikan ke ponsel kita. Klik mouse disampaikan sistem operasi ke aplikasi desktop. Aplikasi memantau jaringan dan memberitahu pengguna bahwa berkas selesai diunggah. Web service menyampaikan event sebagai sebuah message melalui message queue, semisal RabbitMQ.

Event adalah suatu cara untuk membalik kontrol. Tanpa event, aplikasi harus berkali-kali mengecek status tiap tombol mouse, lalu bereaksi ketika status tombol mouse berubah. Melalui event, aplikasi hanya perlu bereaksi ketika ada event klik datang.

Salah satu implementasi event paling primitif adalah dengan function pointer. Penerima event mendaftarkan function pointer kepada pengirim event. Ketika event terjadi, si pengirim memanggil function pointer yang didaftarkan oleh penerima. Data event dikirimkan melalui parameter fungsi. Dalam konteks ini, event sering disebut dengan callback. Ibaratnya seorang pelanggan VVIP menelpon ke resepsionis hotel, "Nanti telepon saya balik ya, kalau sudah ada kamar kosong".

Di bahasa-bahasa OOP, event menjadi bagian dari public interface suatu klas bersama dengan public methods. Dengan adanya mekanisme event, pengirim bisa menentukan kapan suatu data dikirim tanpa perlu tahu siapa yang akan menerima. Dengan begini, event bisa digunakan untuk membalik ketergantungan. Jika tadinya pengirim harus tahu siapa penerima untuk bisa memanggil langsung fungsi yang dimiliki penerima, sekarang penerima yang harus tahu siapa yang mengirim untuk bisa dipanggil fungsinya. Bahkan, bisa saja penerima tidak tahu pengirim kalau event disambungkan saat program berjalan atau diterima oleh mediator sebelum dikirimkan ulang ke penerima. Pengirim Bahasa kerennya, pengirim dan penerima event sudah loosely coupled.

Di dalam sistem terdistribusi, "objek" yang menjadi target terpisah proses atau bahkan berbeda mesin. Baik memanggil fungsi maupun menerima event harus dilakukan melalui jaringan. Walaupun secara prinsip sama saja, banyak masalah timbul ketika event diterapkan pada sistem terdistribusi. Setidaknya, tiga masalah utama timbul ketika kita menerapkan sistem terdistribusi berbasis event:

  • Event tak sampai

    Ketika event masih berupa callback dalam sebuah aplikasi, maka event tak sampai kemungkinannya sangat kecil, kecuali terjadi race condition. Ketika data event dilewatkan jaringan, maka sering sekali terjadi data hilang di tengah jalan

  • Event tak terurut

    Dalam sistem terdistribusi, bisa saja terjadi ada banyak service yang memroses antrian event secara paralel. Beberapa event yang saling berhubungan dan harusnya diproses secara terurut bisa saja terselip.

  • Event pasif agresif

    Jangan ketawa! Ini yang bilang al-ustadz Martin Fowler. Kalau suatu service mengirim event kemudian menunggu event lain sebagai balasannya, itulah pasif agresif. Niatnya nyuruh tapi bilangnya ngasih tahu. Terus kalo nggak ditanggapi ngambek. Karena itu, jangan buru-buru menepuk dada ketika sudah berhasil menerapkan event driven architecture. Satu service dengan service lainnya mungkin sudah tidak terang-terangan saling tergantung. Akan tetapi, kalau event dilepas lalu pengirim dan penerima jadi tidak bisa jalan dua-duanya, percuma pakai event. Kalau penerima jadi tidak jalan, wajar. Tidak masalah karena itu ketergantungan satu arah saja.

Ada lagi konsep topik. Tidak penting siapa pengirim dan siapa penerima event. Yang penting adalah data dari event itu sendiri. Untuk mengelompokkan event-event yang berhubungan, digunakanlah topik. Dengan begini, event jadi terlepas dari pengirimnya. Akhirnya, pengirim, event, dan penerimanya benar-benar bisa loosely coupled!

Tidak semudah itu. Secara terang-terangan sih mungkin tidak saling tergantung, tapi secara proses bisa saja masih saling menunggu dan berharap. Ini menurut saya lebih buruk dari ketergantungan yang terang-terangan. Kalau dalam perguruan OOP, saya diajari untuk mendeteksi kesalahan paling bagus pada compile time. Kalau tidak bisa dideteksi pada saat compile time, pada saat initialization time. Kalau tidak bisa, baru pada saat runtime. Kalau tadinya sudah jelas saling tergantung, jangan dibikin pura-pura tidak saling tergantung dengan event driven architecture. Itu namanya membohongi diri sendiri sekaligus menyiksa diri. Soalnya, event driven architecture itu lebih susah untuk diimplementasikan dan di-debug.

Yang jelas, saya tidak suka dengan event yang tidak diketahui siapa pengirimnya. Pembatasan hak akses jadi lebih rumit. Biasanya memang lalu lintas event itu ada di dalam jaringan aman. Tapi, kalau tidak ada pembatasan hak akses, akan terjadi kekacauan dan tumpang tindih lalu lintas data. Apalagi jika sistemnya besar dengan banyak service yang saling tersambung. Ini artinya setiap event harus mempunyai pengirim, sehingga bisa ditentukan apakah pengirim berhak mengirim event tersebut atau tidak. Setiap penerima juga harus dibatasi, event apa saja yang boleh dia terima.

Dengan cara pandang OOP yang sudah terpatri di otak saya, maka event adalah bagian dari public interface suatu klas. Microservice anggap saja sama dengan klas lah, biar nggak pusing. Data-data yang ada pada event biasanya berhubungan erat dengan pengirimnya. Kita akan jarang menemukan dua data yang bertipe sama kalau pengirimnya tidak memiliki peran serupa. Dengan begini, saya merasa lebih enak untuk menganalisisnya.

Atau mungkin saya menganalisis event dengan kacamata yang salah. Seharusnya, saya menganggap event itu sebagai message. Event mungkin harus dianggap sebagai surat dari pengirim kepada penerima. Artinya, kalau begitu event harus punya alamat penerima, tapi tidak harus punya alamat pengirim. Penerima boleh saja satu, boleh saja banyak. Kalau banyak, bisa sebagian, bisa semuanya. Kalau bahasa jaringan, bisa unicast, multicast, ataupun broadcast. Soal hak akses, penerima yang harus mengecek apakah suatu message itu harus ditanggapi atau tidak. Kalau memang tidak kenal atau tidak berhak, ya tidak usah ditanggapi.

Sekian lamunan saya soal event. Meski belum banyak tahu, tapi paling tidak saya sudah faham garis besarnya. Dan, seringkali lamunan filosofis inilah yang bermanfaat untuk menganalisis permasalahan. Soal bagaimana diimplementasikan, ahlinya sudah banyak. Misal, agar event selalu sampai, agar event cepat sampai, agar event bisa dibagi-bagi ke banyak _service, dan lain-lain. Saya sementara belajar dulu dasar-dasarnya saja. Mudah-mudahan, kalau ada waktu, bisa diperdalam lagi.