GraphQL BFF vs Gateway bukan pertanyaan tentang pola mana yang paling modern, tetapi pola mana yang paling sesuai dengan ukuran tim, batas domain, beban query, dan kebutuhan evolusi schema Anda. Untuk tim kecil atau produk dengan satu-dua klien utama, BFF sering lebih cepat dibangun dan lebih mudah dikendalikan. Untuk organisasi multi-domain dengan banyak service owner, gateway atau federation ringan biasanya lebih masuk akal karena ownership schema dapat dibagi tanpa memusatkan semua logika ke satu tim.

Masalah utama biasanya muncul bukan saat sistem masih kecil, tetapi ketika schema mulai melebar, resolver makin kompleks, dan perubahan kecil perlu koordinasi lintas tim. Di titik itu, keputusan antara BFF dan gateway akan memengaruhi latensi, observability, deployment, caching, kontrak API, dan kecepatan perubahan jangka panjang.

Apa yang dimaksud dengan GraphQL BFF dan Gateway?

GraphQL BFF

Backend for Frontend berarti ada satu lapisan GraphQL yang dibuat khusus untuk kebutuhan satu client atau satu kelompok client yang mirip, misalnya web app internal, mobile app, atau dashboard admin. Lapisan ini biasanya:

  • menggabungkan data dari beberapa service upstream, REST API, database, atau message-driven backend,
  • membentuk schema yang dioptimalkan untuk kebutuhan UI,
  • menyimpan logika komposisi di resolver layer,
  • sering dimiliki oleh satu tim aplikasi atau platform kecil.

Kelebihan utamanya adalah kecepatan iterasi. Tim frontend dan backend aplikasi bisa mengubah bentuk query atau menambah field tanpa menunggu koordinasi panjang dengan banyak domain owner.

GraphQL Gateway atau Federation Ringan

Gateway adalah lapisan GraphQL yang menerima request dari client lalu mendelegasikan sebagian eksekusi ke beberapa subgraph atau service GraphQL/domain service di belakangnya. Federation ringan di sini tidak harus berarti memakai implementasi kompleks; idenya adalah schema dibangun dari beberapa domain yang punya ownership terpisah.

Ciri umum pendekatan ini:

  • setiap domain memiliki bagian schema sendiri,
  • gateway bertugas untuk routing, composition, auth forwarding, observability, dan kadang policy lintas domain,
  • kontrak schema dikelola lebih terdistribusi,
  • tim domain dapat melakukan perubahan dengan otonomi yang lebih tinggi.

Pendekatan ini biasanya cocok ketika masalah utamanya bukan sekadar menggabungkan data, tetapi mengelola batas tanggung jawab antar banyak tim.

Kapan GraphQL BFF lebih cocok?

1. Satu tim memiliki sebagian besar kebutuhan schema

Jika satu tim mengontrol sebagian besar pengalaman pengguna dan integrasi backend, BFF biasanya lebih sederhana. Anda tidak perlu memecah ownership schema terlalu dini. Resolver dapat langsung memanggil service upstream yang relevan, dan proses review perubahan schema lebih singkat.

2. Kebutuhan client sangat spesifik

BFF efektif ketika kebutuhan mobile, web, dan admin sangat berbeda. Alih-alih memaksakan satu schema global yang generik, tiap BFF bisa mengekspresikan kebutuhan datanya dengan jelas. Ini mengurangi kompromi schema yang terlalu abstrak dan sulit dipakai.

3. Domain backend belum stabil

Bila service internal masih sering berubah, BFF dapat menjadi lapisan anti-korupsi. Ia menyerap perubahan kontrak upstream sehingga client tidak terlalu sering terkena dampak. Ini berguna pada fase awal produk atau saat migrasi dari monolith ke service terpisah.

4. Organisasi belum siap untuk ownership terdistribusi

Gateway dengan schema lintas domain terdengar rapi, tetapi memerlukan disiplin ownership, review schema, observability yang baik, dan proses deployment yang matang. Jika itu belum ada, BFF sering lebih realistis secara operasional.

Kapan Gateway atau Federation ringan lebih cocok?

1. Banyak domain dengan owner yang jelas

Jika ada tim terpisah untuk identity, catalog, order, billing, dan inventory, memusatkan semua resolver ke satu BFF akan cepat menjadi bottleneck. Gateway memberi cara untuk membagi tanggung jawab schema sesuai domain.

2. Schema harus berkembang tanpa satu tim menjadi pusat semua perubahan

Dalam organisasi yang lebih besar, maintainability sering turun karena semua field baru, policy baru, dan resolver baru menumpuk di satu repo BFF. Gateway membantu menghindari pola ini dengan mendorong tiap domain mengelola bagian schema dan logika resolusinya sendiri.

