DEV Community

Cover image for Backend for Frontend (BFF) là gì?
Sebastian Petrus
Sebastian Petrus

Posted on • Originally published at apidog.com

Backend for Frontend (BFF) là gì?

Backend for Frontend (BFF) là một dịch vụ backend chuyên biệt cho một frontend cụ thể. Thay vì web, iOS, Android và tích hợp bên thứ ba cùng gọi một backend đa năng, mỗi client có một lớp server riêng để tổng hợp, lọc và định hình dữ liệu từ microservice thành đúng payload mà giao diện đó cần.

Dùng thử Apidog ngay hôm nay

Sam Newman đặt tên và phổ biến mô hình này vào năm 2015, dựa trên công việc tại SoundCloud. Đến nay, BFF vẫn là một mô hình phổ biến cho các nhóm chạy microservice phía sau nhiều ứng dụng client, và Microsoft cũng tài liệu hóa nó như một mô hình kiến trúc đám mây cốt lõi.

Vấn đề mà BFF giải quyết

Giả sử hệ thống ban đầu chỉ có một web app và một backend REST. Sau đó bạn thêm mobile app, tích hợp đối tác, rồi smartwatch app. Tất cả client bắt đầu gọi cùng một backend, trong khi mỗi client cần dữ liệu khác nhau.

Hai vấn đề thường xuất hiện:

1. Over-fetching và under-fetching

Một endpoint đa năng thường trả về một cấu trúc cố định.

  • Web dashboard cần hồ sơ khách hàng đầy đủ, lịch sử đơn hàng, gợi ý và cài đặt tài khoản.
  • Mobile app trên mạng yếu chỉ cần name, avatarUrl, unreadCount.

Nếu cả hai dùng cùng endpoint, mobile phải tải quá nhiều dữ liệu hoặc phải gọi thêm nhiều endpoint khác để ghép dữ liệu.

2. Client gọi quá nhiều request

Một màn hình home trên mobile có thể cần:

  • thông tin user
  • số thông báo chưa đọc
  • feed mới nhất
  • trạng thái subscription

Nếu client tự gọi 3–4 microservice rồi ghép dữ liệu trên thiết bị, bạn sẽ gặp:

  • latency cao hơn
  • tốn pin hơn
  • logic orchestration nằm trong app
  • khó test và versioning hơn

BFF chuyển phần orchestration này lên server, nơi dễ kiểm thử, quan sát và triển khai hơn.

Mô hình BFF hoạt động như thế nào

BFF là một lớp server mỏng nằm giữa frontend và các service phía dưới.

[ Ứng dụng Web ]     ---> [ Web BFF ]     ---\
[ Ứng dụng iOS ]     ---> [ iOS BFF ]     -----> [ Microservice ]
[ Ứng dụng Android ] ---> [ Android BFF ] ---/
Enter fullscreen mode Exit fullscreen mode

Mỗi BFF thường làm ba việc chính.

1. Aggregate: tổng hợp dữ liệu

BFF gọi nhiều service downstream và trả về một response duy nhất cho client.

Ví dụ mobile app chỉ gọi:

GET /mobile/home
Enter fullscreen mode Exit fullscreen mode

Thay vì tự gọi:

GET /users/me
GET /notifications/count
GET /feed
GET /subscriptions/current
Enter fullscreen mode Exit fullscreen mode

Ví dụ đơn giản bằng Node.js/Express:

app.get("/mobile/home", async (req, res, next) => {
  try {
    const userId = req.user.id;

    const [profile, notifications, feed] = await Promise.all([
      userService.getProfile(userId),
      notificationService.getUnreadCount(userId),
      feedService.getLatest(userId),
    ]);

    res.json({
      user: {
        name: profile.name,
        avatarUrl: profile.avatarUrl,
      },
      unreadCount: notifications.count,
      feed: feed.items.slice(0, 10),
    });
  } catch (err) {
    next(err);
  }
});
Enter fullscreen mode Exit fullscreen mode

