FCM Adalah Jalur Wake-Up, Bukan Transport Real-Time
Aplikasi mobile dapat mempertahankan WebSocket ketika aktif dan tetap membutuhkan Firebase Cloud Messaging saat sistem operasi menangguhkannya. Kedua mekanisme itu menangani kondisi kegagalan yang berbeda.
WebSocket, MQTT, dan SignalR mengasumsikan client dapat berpartisipasi dalam sesi komunikasi yang hidup. FCM justru berguna ketika asumsi itu tidak lagi berlaku: aplikasi mungkin berada di background, prosesnya mungkin tidak berjalan, atau koneksi persistennya sudah hilang.
Menganggap FCM sebagai transport real-time lain menyamarkan boundary tersebut. Desain yang lebih kuat memakai live channel selama tersedia dan memperlakukan push delivery sebagai sinyal agar aplikasi kembali aktif lalu merekonsiliasi state.
Koneksi adalah batas pemisahnya
WebSocket membentuk koneksi bidirectional antara dua endpoint. MQTT biasanya mempertahankan koneksi client ke broker dan menambahkan semantik messaging seperti topic, subscription, session, dan Quality of Service. ASP.NET Core SignalR mengelola koneksi real-time pada level yang lebih tinggi dan dapat memakai WebSockets, Server-Sent Events, atau Long Polling sesuai kemampuan client dan server.
Ketiganya bergantung pada jalur komunikasi yang aktif.
FCM memiliki bentuk berbeda:
persistent channel tersedia
client <====================> application server
WebSocket / MQTT /
SignalRKetika jalur itu hilang, server tidak dapat mengirim frame WebSocket lain melalui koneksi yang sudah mati. Menyimpan application state di broker tidak membuat proses yang ditangguhkan kembali mengeksekusi kode. Logika reconnect SignalR juga tidak dapat berjalan ketika sistem operasi tidak memberikan execution time kepada aplikasi.
Push service menyediakan jalur lain:
application server
|
| push request
v
FCM
|
| OS-mediated delivery
v
mobile device
|
| app resumes
v
sync authoritative stateProperti arsitektural yang penting bukan bahwa FCM adalah “WebSocket yang lebih lambat”. Delivery dimediasi platform mobile, bukan koneksi persisten milik aplikasi sendiri.
Push message tidak seharusnya menjadi database
Misalkan server chat menerima message m42 ketika penerima sedang offline. Salah satu desain memasukkan seluruh message ke payload FCM lalu menganggap penerimaan push sebagai selesainya delivery.
Desain tersebut mengikat durable application state ke notification channel.
Boundary yang lebih aman menyimpan message terlebih dahulu, kemudian mengirim invalidation atau wake-up signal berukuran kecil:
sender
|
| SEND m42
v
server
|
+---- persist m42
|
+---- FCM: "conversation 7 changed"
|
v
recipient
|
| GET /sync?cursor=...
v
serverJika push tiba, client melakukan sinkronisasi. Jika beberapa push pada praktiknya melebur menjadi satu wake-up, sinkronisasi tetap membangun kembali state terbaru. Jika push terlambat, aplikasi dapat menjalankan sinkronisasi yang sama saat berikutnya aktif tanpa bergantung pada notification body yang hilang.
Dengan boundary ini, peran FCM berubah dari state transfer menjadi state invalidation.
Delivery acknowledgement berada di application layer
FCM menerima request bukan berarti aplikasi penerima sudah menerima domain event. Perbedaan serupa ada pada layer lain.
MQTT QoS 1, misalnya, menetapkan at-least-once delivery untuk pertukaran protokol MQTT dan memakai PUBACK. QoS 2 menambahkan protocol flow untuk exactly-once delivery di antara peer MQTT yang relevan. Jaminan itu tidak membuktikan UI chat sudah merender message atau pengguna sudah membacanya.
WebSocket menyediakan framing di atas koneksi aktif, tetapi tidak mendefinisikan konsep application-level seperti “stored”, “delivered to device”, atau “read”. SignalR juga memberi aplikasi model pemrograman real-time; status delivery pada level produk tetap merupakan semantik aplikasi.
Karena itu, sistem messaging sebaiknya memiliki boundary eksplisit:
message_id = m42
sender -> server SEND m42
sender <- server ACCEPTED m42
server -> recipient MESSAGE m42
server <- recipient DELIVERED m42
recipient -> server READ m42FCM dapat menyebabkan recipient reconnect lalu mencapai transisi DELIVERED. Push itu sendiri tidak seharusnya dianggap sebagai transisi tersebut.
FCM dan WebSocket membentuk pasangan foreground/background
Untuk aplikasi chat mobile, state machine lebih berguna daripada tabel perbandingan protokol:
app active
|
v
persistent channel
WebSocket / SignalR
|
connection lost
v
offline
|
FCM signal
v
app gets execution
|
reconnect
v
sync
|
+------> live channelSaat aplikasi aktif, persistent connection membawa event berlatensi rendah. Ketika koneksi hilang, server mencatat event secara durable. FCM menyediakan jalur untuk memicu device ketika kebijakan platform mengizinkan. Setelah aplikasi kembali memperoleh execution time, client mengambil state menggunakan application cursor atau mekanisme sinkronisasi lain.
Desain ini tidak mengharuskan setiap notification FCM berkorespondensi satu-ke-satu dengan domain event. Sepuluh message baru dapat diwakili satu wake-up signal karena operasi sync, bukan jumlah push, yang menentukan apa saja yang terlewat oleh client.
MQTT mengubah semantik messaging, bukan kebijakan proses mobile
MQTT menarik ketika aplikasi membutuhkan brokered publish/subscribe, retained message, session state, dan level QoS eksplisit. MQTT 5 mendefinisikan QoS 0 sebagai at most once, QoS 1 sebagai at least once, dan QoS 2 sebagai exactly once untuk protocol delivery flow.
Fitur tersebut menangani pertukaran message selama peer MQTT dapat berkomunikasi dan, bergantung pada konfigurasi session, state yang dapat bertahan melewati network session yang terputus.
Fitur itu tidak memberikan hak background execution tanpa batas kepada proses mobile.
Perbedaan ini penting karena developer dapat mengganti WebSocket dengan MQTT dan tetap menghadapi masalah lifecycle mobile yang sama:
broker
X
|
| no usable client connection
|
suspended mobile processMQTT dapat memperbaiki session recovery setelah reconnect. FCM dapat membantu memicu kesempatan untuk reconnect. Keduanya saling melengkapi ketika kedua properti tersebut dibutuhkan.
SignalR mengurangi connection plumbing, bukan menghapus offline boundary
SignalR juga mudah dibandingkan terlalu langsung dengan FCM. ASP.NET Core SignalR mengelola connection, merutekan message ke client atau group, dan memilih transport real-time yang didukung. WebSockets menjadi transport yang diprioritaskan ketika tersedia, dengan Server-Sent Events dan Long Polling sebagai alternatif.
Abstraksi tersebut mengurangi kode aplikasi yang harus menangani transport negotiation dan connection machinery. Namun, abstraction itu tidak mengubah proses mobile yang tidak tersedia menjadi client SignalR yang terhubung.
Backend .NET karena itu dapat memakai SignalR untuk sesi aktif dan FCM untuk device mobile yang tidak aktif tanpa menduplikasi tanggung jawab:
+--> SignalR --> connected clients
domain event --------+
+--> FCM -----> inactive mobile clientsKedua jalur harus bermuara pada durable application state yang sama. Jika tidak, perilaku reconnect akan bergantung pada transport mana yang kebetulan membawa event.
Ukuran payload mendorong penggunaan indirection
FCM memang dirancang dengan payload kecil. Dokumentasi HTTP v1 menetapkan batas payload 4096 byte untuk sebagian besar message, dengan batas lebih rendah untuk message ke topic pada konteks tertentu yang didokumentasikan.
Walaupun sebuah domain object muat, memasukkan seluruh object authoritative ke push notification menimbulkan masalah versioning dan konsistensi. Server dapat mengubah atau mencabut object setelah notification masuk antrean. Client yang kemudian memakai payload lama dapat membangun state yang salah.
Identifier atau invalidation signal menghindari masalah tersebut:
{
"type": "conversation_changed",
"conversation_id": "7",
"cursor_hint": "18422"
}Client memperlakukan field tersebut sebagai alasan untuk sinkronisasi, bukan bukti bahwa local state sudah lengkap.
TTL bukan application retention policy
FCM mendukung time-to-live untuk message, tetapi push TTL dan application retention menjawab pertanyaan yang berbeda.
Push TTL menentukan berapa lama infrastruktur push boleh terus mencoba mengirim notification. Application retention menentukan berapa lama server mempertahankan message, event, atau state yang dibutuhkan untuk recovery.
Jika message chat harus tersedia selama 30 hari, requirement itu berada di storage chat. Requirement tersebut tidak seharusnya diimplementasikan dengan mengatur notification TTL lalu menganggap notification service sebagai archive.
Pemisahan yang sama membuat logika reconnect deterministik:
push expired? irrelevant to durable state
socket disconnected? irrelevant to durable state
client cursor = 120
server cursor = 127
sync -> events 121..127Kegagalan transport mengubah kapan client melihat state, bukan apakah authoritative state tersebut ada.
Sinkronisasi idempotent menyerap sinyal duplikat
Wake-up signal dapat beradu dengan koneksi yang sebenarnya masih aktif. Pengguna mungkin menerima event melalui WebSocket sesaat sebelum notification FCM memicu synchronization request lain.
Aplikasi perlu menganggap overlap tersebut normal.
Jika event memiliki identifier stabil atau monotonic cursor, sinkronisasi dapat dibuat idempotent:
local cursor: 127
WebSocket event 128
-> apply 128
-> cursor = 128
FCM wake-up arrives
-> sync after 128
-> no duplicate domain eventTanpa properti ini, menambahkan FCM sebagai fallback dapat menghasilkan notification duplikat, insert berulang, atau unread counter yang tidak konsisten. Kegagalannya bukan berasal dari FCM sendiri, tetapi dari perlakuan terhadap beberapa delivery path seolah masing-masing merupakan sumber authoritative.
Memilih live channel
Setelah FCM dipisahkan ke peran wake-up, pilihan antara WebSocket, MQTT, dan SignalR menjadi lebih jelas.
WebSocket cocok ketika aplikasi membutuhkan bidirectional persistent channel dan siap mendefinisikan sendiri semantik reconnect, routing, acknowledgement, dan state synchronization.
MQTT cocok untuk sistem yang memperoleh manfaat dari semantik publish/subscribe terstandar, subscription yang dikelola broker, session behavior, retained message, dan QoS yang dapat dipilih.
SignalR cocok untuk aplikasi yang menginginkan model pemrograman real-time pada level lebih tinggi, terutama dalam stack ASP.NET Core, dengan connection management dan transport selection ditangani framework.
Tidak satu pun pilihan tersebut menghapus kebutuhan durable state pada aplikasi mobile yang harus bertahan melewati suspension dan disconnection.
Arsitekturnya dapat diringkas menjadi dua keputusan independen:
Bagaimana client aktif berkomunikasi?
WebSocket / MQTT / SignalR / live channel lain
Bagaimana client mobile yang tidak aktif mendapat kesempatan untuk berjalan lagi?
platform push service seperti FCMSynchronization protocol menghubungkan kedua keputusan itu. Persistent connection mengoptimalkan jalur saat client online. FCM memulihkan kesempatan berkomunikasi setelah jalur tersebut hilang. Durable state, application acknowledgement, dan idempotent synchronization membuat kedua jalur tersebut berperilaku sebagai satu sistem.