3. Dibutuhkan kontrak API yang lebih universal

Jika GraphQL menjadi entry point lintas produk, bukan hanya adapter untuk satu UI, maka schema global dengan ownership domain lebih masuk akal. Ini tidak berarti schema harus sepenuhnya generik, tetapi ia perlu lebih stabil dan konsisten di seluruh organisasi.

4. Kebutuhan observability lintas domain sudah dianggap prioritas

Gateway menambah kompleksitas, tetapi juga menyediakan satu titik untuk mengukur query plan, field-level cost, auth propagation, request tracing, dan error mapping. Pada skala tertentu, visibilitas ini lebih penting daripada kesederhanaan awal.

Trade-off teknis yang paling sering menentukan

Schema ownership

Ini sering menjadi pembeda utama.

  • BFF: ownership terpusat. Cocok jika perubahan schema didominasi satu tim dan kebutuhan client spesifik.
  • Gateway: ownership terdistribusi. Cocok jika domain punya lifecycle berbeda dan tim perlu otonomi.

Masalah umum pada BFF adalah schema menjadi terlalu besar dan semua keputusan naming, deprecation, dan field behavior bergantung pada satu tim. Masalah umum pada gateway adalah governance menjadi berat jika aturan schema tidak jelas.

Kompleksitas resolver

Pada BFF, resolver sering berubah menjadi tempat semua logika komposisi, fallback, transformasi data, deduplikasi request, auth context, dan mapping error. Awalnya nyaman, tetapi lama-kelamaan resolver menjadi padat dan sulit diuji.

Pada gateway, resolver domain bisa lebih dekat dengan sumber data. Namun kompleksitas pindah ke composition, entity resolution, query planning, dan koordinasi antar schema. Jadi kompleksitas tidak hilang; hanya dipindahkan ke tempat yang berbeda.

Latensi hop tambahan

Baik BFF maupun gateway menambah satu hop jaringan dari client ke lapisan GraphQL. Pada gateway, bisa ada hop tambahan lagi ke subgraph atau service lain. Ini penting jika satu query memicu banyak fan-out ke backend.

Yang perlu diperhatikan bukan hanya jumlah hop, tetapi:

  • apakah resolver menjalankan request secara serial atau paralel,
  • apakah terjadi masalah N+1 ke upstream service,
  • apakah query sering menyentuh banyak domain dalam satu request,
  • apakah timeout upstream dikelola dengan baik.

Latency budget yang kecil akan lebih cepat habis jika komposisi dilakukan sembarangan.

Caching

Caching di GraphQL selalu lebih rumit daripada REST karena respons bergantung pada bentuk query. Dalam BFF, Anda bisa lebih agresif membuat cache per use case karena pola query cenderung lebih terbatas. Dalam gateway, cache harus mempertimbangkan ownership data, invalidation lintas domain, dan kemungkinan field dengan TTL berbeda.

Pendekatan praktis yang umum:

  • cache di upstream service untuk data yang mahal diambil,
  • request-level batching dan deduplication di resolver,
  • persisted query untuk pola query populer,
  • hindari hanya mengandalkan full-response cache di lapisan GraphQL.

Observability

Tanpa tracing dan metrik yang baik, BFF maupun gateway mudah menjadi black box. Minimal, Anda perlu melihat:

  • durasi request total,
  • durasi per resolver atau per subgraph call,
  • error rate per field atau domain,
  • query yang paling mahal dan paling sering dipakai,
  • fan-out ke upstream service.

Gateway biasanya memberi alasan lebih kuat untuk investasi observability, karena sumber masalah bisa tersebar di banyak domain. Pada BFF, observability sering diabaikan sampai resolver mulai lambat.

Deployment dan blast radius

Satu BFF besar sering mudah dipahami pada awalnya, tetapi deployment-nya bisa berisiko jika semua perubahan schema dan resolver ada di satu artefak. Bug kecil dapat berdampak ke banyak use case sekaligus.

Gateway dengan subgraph terpisah dapat memperkecil blast radius perubahan domain tertentu, tetapi juga menambah risiko inkompatibilitas composition jika kontrak tidak divalidasi sebelum rilis.

Versioning kontrak

GraphQL umumnya mengandalkan evolusi schema melalui penambahan field baru dan deprecation, bukan versioning endpoint seperti /v1 dan /v2. Dalam BFF, deprecation relatif lebih mudah dikelola karena client lebih sedikit dan lebih dekat dengan tim pemilik. Dalam gateway, Anda memerlukan disiplin lebih tinggi:

  • field tidak boleh dihapus sebelum semua consumer aman,
  • perubahan nullability harus hati-hati,
  • rename hampir selalu butuh fase transisi,
  • breaking change perlu deteksi otomatis di CI.

