Backend for Frontend (BFF) là một dịch vụ backend chuyên biệt được xây dựng cho một giao diện người dùng (frontend) cụ thể. Thay vì mọi client (web, iOS, Android, bên thứ ba) giao tiếp với cùng một backend đa năng, mỗi client sẽ có một lớp phía máy chủ riêng để tổng hợp và định hình lại dữ liệu từ các microservice của bạn thành chính xác tải trọng mà giao diện đó cần.
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 đã được thực hiện tại SoundCloud. Hơn một thập kỷ sau, mô hình BFF vẫn là một công cụ tiêu chuẩn cho các nhóm chạy microservice đằng sau nhiều ứng dụng client, và Microsoft đã tài liệu hóa nó như một mô hình kiến trúc đám mây cốt lõi.
nút
Vấn đề mà BFF giải quyết
Hãy hình dung một hệ thống bắt đầu với một ứng dụng web và một backend duy nhất. Backend cung cấp các endpoint REST, ứng dụng web tiêu thụ chúng, và mọi thứ đều đơn giản. Sau đó, công ty ra mắt một ứng dụng di động. Rồi một tích hợp đối tác. Rồi một tiện ích đồng hồ thông minh. Đột nhiên, bốn client rất khác nhau đều kéo dữ liệu từ cùng một backend, và backend đó đang cố gắng làm hài lòng tất cả mọi người cùng một lúc.
Điều này tạo ra hai vấn đề lặp đi lặp lại.
Over-fetching (lấy quá nhiều) và under-fetching (lấy quá ít). Một endpoint đa năng trả về một cấu trúc dữ liệu cố định. Một bảng điều khiển trên máy tính để bàn có thể muốn toàn bộ hồ sơ khách hàng với lịch sử đặt hàng, khuyến nghị và cài đặt tài khoản trong một phản hồi. Một ứng dụng di động trên kết nối di động không ổn định chỉ muốn ba trường và không hơn. Khi cả hai cùng truy cập một endpoint, một trong số chúng sẽ nhận được lượng dữ liệu không phù hợp. Client di động hoặc tải xuống một tải trọng cồng kềnh mà nó phải loại bỏ (over-fetching) hoặc phải thực hiện thêm vài vòng lặp để tập hợp những gì nó cần (under-fetching).
Client "nhiều lời". Khi một backend không được thiết kế riêng cho một màn hình, client sẽ bù đắp bằng cách thực hiện nhiều cuộc gọi. Một màn hình chính di động cần dữ liệu hồ sơ, số lượng thông báo và một feed có thể gửi ba hoặc bốn yêu cầu riêng biệt đến ba hoặc bốn microservice, sau đó kết hợp các kết quả trên thiết bị. Mỗi vòng lặp bổ sung làm tăng độ trễ và tiêu hao pin, và logic điều phối bị rò rỉ vào client, nơi khó kiểm tra và quản lý phiên bản.
Căng thẳng cốt lõi mang tính tổ chức cũng như kỹ thuật. Một backend dùng chung có những yêu cầu cạnh tranh từ mọi nhóm frontend. Một thay đổi của một nhóm phải được xác nhận dựa trên nhu cầu của mọi nhóm khác trước khi triển khai, điều này biến backend thành một nút thắt cổ chai và là nguồn gây ma sát giữa các nhóm.
Mô hình BFF hoạt động như thế nào
Mô hình BFF giới thiệu một lớp mỏng phía máy chủ nằm giữa một frontend và các dịch vụ downstream của bạn. Mỗi giao diện sẽ có backend riêng.
[ Ứng dụng Web ] ---> [ Web BFF ] ---\
[ Ứng dụng iOS ] ---> [ iOS BFF ] -----> [ Microservice ]
[ Ứng dụng Android] ---> [ Android BFF ] ---/
Mỗi BFF thực hiện ba nhiệm vụ cho client của nó:
- Tổng hợp (Aggregate). Nó gọi các microservice downstream mà màn hình cần và kết hợp các phản hồi của chúng, để client chỉ thực hiện một yêu cầu thay vì năm. Đây là sự tổng hợp API được áp dụng cho một trải nghiệm người dùng duy nhất. Nếu bạn muốn phiên bản tổng quát của ý tưởng đó, hãy xem giải thích của chúng tôi về mô hình API aggregator.
- Định hình lại (Reshape). Nó cắt bớt các trường, đổi tên thành các thuật ngữ thân thiện với client, làm phẳng các cấu trúc lồng nhau và định dạng giá trị theo cách mà giao diện mong đợi. BFF di động trả về các tải trọng gọn nhẹ; BFF máy tính để bàn trả về các tải trọng phong phú.
- Phiên dịch (Translate). Nó xử lý các vấn đề dành riêng cho client như chiến lược phân trang, lưu trữ phản hồi được điều chỉnh cho client đó và lựa chọn giao thức, mà không áp đặt những quyết định đó lên các dịch vụ dùng chung bên dưới.
Các microservice downstream vẫn giữ nguyên tính đa năng và không phụ thuộc vào frontend. Chúng cung cấp các khả năng sạch sẽ, có thể tái sử dụng. BFF là nơi các định hình dành riêng cho client tồn tại, giúp logic đó không nằm trong cả microservice và ứng dụng client. Nếu bạn mới làm quen với lớp dịch vụ bên dưới, tổng quan của chúng tôi về microservice so với API và sự chuyển đổi từ monolith sang microservice sẽ cung cấp bối cảnh.
Một BFF cho mỗi trải nghiệm client
Hướng dẫn cốt lõi của Newman rất ngắn gọn: một trải nghiệm, một BFF. Nếu ứng dụng iOS và Android của bạn cung cấp những trải nghiệm khác biệt rõ rệt, hãy cung cấp cho mỗi ứng dụng một BFF riêng. Nếu một ứng dụng web và một ứng dụng di động phân kỳ, quy tắc tương tự cũng được áp dụng.
Mục đích của quy tắc là giữ cho mỗi BFF tập trung. Ngay khi một BFF duy nhất cố gắng phục vụ hai client với các nhu cầu khác nhau, nó bắt đầu tích lũy logic có điều kiện ("nếu di động, trả về cái này; nếu web, trả về cái kia"), và bạn sẽ quay lại một backend đa năng với tất cả các vấn đề phối hợp tương tự. Một BFF tập trung sẽ giữ cho nó nhỏ gọn, đây là đặc tính làm cho toàn bộ mô hình phát huy hiệu quả.
Có một ngoại lệ hợp lý mà Newman tự mình rút ra từ SoundCloud: khi một nhóm sở hữu hai client tương tự, chẳng hạn như ứng dụng iOS và Android chia sẻ gần như cùng một trải nghiệm, việc chia sẻ một BFF di động duy nhất giữa chúng có thể hợp lý. Yếu tố quyết định là quyền sở hữu và sự tương đồng, chứ không phải tên nền tảng. Quy tắc này là một mặc định, không phải là một luật.
Quyền sở hữu thuộc về nhóm frontend
BFF không phải là một lớp mà nhóm nền tảng xây dựng và bàn giao. Nhóm frontend sở hữu client sẽ sở hữu BFF của nó. Đây là nửa thứ hai làm cho mô hình hoạt động.
Khi nhóm frontend sở hữu BFF, họ kiểm soát nhịp độ phát hành, chọn ngôn ngữ và môi trường chạy (runtime), ưu tiên backlog và cùng nhau triển khai các thay đổi cho client và dịch vụ hỗ trợ của nó. Một thay đổi giao diện người dùng cần một endpoint tổng hợp mới không yêu cầu tạo một yêu cầu hỗ trợ với một nhóm backend riêng biệt và chờ đợi nó được xử lý trong hàng đợi của nhóm đó. Nhóm cảm thấy khó khăn sẽ là nhóm khắc phục.
Quyền tự chủ này là chiến thắng thực sự. BFF di chuyển ranh giới để các quyết định dành riêng cho client được đưa ra bởi những người chịu trách nhiệm về client, đây chính xác là nơi tư duy kết nối dựa trên API đặt tầng "trải nghiệm".
BFF so với API Gateway
Đây là sự so sánh mà hầu hết các nhóm gặp khó khăn, bởi vì BFF và API gateway trông tương tự nhau trên sơ đồ. Cả hai đều nằm giữa client và các dịch vụ. Cả hai đều có thể định tuyến và tổng hợp. Nhưng chúng giải quyết các câu hỏi khác nhau.
Một API gateway là một điểm vào đa năng xử lý các vấn đề chung cho tất cả lưu lượng truy cập: xác thực, giới hạn tốc độ (rate limiting), định tuyến, chấm dứt TLS và ghi nhật ký yêu cầu. Nó thuộc sở hữu của một nhóm nền tảng hoặc hạ tầng và cố tình không phụ thuộc vào client. Một gateway phục vụ mọi người theo cùng một cách.
BFF thì ngược lại. Nó được thiết kế dành riêng cho client, thuộc sở hữu của một nhóm frontend, và toàn bộ mục đích của nó là khác biệt cho mỗi giao diện. Đó là nơi định hình tải trọng của một client tồn tại, không phải là một điểm nghẽn dùng chung.
Hai thứ này không phải là đối thủ. Trong một bố cục sản xuất phổ biến, một API gateway nằm ở phía trước, xử lý xác thực, giới hạn tốc độ và giám sát cho tất cả lưu lượng truy cập, sau đó định tuyến mỗi client đến BFF chuyên dụng của nó đằng sau gateway. Kiến trúc tham chiếu của Microsoft thể hiện chính xác điều này: một gateway quản lý các vấn đề chung, với một BFF serverless cho mỗi client đằng sau nó. Sử dụng gateway cho những gì giống nhau giữa các client, và một BFF cho những gì khác biệt. (Chúng tôi đề cập đến phiên bản sâu sắc của sự tương phản này trong một bài viết riêng; ở đây chỉ cần biết rằng chúng nằm ở các lớp khác nhau và giải quyết các nhu cầu khác nhau.)
Đối với bức tranh tổng thể về gateway, các so sánh đã xuất bản này sẽ hữu ích: 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
Mô hình này chứng tỏ giá trị của nó khi các điều kiện sau đây được đáp ứng:
- Bạn có nhiều client thực sự khác biệt. Web cộng với di động cộng với tích hợp đối tác, mỗi loại có nhu cầu dữ liệu riêng biệt. Trải nghiệm càng khác biệt, BFF càng hữu ích.
- Backend dùng chung đã trở thành nút thắt cổ chai. Nếu mọi thay đổi frontend đều buộc phải đàm phán giữa các nhóm, việc tách biệt định hình dành riêng cho client thành các BFF riêng cho từng nhóm sẽ loại bỏ chi phí phối hợp.
- Bạn muốn tải trọng tối ưu hóa cho client. Di động cần phản hồi gọn nhẹ và lưu trữ đệm mạnh mẽ; máy tính để bàn muốn dữ liệu tổng hợp phong phú. BFF cho phép bạn tối ưu hóa từng loại mà không cần thỏa hiệp.
- Một ngôn ngữ phù hợp hơn với một frontend. Một nhóm có thể xây dựng BFF của mình trong môi trường chạy (runtime) phù hợp với client của họ, độc lập với những gì các BFF khác sử dụng.
Khi nào không nên sử dụng BFF
Mô hình này không miễn phí, và có những trường hợp rõ ràng khi nó làm tăng chi phí mà không mang lại lợi ích:
- Bạn chỉ có một client. Với một giao diện duy nhất, BFF chỉ là một bước nhảy bổ sung. Hãy xây dựng một backend thông thường.
- Các client của bạn thực hiện các yêu cầu tương tự. Nếu web và di động muốn dữ liệu gần như giống hệt nhau với cùng một cấu trúc, các BFF riêng biệt sẽ làm trùng lặp công sức mà không mang lại lợi ích. Thay vào đó, hãy hợp nhất.
- GraphQL đã giải quyết vấn đề định hình của bạn. Với GraphQL, mỗi client truy vấn chính xác các trường mà nó cần từ một endpoint duy nhất, điều này bao gồm phần lớn những gì BFF làm để định hình tải trọng. Nếu bạn có một lớp GraphQL với các resolver dành riêng cho frontend, một tầng BFF riêng biệt thường không thêm giá trị. Hãy xem GraphQL là gì để đánh giá xem nó có phù hợp không trước khi thêm một tầng BFF.
- Một gateway cộng với microservice là đủ. Đối với các hệ thống đơn giản hơn, một API gateway đứng trước các microservice được thiết kế tốt có thể mang lại kết quả chấp nhận được mà không cần một tầng riêng biệt cho mỗi client.
Những nhược điểm thực tế
Ngay cả khi BFF là lựa chọn đúng đắn, bạn vẫn phải chịu những chi phí thực tế. Hiểu rõ điều này là một phần của việc sử dụng mô hình hiệu quả.
Trùng lặp mã. Đây là sự đánh đổi hàng đầu, và tài liệu của Microsoft đã chỉ ra điều đó một cách trực tiếp. Khi ba BFF đều cần gọi cùng một kiểm tra xác thực hoặc định dạng cùng một ngày theo cùng một cách, logic đó có xu hướng được viết ba lần. Bạn đang đánh đổi sự trùng lặp để có sự tùy chỉnh. Cách khắc phục là kỷ luật: giữ logic thực sự dùng chung trong các thư viện mà các BFF import, và dành bản thân BFF cho việc định hình dành riêng cho client. Đẩy các vấn đề chung thực sự (xác thực, giới hạn tốc độ, giám sát) lên gateway thay vì triển khai lại chúng cho mỗi BFF.
Nhiều dịch vụ hơn để vận hành. Mỗi BFF là một đơn vị có thể triển khai khác với vòng đời, pipeline, lịch trực và bề mặt bảo mật riêng. Nhiều dịch vụ hơn có nghĩa là nhiều chi phí vận hành hơn.
Một bước nhảy mạng bổ sung. Các client không còn giao tiếp trực tiếp với các dịch vụ. BFF thêm một bước nhảy, và điều đó có thể làm tăng độ trễ. Điều này thường là một sự đánh đổi đáng giá vì BFF loại bỏ một số vòng lặp client-to-service, nhưng đó là một chi phí cần đo lường, không phải giả định.
Nguy cơ BFF trở nên cồng kềnh. Nếu một BFF bắt đầu phục vụ nhiều client hoặc hấp thụ logic nghiệp vụ thuộc về các microservice, nó sẽ quay trở lại backend đa năng mà bạn đang cố gắng thoát khỏi. Hãy giữ cho nó mỏng gọn.
Giữ cho các hợp đồng BFF đồng bộ với Apidog
Phần khó khăn của việc chạy BFF trong thực tế là các hợp đồng. Mỗi BFF đều cung cấp API dành cho client của riêng nó, và nó cũng phụ thuộc vào các hợp đồng của các microservice bên dưới. Đó là một lượng lớn các giao diện di chuyển giữa các nhóm sở hữu các lớp khác nhau, và sự sai lệch giữa chúng là nơi phát sinh lỗi và client bị hỏng.

