Feature-Based Frontend Architecture
On this page
- Sekilas
- Gagasannya secara singkat
- A. Fitur adalah satuannya
- B. Yang dipakai bersama tetap global, per jenis
- C. Data mengalir satu arah
- D. Satu design system, dikunci per versi
- Kenapa saya memilihnya
- Kesepakatan saya
- Cara saya memeriksa project
- Di mana saya tidak memakainya
- Aplikasi Flutter
- Rujukan
- Istilah
Feature-Based Frontend Architecture adalah bentuk yang saya berikan pada antarmuka web berbasis Vue: tiap layar dan komponen milik satu fitur tinggal di folder fitur itu, yang dipakai bersama beberapa fitur ditaruh di beberapa folder global, dan tiap request ke backend lewat satu HTTP client. Ini kesepakatan saya sendiri, bukan rancangan dari buku, dan saya memakainya ulang di tiap project Vue yang saya mulai.
Ini salah satu arsitektur yang saya pakai, dan ia mengisi bagian front end web. Aturan bisnis sistem saya tinggal di backend service di belakang antarmuka, jadi front end tak butuh lapisan-lapisan yang saya pakai di sana; yang ia butuhkan adalah struktur yang membuat layar mudah dicari, ditambah, dan dibuang.
Sekilas
| Asal | Kesepakatan saya sendiri; dekat dengan Feature-Sliced Design yang disederhanakan, ditambah service layer |
|---|---|
| Saya pakai untuk | Antarmuka web berbasis Vue: admin panel, dashboard, aplikasi web |
| Tak saya pakai untuk | Aplikasi Flutter, dan apa pun di luar browser |
| Bahasa | TypeScript dengan Vue 3 |
Gagasannya secara singkat
A. Fitur adalah satuannya
Tiap fitur antarmuka — pesanan, user, laporan — punya folder sendiri berisi halaman-halamannya dan komponen yang cuma ia pakai. Bila sesuatu milik satu fitur dan tak dipakai di tempat lain, ia tinggal di folder fitur itu, termasuk service atau type miliknya sendiri bila tak ada fitur lain yang membutuhkannya.
B. Yang dipakai bersama tetap global, per jenis
Yang dipakai beberapa fitur masuk ke sekumpulan kecil folder global, satu per jenis: service yang memanggil backend, store yang memegang state bersama, type, layout, komponen bersama, konfigurasi, terjemahan, dan utilitas. Sepotong kode baru pindah dari folder fitur ke folder global ketika fitur kedua membutuhkannya.
C. Data mengalir satu arah
Halaman membaca dan mengubah state bersama lewat store, store memanggil service, dan service memanggil backend lewat satu HTTP client. Halaman juga boleh memanggil service langsung ketika tak ada state yang perlu dibagi. Tak ada bagian service atau store yang meng-import halaman atau komponen.
D. Satu design system, dikunci per versi
Lapisan tampilannya berasal dari design system saya sendiri, package terpisah berisi komponen dan design token. Tiap project membawa salinan hasil build-nya di folder bernama versinya, menjangkaunya lewat satu import alias, dan tak pernah menyuntingnya dengan tangan; memperbarui design system berarti menyinkronkan versi baru ke folder itu.
Kenapa saya memilihnya
Saya menemukan halaman lewat nama fiturnya. Ketika sesuatu di layar perlu diubah, saya tahu fitur mana pemiliknya, dan semua yang dibutuhkan fitur itu ada di satu folder. Saya tak perlu mengingat folder berbasis jenis mana saja yang memegang halaman, komponen, dan helper-nya.
Fitur mudah dibuang atau dipindah. Karena fitur menyimpan halaman dan komponennya bersama-sama, membuangnya sebagian besar cukup dengan menghapus satu folder, dan memindahkannya ke project lain sebagian besar cukup dengan menyalin satu folder. Folder global hanya memegang yang benar-benar dipakai bersama, jadi fitur jarang meninggalkan jejak di tempat lain.
Antarmuka tampil sama di semua project. Tiap project memakai design system yang sama, jadi tombol, tabel, form, dan warna berperilaku sama di mana-mana. Menguncinya per versi berarti project hanya berganti tampilan ketika saya memutuskan memperbaruinya, bukan karena kebetulan.
Akses ke backend ada di satu tempat. Tiap request lewat satu HTTP client, yang melampirkan access token dan, ketika backend menjawab bahwa token-nya kedaluwarsa, memperbaruinya sekali lalu mengulang request yang sedang menunggu. Tak ada halaman yang harus menangani autentikasi atau pengulangan sendiri.
Kesepakatan saya
Project Vue saya yang mengikuti arsitektur ini punya pohon src/ seperti berikut:
| Folder | Isinya | Boleh meng-import |
|---|---|---|
features/<name>/ | halaman dan komponen satu fitur, plus service atau type miliknya sendiri bila cuma ia yang butuh | store, service, shared, type, design system |
stores/ | state bersama dan action yang mengubahnya | service, type |
services/ | satu module per resource backend, semuanya dibangun di atas satu HTTP client | HTTP client, type |
shared/, layouts/ | komponen dan bingkai halaman yang dipakai beberapa fitur | design system, store |
Service membungkus satu resource backend dan tak lebih:
// services/orderService.ts
import { httpClient } from './httpClient'
import type { Order } from '@/types/order'
export const orderService = {
list: () => httpClient.get<Order[]>('/orders').then((r) => r.data),
create: (input: Partial<Order>) => httpClient.post<Order>('/order', input).then((r) => r.data),
}Design system dijangkau lewat import alias, jadi halaman menulis import { DataTable } from '@theme' tanpa tahu folder versi mana yang berdiri di baliknya. Mengganti versi berarti mengganti folder dan alias-nya, bukan tiap import.
Cara saya memeriksa project
Inilah pertanyaan yang saya ajukan kepada project Vue, diambil dari kesepakatan di atas:
| Bidang | Pertanyaan yang saya ajukan | Sumber |
|---|---|---|
| Fitur | Apakah tiap halaman tinggal di folder fitur pemiliknya? | fitur adalah satuannya |
| Berbagi | Apakah semua isi folder global dipakai paling sedikit dua fitur? | yang dipakai bersama tetap global |
| Arah | Apakah service dan store menghindari import halaman dan komponen? | data mengalir satu arah |
| Akses backend | Apakah tiap request lewat satu HTTP client itu? | akses di satu tempat |
| Design system | Apakah design system dipakai lewat alias-nya, di versi yang dikunci, dan tak pernah disunting di tempat? | satu design system |
| Pembuangan | Bisakah fitur dibuang dengan menghapus folder dan route-nya? | mudah dibuang |
Di mana saya tidak memakainya
Aplikasi Flutter
Aplikasi Flutter saya membawa logika aplikasi sungguhan di perangkat, jadi saya memberinya Clean Architecture, dengan lapisan presentation/ untuk layar-layarnya. Antarmuka web berbasis Vue sebagian besar menampilkan dan menyunting apa yang diputuskan backend, karena itulah ia tak butuh lapisan-lapisan tadi, sedangkan aplikasi Flutter butuh.
Rujukan
Arsitektur ini kesepakatan saya sendiri dan tak punya buku di belakangnya. Kerabat terdekatnya yang sudah bernama adalah Feature-Sliced Design, metodologi untuk menyusun project front end menurut lapisan dan irisan fitur; milik saya bentuk yang lebih sederhana dari gagasan yang sama, digabung dengan service layer untuk akses backend. Saya menyebutnya sebagai kemiripan, bukan sumber.
Istilah
- Fitur
- Satu wilayah antarmuka yang dikenali user sebagai satu kesatuan, misalnya pesanan atau user, beserta halaman dan komponennya.
- Store
- Tempat yang memegang state bersama beberapa halaman, beserta action yang mengubahnya. Di project Vue saya memakai Pinia untuk ini.
- Service
- Module yang membungkus satu resource backend dan mengubah panggilan HTTP-nya menjadi fungsi biasa.
- Design system
- Package bersama berisi komponen dan design token — warna, huruf, jarak — yang menjaga beberapa antarmuka tampil dan berperilaku sama.
- Vendoring
- Menyalin versi hasil build sebuah dependency ke dalam project itu sendiri, bukan memasangnya dari package registry.
- Import alias
- Nama pendek, misalnya
@theme, yang dipetakan build tool ke folder sungguhan, sehingga import tak berubah ketika folder-nya berubah.
Technologies covered
- Vue