Koordinasi tim

BFF mengurangi overhead koordinasi pada tim kecil, tetapi bisa menciptakan ketergantungan tinggi pada tim BFF. Gateway meningkatkan otonomi domain, namun menuntut kontrak kerja yang lebih jelas antar tim: siapa pemilik field, bagaimana review schema dilakukan, bagaimana perubahan lintas domain diuji, dan siapa yang menangani incident saat query lintas domain gagal.

Contoh perbedaan implementasi

Contoh berikut disederhanakan untuk menunjukkan letak kompleksitas, bukan sebagai implementasi produksi lengkap.

Contoh resolver pada BFF

const resolvers = {
  Query: {
    async dashboard(_, { userId }, { services, loaders }) {
      const user = await services.identity.getUser(userId);

      const [orders, recommendations] = await Promise.all([
        services.order.listRecentOrders(user.id),
        services.catalog.getRecommendations(user.segment)
      ]);

      return {
        user,
        orders,
        recommendations
      };
    }
  },
  Order: {
    async items(order, _, { loaders }) {
      return loaders.orderItems.load(order.id);
    }
  }
};

Dalam BFF, resolver seperti ini nyaman karena kebutuhan UI dirakit langsung. Namun jika pola serupa muncul di banyak query, BFF dapat berubah menjadi lapisan orkestrasi besar yang sulit dirawat.

Contoh sketsa schema domain pada gateway

# subgraph catalog
 type Product {
   id: ID!
   name: String!
   price: Money!
 }

 type Query {
   product(id: ID!): Product
 }

# subgraph order
 type Order {
   id: ID!
   productIds: [ID!]!
 }

 type Query {
   order(id: ID!): Order
 }

Pada gateway, tiap domain cenderung mengekspor bagian schema sesuai bounded context-nya. Kompleksitas berikutnya adalah bagaimana relasi antar domain dikomposisikan secara aman, bagaimana auth diteruskan, dan bagaimana query lintas domain diobservasi.

Matriks keputusan: tim kecil vs organisasi multi-domain

KriteriaGraphQL BFFGateway/Federation ringan
Ukuran timLebih cocok untuk tim kecil hingga menengahLebih cocok untuk banyak tim domain
Ownership schemaTerpusatTerdistribusi
Kecepatan iterasi untuk satu produkTinggiSedang, karena ada koordinasi composition
Kompleksitas operasional awalLebih rendahLebih tinggi
Risiko bottleneck organisasiTinggi jika semua perubahan lewat satu timLebih rendah jika governance matang
Kesesuaian untuk schema global lintas domainTerbatasLebih baik
Observability lintas domainPerlu dibangun manual dengan disiplinSering menjadi bagian penting dari desain
Latency pada query lintas banyak backendBisa baik jika fan-out terkendaliBisa meningkat jika query planning dan hop tidak efisien
Biaya koordinasi timRendah di awal, bisa tinggi saat skala tim naikTinggi di awal, lebih sehat untuk organisasi besar

Jika disederhanakan:

  • Pilih BFF bila masalah utama Anda adalah produktivitas satu tim aplikasi dan kebutuhan client yang spesifik.
  • Pilih gateway/federation ringan bila masalah utama Anda adalah pembagian ownership schema dan skala organisasi lintas domain.

Biaya operasional yang sering diremehkan

Pada BFF

  • satu service menjadi tempat akumulasi business logic presentasional,
  • test integrasi ke banyak upstream makin berat,
  • perubahan upstream sering memerlukan patch cepat di satu titik sentral,
  • tim BFF bisa menjadi bottleneck review dan deployment.

Pada Gateway

  • perlu validasi composition schema sebelum deploy,
  • tracing dan correlation ID harus konsisten antar service,
  • debugging error lintas subgraph lebih sulit tanpa tooling,
  • kontrak ownership field harus terdokumentasi dengan jelas.

Secara praktis, gateway hampir selalu menuntut investasi lebih besar pada platform engineering, CI, schema checks, dan observability.

Anti-pattern yang sering muncul

1. Mengadopsi federation terlalu cepat

Jika tim masih kecil dan semua perubahan dikerjakan oleh orang yang sama, federation bisa menjadi kompleksitas yang belum menghasilkan nilai. Anda membayar biaya koordinasi sebelum benar-benar membutuhkannya.

2. Satu BFF melayani terlalu banyak domain dan terlalu banyak client

Ini anti-pattern kebalikan. Nama BFF tetap dipakai, tetapi kenyataannya ia sudah menjadi pseudo-gateway tanpa ownership domain yang jelas. Hasilnya: schema besar, resolver sulit dipahami, dan semua keputusan menumpuk di satu repo.