Đây là API aggregation áp dụng cho một trải nghiệm người dùng cụ thể. Nếu bạn muốn hiểu phiên bản tổng quát hơn, xem bài về mô hình API aggregator.

2. Reshape: định hình lại payload

BFF cắt bớt field, đổi tên field, làm phẳng object lồng nhau và định dạng dữ liệu theo cách client cần.

Ví dụ service trả về:

{
  "id": "u_123",
  "first_name": "An",
  "last_name": "Nguyen",
  "profile": {
    "avatar": {
      "url": "https://cdn.example.com/a.png"
    }
  },
  "internal_flags": {
    "risk_score": 12
  }
}
Enter fullscreen mode Exit fullscreen mode

Mobile BFF có thể trả về:

{
  "id": "u_123",
  "displayName": "An Nguyen",
  "avatarUrl": "https://cdn.example.com/a.png"
}
Enter fullscreen mode Exit fullscreen mode

Client nhận đúng dữ liệu cần dùng, không phải biết cấu trúc nội bộ của service.

3. Translate: phiên dịch theo nhu cầu client

BFF cũng có thể xử lý các khác biệt theo client như:

  • phân trang kiểu cursor cho mobile
  • cache response ngắn hạn cho màn hình home
  • chuyển đổi protocol
  • chuẩn hóa error message cho UI
  • gom nhiều error downstream thành một response dễ xử lý

Các microservice phía dưới vẫn giữ tính đa năng và không phụ thuộc frontend. BFF là nơi đặt logic định hình dành riêng cho client. Nếu bạn mới làm quen với tầng service bên dưới, xem thêm microservice so với API và quá trình từ monolith sang microservice.

Một BFF cho mỗi trải nghiệm client

Nguyên tắc ngắn gọn của Newman là: một trải nghiệm, một BFF.

Nếu iOS và Android có trải nghiệm khác nhau rõ rệt, hãy tách BFF. Nếu web và mobile cần payload khác nhau, cũng nên tách.

Web UI       -> Web BFF
iOS App      -> iOS BFF
Android App  -> Android BFF
Partner API  -> Partner BFF
Enter fullscreen mode Exit fullscreen mode

Mục tiêu là giữ mỗi BFF nhỏ và tập trung. Khi một BFF bắt đầu chứa nhiều nhánh như:

if (client === "mobile") {
  // return compact payload
} else if (client === "web") {
  // return full payload
}
Enter fullscreen mode Exit fullscreen mode

nó đang quay lại thành backend đa năng, đúng vấn đề mà BFF muốn tránh.

Có ngoại lệ hợp lý: nếu cùng một nhóm sở hữu iOS và Android, và hai app có trải nghiệm gần như giống nhau, một Mobile BFF dùng chung có thể đủ. Tiêu chí quyết định là quyền sở hữu và mức độ giống nhau của trải nghiệm, không chỉ tên nền tảng.

Quyền sở hữu thuộc về nhóm frontend

BFF nên thuộc về nhóm sở hữu frontend tương ứng.

Điều này có nghĩa:

  • nhóm web sở hữu Web BFF
  • nhóm mobile sở hữu Mobile BFF
  • nhóm partner integration sở hữu Partner BFF

Lợi ích thực tế:

  • frontend và BFF có thể release cùng nhịp
  • nhóm frontend tự ưu tiên backlog API cho UI
  • không phải chờ một nhóm backend chung thêm endpoint
  • logic phục vụ UI nằm gần nhóm hiểu UI nhất

BFF di chuyển ranh giới quyết định dành riêng cho client về đúng nhóm chịu trách nhiệm cho client đó. Đây cũng là cách tầng “experience” trong kết nối dựa trên API thường được tổ chức.

BFF so với API Gateway

BFF và API gateway đều nằm giữa client và service, nên dễ bị nhầm lẫn. Nhưng chúng giải quyết hai vấn đề khác nhau.

