Desain Tingkat Tinggi

Diagram komponen dengan pilihan yang terjustifikasi beserta API utamanya.

Daftar isi

Kebutuhan dan angka dari estimasi kini diterjemahkan menjadi gambar komponen beserta kontrak API-nya, yaitu bentuk desain yang langsung dikenali. Topik ini menyusun diagram dari angka yang sudah dihitung, memberi alasan hadirnya setiap kotak, menyatakan pilihan yang sengaja ditunda, dan menetapkan endpoint utama yang menjadi kontrak antara browser dan layanan aplikasi. Hasil akhirnya adalah peta komponen yang setiap kotaknya dapat dipertanggungjawabkan, bukan gambar yang meniru arsitektur populer.

Diagram komponen beserta alasannya#

Diagram berikut menempatkan tujuh komponen pada dua baris, yaitu jalur permintaan di atas dan tempat penyimpanan beserta antreiannya di bawah. Alasan setiap kotak diturunkan dari angka estimasi, bukan dari kebiasaan. Alasan itu dibahas berurutan mulai dari klien hingga tempat penyimpanannya.

Komponen desain tingkat tinggi aplikasi catatanBrowserLoad balancerLayanan aplikasi (stateless)replika mengikuti beban puncakbaca dan tulisBasis datateks dan metadataCachebaca berulangQueue tugas latarindeks pencarianObject storagelampiran catatan
Komponen desain tingkat tinggi aplikasi catatan

Browser adalah klien aplikasi web dan tempat seluruh permintaan dimulai, baik membaca catatan, mencari, maupun mentransfer lampiran. Load balancer menjadi satu titik masuk dengan alamat publik, sehingga jumlah replika layanan aplikasi boleh berubah tanpa memberi tahu klien, dan health check miliknya mengeluarkan replika yang bermasalah dari perputaran. Layanan aplikasi dirancang stateless, yaitu tidak menyimpan state apa pun secara lokal. Dengan QPS puncak sekitar 70, satu sampai dua replika sudah cukup, tetapi bentuk stateless tetap dipertahankan agar penambahan replika tidak menyentuh kode, sedangkan state ditempatkan pada tempat yang memang untuknya, yaitu basis data dan cache.

Basis data adalah sumber kebenaran catatan. Teksnya kecil, sekitar 36,5 GB per tahun, dan aksesnya berpola baca berdasarkan pengenal catatan, sehingga satu basis data dengan replikanya masih jauh dari batas kemampuannya. Pembaruan parsial dan transaksi tulis yang dijanjikan basis data memang dibutuhkan, karena penyuntingan catatan berlangsung beruntun. Cache menyimpan hasil baca yang berulang, terutama daftar catatan terbaru dan isi catatan yang baru dibuka, karena delapan dari sepuluh permintaan berupa baca, dan bila cache meleset permintaan jatuh ke basis data yang sudah dirancang mampu menanganinya.

Object storage menampung lampiran yang volumenya 50 kali teksnya, tersimpan sebagai objek dalam bucket dengan pengenal catatan sebagai bagian dari kuncinya. Berkas ditulis sekali dan dibaca berulang tanpa pembaruan parsial, persis pola yang dilayani object storage, sedangkan URL bertanda tangan (presigned URL) membuat transfer besar tidak melintasi layanan aplikasi. Queue tugas latar memisahkan pekerjaan yang tidak boleh menunda respons tulis, terutama pembaruan indeks pencarian, yang ditarik oleh worker di luar jalur permintaan dan diperdalam pada topik Pendalaman dan Tradeoff.

Pilihan yang ditunda beserta pemicunya#

Desain yang jujur juga menyatakan apa yang tidak dimasukkan. Tiga komponen yang sering muncul pada arsitektur serupa sengaja ditunda, dan setiap penundaan disertai pemicu yang akan membangkitkannya kembali.

Komponen yang ditundaAlasan pada lingkup iniPemicu untuk memasukkannya
Mesin pencarian terpisah18 juta catatan per tahun masih dapat dilayani indeks di basis dataKueri pencarian melambat atau kebutuhan relevansi dan stemming meningkat
CDNUnduhan lampiran hanya sekitar 1,4 Mbps dan pengguna berada pada satu wilayahPengguna tersebar geografis dan unduhan lampiran menjadi menonjol
Sharding basis dataTeks 36,5 GB per tahun masih dalam jangkauan satu basis dataUkuran atau laju tulis melebihi kapasitas satu simpul