3. Menaruh business logic inti hanya di resolver GraphQL

Resolver sebaiknya fokus pada komposisi, validasi permintaan, auth context, dan adaptasi kontrak. Jika aturan bisnis inti hanya hidup di resolver, reuse antar channel dan pengujian domain menjadi buruk. Logika inti lebih aman diletakkan di service layer/domain layer yang dapat dipakai ulang.

4. Mengabaikan N+1 dan fan-out sejak awal

Baik BFF maupun gateway bisa lambat bukan karena GraphQL-nya, tetapi karena resolver melakukan panggilan upstream berulang tanpa batching. Gunakan data loader, batch endpoint upstream bila memungkinkan, dan perhatikan pola query yang paling mahal.

5. Tidak punya kebijakan deprecation

Schema GraphQL yang terus bertambah tanpa deprecation policy akan sulit dipelihara. Field lama tetap hidup, dokumentasi tidak sinkron, dan consumer tidak pernah terdorong migrasi.

6. Observability hanya di level HTTP

Status 200 tidak berarti query sehat. Banyak error GraphQL muncul di payload sementara HTTP tetap sukses. Pantau error per field, partial failure, timeout upstream, dan field yang paling mahal.

Checklist evaluasi sebelum migrasi

  1. Siapa pemilik schema? Apakah satu tim masih realistis memegang sebagian besar perubahan, atau ownership sudah jelas per domain?
  2. Berapa banyak client dengan kebutuhan sangat berbeda? Jika perbedaan besar, beberapa BFF bisa lebih sehat daripada memaksakan satu schema global.
  3. Seberapa sering query menyentuh banyak domain? Jika sering, perhatikan biaya fan-out dan desain relasi antardomain.
  4. Apakah observability Anda cukup? Minimal harus ada tracing request lintas upstream, metrik resolver, dan logging terstruktur.
  5. Apakah upstream service stabil? BFF sering berguna sebagai lapisan adaptasi ketika kontrak internal belum matang.
  6. Apakah tim siap untuk governance schema? Gateway butuh aturan naming, review, ownership, deprecation, dan compatibility check.
  7. Bagaimana strategi deployment? Apakah perubahan schema bisa divalidasi otomatis sebelum rilis? Apakah ada rollback yang jelas?
  8. Apakah ada masalah bottleneck organisasi saat ini? Jika semua perubahan harus melewati satu tim dan itu sudah melambatkan delivery, BFF mungkin mulai tidak cukup.
  9. Bagaimana caching akan bekerja? Tentukan apakah cache lebih efektif di upstream, di resolver batch, atau melalui persisted query.
  10. Apakah keamanan dan auth context konsisten? Terutama jika request akan melewati gateway lalu beberapa domain service.

Panduan keputusan yang lebih praktis

Pilih BFF jika kondisi Anda mirip berikut

  • satu produk utama dengan satu atau dua client dominan,
  • tim masih kecil dan ingin iterasi cepat,
  • schema lebih berorientasi kebutuhan UI daripada kontrak organisasi,
  • backend internal belum cukup stabil dan butuh lapisan adaptasi,
  • biaya operasional tambahan harus ditekan.

Pilih Gateway/Federation ringan jika kondisi Anda mirip berikut

  • banyak tim domain sudah ada dan cukup mandiri,
  • schema dipakai lintas produk atau lintas channel,
  • perubahan terpusat mulai menjadi bottleneck,
  • butuh governance schema dan observability lintas domain yang kuat,
  • platform team mampu mendukung tooling composition, checks, dan tracing.

Kesimpulan

Tidak ada jawaban absolut dalam memilih GraphQL BFF vs Gateway. BFF biasanya unggul pada kesederhanaan awal, fokus pada kebutuhan client, dan kecepatan iterasi tim kecil. Gateway atau federation ringan unggul ketika ownership schema harus dibagi, organisasi mulai multi-domain, dan maintainability jangka panjang lebih ditentukan oleh batas tanggung jawab antar tim daripada oleh kemudahan implementasi awal.

Pilihan terbaik bergantung pada ukuran tim, pola query, stabilitas service upstream, dan seberapa penting schema berevolusi tanpa bottleneck sentral. Jika ragu, mulai dari solusi yang paling sederhana yang masih sesuai dengan struktur tim saat ini, lalu ukur kapan biaya koordinasi atau kompleksitas resolver mulai lebih mahal daripada manfaatnya.

Catatan praktis: banyak organisasi tidak langsung melompat dari satu BFF ke federation penuh. Langkah tengah yang sering lebih aman adalah merapikan service boundary, menambahkan observability, menerapkan schema checks, lalu memisahkan ownership secara bertahap ketika memang dibutuhkan.