API gateway là điểm vào chung cho toàn bộ traffic. Nó thường xử lý:

  • authentication
  • rate limiting
  • routing
  • TLS termination
  • logging
  • monitoring

Gateway nên trung lập với client.

BFF thì ngược lại. Nó được thiết kế riêng cho một client hoặc một trải nghiệm. Nó xử lý payload shape, orchestration và nhu cầu UI cụ thể.

Một bố cục phổ biến trong production:

Client
  |
  v
API Gateway
  |
  +--> Web BFF
  +--> Mobile BFF
  +--> Partner BFF
        |
        v
   Microservices
Enter fullscreen mode Exit fullscreen mode

Dùng gateway cho phần giống nhau giữa mọi client. Dùng BFF cho phần khác nhau giữa các client.

Để hiểu thêm về gateway, xem các so sánh: API management so với API gateway, API gateway so với bộ cân bằng tải, và service mesh so với API gateway.

Khi nào nên sử dụng BFF

BFF phù hợp khi bạn có một hoặc nhiều điều kiện sau.

Bạn có nhiều client thực sự khác nhau

Ví dụ:

  • web dashboard cần dữ liệu đầy đủ
  • mobile app cần response nhẹ
  • partner API cần schema ổn định và khác với UI
  • smartwatch chỉ cần vài field

Trải nghiệm càng khác nhau, BFF càng hữu ích.

Backend chung đã thành nút thắt cổ chai

Nếu mọi thay đổi UI đều cần thương lượng với nhiều nhóm backend, BFF giúp tách phần định hình dữ liệu theo client ra khỏi backend chung.

Bạn cần payload tối ưu cho từng client

Mobile có thể cần response nhỏ, cache tốt và ít round-trip. Web có thể cần payload phong phú hơn. BFF cho phép tối ưu từng loại mà không buộc service phía dưới phục vụ mọi trường hợp.

Một runtime phù hợp hơn với frontend

Một nhóm có thể viết BFF bằng runtime quen thuộc với họ, miễn là vẫn tuân thủ tiêu chuẩn vận hành chung của tổ chức.

Khi nào không nên sử dụng BFF

BFF không miễn phí. Đừng thêm BFF nếu nó chỉ làm kiến trúc phức tạp hơn.

Bạn chỉ có một client

Nếu hệ thống chỉ có một frontend, BFF thường chỉ là một hop thừa. Một backend thông thường có thể đủ.

Các client dùng dữ liệu gần như giống nhau

Nếu web và mobile cần cùng schema, cùng field, cùng behavior, tách BFF có thể chỉ tạo trùng lặp.

GraphQL đã giải quyết vấn đề payload shaping

GraphQL cho phép client query đúng field cần dùng từ một endpoint. Điều này có thể giải quyết over-fetching và under-fetching mà không cần BFF riêng.

Nếu bạn đã có GraphQL server với resolver phù hợp cho frontend, một tầng BFF riêng có thể không thêm nhiều giá trị. Xem thêm GraphQL là gì trước khi thêm BFF.

Gateway cộng microservice đã đủ

Với hệ thống đơn giản, API gateway phía trước các microservice được thiết kế tốt có thể đáp ứng nhu cầu mà không cần một tầng riêng cho từng client.

Những nhược điểm thực tế

Ngay cả khi BFF là lựa chọn đúng, bạn vẫn cần quản lý các chi phí sau.

Trùng lặp mã

Nhiều BFF có thể cùng cần:

  • xác thực token
  • format ngày giờ
  • mapping lỗi
  • gọi cùng một service downstream

Cách xử lý:

  • đưa logic dùng chung vào library nội bộ
  • để gateway xử lý việc chung như authentication, rate limiting, monitoring
  • giữ BFF tập trung vào logic dành riêng cho client

Không nên biến BFF thành nơi chứa business logic cốt lõi.

Nhiều service hơn để vận hành

Mỗi BFF có:

  • pipeline riêng
  • deployment riêng
  • monitoring riêng
  • quyền truy cập riêng
  • surface bảo mật riêng