Đây là nơi Apidog phù hợp với quy trình làm việc. Apidog là một nền tảng thiết kế, kiểm thử, giả lập và tài liệu API, vì vậy hợp đồng API của mỗi BFF có một ngôi nhà duy nhất mà cả nhóm frontend và backend đều có thể làm việc cùng:
- Thiết kế hợp đồng trước tiên. Định nghĩa từng endpoint BFF và schema yêu cầu và phản hồi của nó trong trình thiết kế trực quan của Apidog với OpenAPI bên dưới, để cấu trúc dành cho client được thống nhất trước khi viết mã. Đây là phương pháp thiết kế API dựa trên hợp đồng được áp dụng cho lớp BFF, và nó giữ cho hợp đồng API rõ ràng.
- Giả lập nó trước khi nó tồn tại. Nhóm frontend có thể bắt đầu xây dựng dựa trên bản giả lập thông minh của BFF từ Apidog ngay khi hợp đồng được thống nhất, mà không cần chờ BFF hoặc các dịch vụ downstream của nó sẵn sàng.
- Kiểm thử hợp đồng. Các bài kiểm thử và xác nhận tự động của Apidog xác minh rằng mỗi BFF trả về tải trọng tổng hợp, được định hình lại mà client của nó mong đợi, và chúng phù hợp với CI để một thay đổi downstream làm hỏng phản hồi của BFF sẽ được phát hiện sớm.
- Tài liệu cho cả hai bên. Apidog tự động tạo tài liệu tương tác từ hợp đồng, vì vậy nhóm frontend đọc API của BFF và nhóm backend sở hữu các dịch vụ bên dưới chia sẻ một nguồn thông tin đáng tin cậy duy nhất.
Để làm rõ về phạm vi: Apidog không xây dựng, lưu trữ hoặc chạy BFF của bạn, và nó không phải là một API gateway. Đó là nơi bạn thiết kế, giả lập, kiểm thử và tài liệu hóa hợp đồng API mà mỗi BFF đứng sau, điều này giúp các nhóm frontend và backend đồng bộ khi các BFF phát triển. Việc coi mỗi BFF như một sản phẩm với một hợp đồng ổn định, được tài liệu hóa tốt là điều làm cho 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 dịch vụ phía máy chủ, và trong một thiết lập microservice, nó thường chạy như một microservice. Nhưng công việc của nó khác với một microservice điển hình. Một microservice sở hữu một khả năng nghiệp vụ và không phụ thuộc vào client; một BFF sở hữu trải nghiệm của một client và tồn tại để tổng hợp và định hình lại các microservice đó cho client đó. Nó là một dịch vụ lớp trải nghiệm, không phải là một dịch vụ khả năng nghiệp vụ.
Tôi nên có bao nhiêu BFF? Mặc định là một BFF cho mỗi trải nghiệm client riêng biệt: một cho web, một cho iOS, một cho Android, v.v. Chỉ kết hợp hai BFF khi một nhóm duy nhất sở hữu các client có nhu cầu gần như giống hệt nhau. Tách biệt thêm khi một BFF bắt đầu tích lũy logic có điều kiện dành riêng cho từng client.
GraphQL có thay thế mô hình BFF không? Có thể, đối với phần định hình tải trọng. GraphQL cho phép mỗi client yêu cầu chính xác các trường nó cần từ một endpoint, điều này giải quyết vấn đề over-fetching và under-fetching mà không cần một backend riêng cho từng client. Nếu bạn có GraphQL với các resolver dành riêng cho frontend, một tầng BFF riêng biệt thường không thêm nhiều giá trị. BFF vẫn hữu ích khi bạn cần điều phối dành riêng cho client, dịch giao thức hoặc lựa chọn môi trường chạy mà một máy chủ GraphQL dùng chung không thể dễ dàng cung cấp.
Tôi có thể sử dụng BFF và API gateway cùng nhau không? Có, và điều này phổ biến. API gateway xử lý các vấn đề chung cho tất cả client, chẳng hạn như xác thực, giới hạn tốc độ và giám sát, và định tuyến lưu lượng truy cập đến đúng BFF. Mỗi BFF xử lý những gì dành riêng cho client của nó. Chúng nằm ở các lớp khác nhau và thực hiện các công việc khác nhau.
Ai nên sở hữu BFF? Nhóm frontend sở hữu client. Quyền sở hữu đó là trung tâm của mô hình. Nó cho phép nhóm triển khai các thay đổi giao diện người dùng và các endpoint hỗ trợ cùng nhau, chọn môi trường chạy riêng và di chuyển mà không cần chờ đợi hàng đợi của một nhóm backend riêng biệt.
BFF có làm tăng độ trễ không? Nó thêm một bước nhảy mạng, điều này có chi phí. Trên thực tế, nó thường làm giảm tổng độ trễ của client, bởi vì nó thay thế một số vòng lặp client-to-service bằng một yêu cầu client-to-BFF và cho phép BFF gọi các dịch vụ song song gần chúng. Hãy đo lường nó cho khối lượng công việc của bạn thay vì giả định theo bất kỳ cách nào.