Tabel penundaan ini menjaga diagram tetap kecil, dan setiap pemicunya dapat diperiksa ulang dari angka estimasi, sehingga keputusan tersebut tidak terasa seperti menghemat usaha, melainkan seperti memilih waktu yang tepat untuk menambah kompleksitas. Pemicu pada kolom terakhir dinyatakan secara kualitatif karena batas persisnya baru terukur setelah sistem berjalan dan dipantau, dan saat itu datanya sudah cukup untuk memutuskan dengan angka, bukan dengan dugaan. Dengan begitu, tabel penundaan bukan catatan kaki, melainkan bagian desain yang statusnya sama dengan diagramnya sendiri.

Kontrak API utama#

Kontrak antara browser dan layanan aplikasi dituliskan sebagai daftar endpoint dengan gaya REST sebagaimana dibahas pada modul Fondasi Web dan Jaringan. Daftarnya sengaja pendek, karena setiap endpoint harus mewakili satu kebutuhan nyata aplikasi catatan.

text Kontrak API utama aplikasi catatan
POST   /sesi                         membuat sesi setelah otentikasi satu akun
GET    /catatan                      daftar catatan, dengan kursor untuk paginasi
GET    /catatan?kueri=kata           mencari catatan pada judul dan isi
POST   /catatan                      membuat catatan baru
GET    /catatan/{id}                 membaca satu catatan
PUT    /catatan/{id}                 memperbarui catatan secara utuh
DELETE /catatan/{id}                 menghapus catatan
POST   /catatan/{id}/lampiran        meminta URL unggah bertanda tangan
GET    /catatan/{id}/lampiran/{lid}  meminta URL unduh bertanda tangan

Dua keputusan pada kontrak ini perlu ditegaskan. Pertama, pencarian tidak diberi endpoint terpisah, melainkan menjadi parameter pada endpoint daftar, sehingga klien tidak perlu menghafal dua jalur untuk kebutuhan yang mirip. Kedua, unggah dan unduh lampiran berbentuk dua langkah, yaitu layanan aplikasi mengeluarkan URL bertanda tangan, kemudian browser mentransfer berkas langsung dengan object storage. Pola dua langkah ini menindaklanjuti hasil estimasi bandwidth, yaitu payload besar sebaiknya tidak memakan waktu layanan aplikasi.

Metode penulisan dipilih agar idempoten, artinya permintaan yang sama yang dikirim dua kali menghasilkan keadaan akhir yang sama. PUT memperbarui catatan secara utuh sehingga sifat itu terpenuhi secara alami, dan DELETE pada catatan yang sudah terhapus juga berakhir pada keadaan yang sama. Paginasi menggunakan kursor alih-alih offset, karena daftar yang bergeser saat ada catatan baru membuat offset mengembalikan butir yang terlewat atau ganda.

Kesalahan umum#

Tiga kesalahan berikut paling sering muncul saat gambar komponen disusun, dan ketiganya berpola sama, yaitu menambahkan sesuatu tanpa menimbang biayanya.

  1. Menambah kotak karena arsitektur populer memilikinya. Komponen yang diambil dari gambar orang lain datang bersama biaya operasionalnya, padahal lingkupnya mungkin tidak membutuhkannya. Perbaikannya, jalankan ujian penghapusan kotak, yaitu tanyakan apa yang hilang bila kotak itu tidak ada, lalu gambar kembali diagramnya tanpa kotak yang tidak mempunyai jawaban.
  2. Merancang endpoint dari struktur tabel. Endpoint yang meniru tabel memaksa klien melakukan banyak panggilan untuk satu kebutuhan layar, dan kontraknya berubah setiap kali skema berubah. Perbaikannya, rancang endpoint dari kebutuhan layar aplikasi catatan, seperti daftar, baca, tulis, cari, dan lampirkan.
  3. Menyimpan state pada layanan aplikasi, misalnya sesi pengguna di memori proses. Replika tidak lagi saling menggantikan, sehingga penambahan replika tidak lagi menaikkan kapasitas sebagaimana diperhitungkan. Perbaikannya, letakkan state pada cache dan basis data, dan pertahankan layanan aplikasi tetap stateless.

Ringkasan#

  • Diagram memuat tujuh komponen, dan setiap kotaknya dibenarkan oleh angka estimasi, bukan oleh kebiasaan gambar arsitektur.
  • Tiga komponen ditunda, yaitu mesin pencarian terpisah, CDN, dan sharding, masing-masing dengan pemicu terukur untuk memasukkannya kembali.
  • Kontrak API dipendekkan dengan tiga keputusan, yaitu pencarian sebagai parameter daftar, lampiran dua langkah melalui URL bertanda tangan, dan penulisan yang idempoten dengan paginasi kursor.
  • Layanan aplikasi tetap stateless, sehingga jumlah replikanya mengikuti beban puncak tanpa perubahan kode.

Referensi#