Trước khi thêm BFF, hãy chắc rằng nhóm có khả năng vận hành nó.

Thêm một network hop

Client không gọi thẳng microservice nữa mà gọi BFF trước. Điều này thêm một hop mạng.

Tuy nhiên, trong nhiều trường hợp BFF vẫn giảm tổng latency vì thay thế nhiều request client-to-service bằng một request client-to-BFF, sau đó BFF gọi các service song song.

Hãy đo thay vì giả định.

BFF có thể trở nên cồng kềnh

BFF không nên:

  • phục vụ quá nhiều client khác nhau
  • chứa nhiều nhánh if client_type
  • sở hữu business capability
  • thay thế microservice

Nếu BFF phình to, hãy tách lại theo trải nghiệm hoặc đẩy logic nghiệp vụ về service phù hợp.

Cách triển khai BFF theo hướng thực tế

Một quy trình triển khai BFF an toàn có thể đi theo các bước sau.

Bước 1: Chọn một màn hình hoặc flow cụ thể

Đừng bắt đầu bằng “xây Mobile BFF cho toàn app”. Hãy chọn một use case rõ ràng, ví dụ:

Mobile Home Screen
Enter fullscreen mode Exit fullscreen mode

Liệt kê dữ liệu màn hình cần:

- user display name
- avatar URL
- unread notification count
- latest 10 feed items
Enter fullscreen mode Exit fullscreen mode

Bước 2: Xác định các service downstream

Ví dụ:

User Service          -> profile
Notification Service  -> unread count
Feed Service          -> latest feed
Enter fullscreen mode Exit fullscreen mode

Bước 3: Thiết kế contract của BFF

Ví dụ endpoint:

GET /mobile/home
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "user": {
    "displayName": "An Nguyen",
    "avatarUrl": "https://cdn.example.com/a.png"
  },
  "unreadCount": 3,
  "feed": [
    {
      "id": "post_1",
      "title": "Hello BFF",
      "publishedAt": "2026-07-01T10:00:00Z"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Bước 4: Implement orchestration

Ví dụ với Express:

app.get("/mobile/home", authenticate, async (req, res, next) => {
  try {
    const userId = req.user.id;

    const [profile, unread, feed] = await Promise.all([
      userService.getProfile(userId),
      notificationService.getUnreadCount(userId),
      feedService.getLatest(userId, { limit: 10 }),
    ]);

    res.json({
      user: {
        displayName: `${profile.firstName} ${profile.lastName}`,
        avatarUrl: profile.avatarUrl,
      },
      unreadCount: unread.count,
      feed: feed.items.map((item) => ({
        id: item.id,
        title: item.title,
        publishedAt: item.publishedAt,
      })),
    });
  } catch (error) {
    next(error);
  }
});
Enter fullscreen mode Exit fullscreen mode

Bước 5: Thêm timeout và fallback

BFF không nên treo toàn bộ màn hình chỉ vì một service chậm. Ví dụ feed có thể fallback về mảng rỗng, nhưng profile thì bắt buộc.

const feed = await feedService
  .getLatest(userId, { limit: 10, timeoutMs: 500 })
  .catch(() => ({ items: [] }));
Enter fullscreen mode Exit fullscreen mode

Bước 6: Test contract

Kiểm tra ít nhất:

  • response đúng schema
  • không leak field nội bộ
  • downstream error được map đúng
  • timeout không làm hỏng toàn bộ response nếu field đó optional
  • versioning rõ ràng khi payload thay đổi

Giữ cho các hợp đồng BFF đồng bộ với Apidog

Phần khó khi chạy BFF trong thực tế là contract. Mỗi BFF cung cấp API riêng cho client của nó và đồng thời phụ thuộc vào contract của các microservice bên dưới. Khi các contract này lệch nhau, lỗi thường xuất hiện ở client.

Đây là nơi Apidog phù hợp với quy trình làm việc. Apidog là nền tảng thiết kế, kiểm thử, giả lập và tài liệu API, giúp contract của từng BFF có một nơi chung để frontend và backend cùng làm việc.

Bạn có thể áp dụng theo cách sau:

1. Thiết kế contract trước

Định nghĩa endpoint, request schema và response schema của BFF trước khi viết code. Apidog dùng OpenAPI bên dưới, phù hợp với cách làm thiết kế API dựa trên hợp đồng và giúp hợp đồng API rõ ràng.

2. Mock BFF trước khi service sẵn sàng

Frontend có thể bắt đầu tích hợp với mock API sau khi contract được thống nhất, ngay cả khi BFF hoặc service downstream chưa hoàn tất.

3. Kiểm thử contract

Dùng test và validation để đảm bảo BFF trả về đúng payload mà client mong đợi. Điều này đặc biệt hữu ích trong CI, khi một thay đổi downstream có thể làm response của BFF sai schema.

4. Tạo tài liệu cho cả hai phía

Tài liệu tương tác giúp:

  • frontend biết cách gọi BFF
  • backend biết BFF đang phụ thuộc vào contract nào
  • các nhóm có cùng một nguồn thông tin đáng tin cậy

Lưu ý phạm vi: Apidog không xây dựng, lưu trữ hoặc chạy BFF của bạn, và cũng không phải API gateway. Nó là nơi thiết kế, mock, test và tài liệu hóa contract API phía sau mỗi BFF. Xem mỗi BFF như một sản phẩm với contract ổn định là cách giúp mô hình này bền vững.

Câu hỏi thường gặp

BFF có phải là microservice không?

BFF là một service phía server và trong kiến trúc microservice, nó thường được triển khai như một microservice. Nhưng vai trò của nó khác microservice nghiệp vụ.

  • Microservice sở hữu một capability nghiệp vụ.
  • BFF sở hữu trải nghiệm của một client.

BFF là service ở tầng experience, không phải service nghiệp vụ cốt lõi.

Tôi nên có bao nhiêu BFF?

Mặc định: một BFF cho mỗi trải nghiệm client riêng biệt.

Ví dụ:

Web BFF
iOS BFF
Android BFF
Partner BFF
Enter fullscreen mode Exit fullscreen mode

Chỉ gộp BFF khi cùng một nhóm sở hữu các client có nhu cầu gần như giống nhau. Tách ra khi một BFF bắt đầu có nhiều logic điều kiện theo client.

GraphQL có thay thế BFF không?

Có thể, nếu vấn đề chính của bạn là payload shaping. GraphQL giúp client lấy đúng field cần dùng từ một endpoint.

BFF vẫn hữu ích nếu bạn cần:

  • orchestration riêng cho client
  • translation protocol
  • runtime riêng
  • caching strategy riêng
  • release cycle gắn với một frontend cụ thể

Có thể dùng BFF và API gateway cùng nhau không?

Có. Đây là cách rất phổ biến.

API gateway xử lý phần chung:

  • authentication
  • rate limiting
  • monitoring
  • routing

BFF xử lý phần riêng của từng client:

  • aggregate
  • reshape
  • translate
  • chuẩn hóa response cho UI

Ai nên sở hữu BFF?

Nhóm frontend sở hữu client nên sở hữu BFF tương ứng. Đây là điểm cốt lõi của mô hình, vì nó cho phép nhóm thay đổi UI và endpoint hỗ trợ cùng nhau.

BFF có làm tăng độ trễ không?

BFF thêm một network hop, nên có chi phí. Nhưng nó cũng có thể giảm tổng latency vì client gọi ít request hơn và BFF có thể gọi service downstream song song.

Cách đúng là đo:

  • latency trước và sau khi thêm BFF
  • số request từ client
  • payload size
  • error rate
  • thời gian render màn hình

Nếu BFF giảm round-trip và payload đủ nhiều, chi phí thêm một hop thường là đánh đổi hợp lý.

Top comments (0)