Backends for Frontends Pattern

Mẫu Backend cho Giao diện Trước

Trong bài viết này, chúng tôi sẽ mô tả cách bạn có thể cải thiện trải nghiệm của khách hàng cuối trên Giao diện Người dùng (UI) của bạn bằng cách triển khai mẫu Backend cho Giao diện Trước và cung cấp cập nhật trực tiếp theo thời gian thực khi các dịch vụ micro của bạn tạo ra các sự kiện về các thay đổi trong các tập hợp lĩnh vực của họ.

Giải pháp được đề xuất kết hợp hai mẫu: 1) mẫu Backend cho Giao diện Trước (BFF), trong đó các ứng dụng có một máy chủ phía sau cho mỗi trải nghiệm của người dùng, thay vì chỉ có một máy chủ API phổ biến; và 2) mẫu Publisher-Subscriber (pub/sub), trong đó các dịch vụ micro thông báo sự kiện cho nhiều người tiêu dùng quan tâm một cách không đồng bộ, mà không kết nối người gửi với người nhận. Khi kết hợp, hai mẫu này cho phép các ứng dụng giao diện trước tải các dự đoán dữ liệu sẵn sàng cho giao diện người dùng và làm mới giao diện bằng thông báo dựa trên sự kiện, dẫn đến trải nghiệm gần thời gian thực với hiệu suất cao cho người dùng cuối.

Để giải thích giải pháp cho các nhà phát triển API REST và GraphQL, chúng tôi cung cấp hai biểu đồ kiến trúc tương tự giải quyết mỗi công nghệ API.

Mẫu BFF

Theo Sam Newman, mẫu Backend cho Giao diện Trước (BFF) đề cập đến việc có một máy chủ phía sau cho mỗi trải nghiệm của người dùng, thay vì chỉ có một máy chủ API phổ biến.

Truyền thống, cách tiếp nhận hơn một loại giao diện là cung cấp một API phía máy chủ duy nhất và thêm thêm chức năng khi cần thiết để hỗ trợ các loại tương tác di động mới. Cách tiếp cận này có thể dẫn đến các thách thức sau:

  1. Các thiết bị di động thực hiện ít cuộc gọi hơn và muốn hiển thị dữ liệu khác (và có thể ít hơn) so với các đối tác máy tính để bàn – điều này có nghĩa là máy chủ API cần chức năng bổ sung để hỗ trợ giao diện di động.
  2. Giao diện người dùng ứng dụng hiện đại ngày càng áp dụng chiến lược phản ứng để cung cấp phản hồi theo thời gian thực cho người dùng cuối (ví dụ: thông qua WebSockets), và các thiết bị khác nhau có thể triển khai các ngăn xếp công nghệ khác nhau để hỗ trợ nó.
  3. Máy chủ API theo định nghĩa đang cung cấp chức năng cho nhiều ứng dụng dành cho người dùng – điều này có nghĩa là máy chủ API duy nhất có thể trở thành một chướng ngại khi triển khai giao hàng mới, do số lượng thay đổi được thực hiện đối với cùng một đối tượng triển khai.

Để giải quyết các thách thức này, Sam đề xuất bạn nên xem xét ứng dụng dành cho người dùng từ hai thành phần: một ứng dụng phía máy khách sống bên ngoài phạm vi của bạn và một thành phần phía máy chủ (BFF) bên trong phạm vi của bạn. Theo ông, BFF liên kết chặt chẽ với một trải nghiệm cụ thể của người dùng và thường được duy trì bởi cùng một nhóm làm giao diện người dùng, từ đó làm cho việc định nghĩa và thích nghi API dễ dàng hơn theo yêu cầu của giao diện người dùng, đồng thời đơn giản hóa quá trình sắp xếp phiên bản của cả phần máy khách và máy chủ.

Theo đề xuất của Sam, mẫu BFF đã được các công ty như Netflix áp dụng, nơi đội Android của họ đã mượn máy chủ API của ứng dụng Android Netflix một cách dễ dàng, cho phép họ làm việc với đối tượng cuối với mức độ kiểm tra, theo dõi và tích hợp cao hơn với hệ sinh thái dịch vụ micro của Netflix.

Các thành phần của một BFF sử dụng kiến trúc dựa trên sự kiện trên AWS

Giả định rằng bạn đã có một kiến trúc dựa trên sự kiện sẵn sàng, bất kể sự kiện được tạo ra bởi nhiều dịch vụ micro hoặc bởi một monolith duy nhất, thì bạn đã có đủ điều kiện để bắt đầu xây dựng các Backends-for-Frontends (BFFs) dựa trên sự kiện được phân tách cho mỗi trải nghiệm người dùng cuối của bạn.

Biểu đồ dưới đây trình bày một cái nhìn tổng quan về kiến trúc và luồng thông điệp của nó. Đại diện cho các nhà xuất bản lĩnh vực ở bên phải, mỗi nhà xuất bản có cơ sở dữ liệu tổng hợp cụ thể cho lĩnh vực của họ; và các người đăng ký BFF ở bên trái, mỗi người có cơ sở dữ liệu tổng hợp cụ thể cho trải nghiệm người dùng của họ. Ở giữa, có một bus sự kiện truyền thông trạng thái lĩnh vực, cho phép nhà xuất bản và người đăng ký duy trì sự tách rời.

Hình 1. Biểu đồ luồng thông điệp.

Các giải pháp BFF dựa trên sự kiện được mô tả trong bài viết blog này dựa trên một API cụ thể về công nghệ, bên cạnh các thành phần chung sau đây:

  • Một cơ sở dữ liệu NoSQL để lưu trữ các bảng tổng hợp lĩnh vực, cũng hỗ trợ Change Data Capture (CDC). Ở đây, chúng tôi sử dụng Amazon DynamoDB – dịch vụ cơ sở dữ liệu NoSQL nhanh chóng và linh hoạt để đạt hiệu suất trong vài mili giây đơn chữ số ở bất kỳ quy mô nào.
  • Một lớp tính toán để xử lý các yêu cầu và tích hợp luồng CDC với API. Ở đây, chúng tôi sử dụng AWS Lambda – dịch vụ tính toán không máy chủ, dựa trên sự kiện, cho phép bạn chạy mã mà không cần nghĩ về máy chủ hoặc nhóm máy chủ.
  • Một cơ chế xác thực và phân quyền để bảo vệ API. Ở đây, chúng tôi sử dụng Amazon Cognito – một dịch vụ đơn giản và an toàn cho việc đăng ký người dùng, đăng nhập và kiểm soát quyền truy cập.

Xây dựng một BFF REST dựa trên sự kiện sử dụng API Gateway

Amazon API Gateway là dịch vụ được quản lý đầy đủ, giúp cho các nhà phát triển dễ dàng tạo, xuất bản, duy trì, theo dõi và bảo mật các API ở bất kỳ quy mô nào. Các API hoạt động như “cửa trước” cho các ứng dụng để truy cập dữ liệu, logic kinh doanh hoặc chức năng từ các dịch vụ máy chủ của bạn. Sử dụng API Gateway, bạn có thể tạo các API RESTful và WebSocket API cho các ứng dụng truyền thông hai chiều thời gian thực.

Biểu đồ kiến trúc sau đây mô tả, sử dụng các khái niệm Thiết kế Định hình theo Lĩnh vực (DDD), cách tận dụng API Gateway WebSocket API để tạo giao diện người dùng dựa trên sự kiện cho người dùng cuối. Để xem phiên bản PDF của biểu đồ này, hãy xem liên kết này.

Hình 2. Biểu đồ của BFF REST sử dụng API Gateway.

Các bước triển khai:

  1. Bắt các sự kiện từ ứng dụng của bạn bằng các người tiêu dùng sự kiện BFF được xây dựng với mục đích cụ thể. Chúng có trách nhiệm cập nhật các dự đoán dữ liệu phiên bản không chuẩn hóa trong Amazon DynamoDB để sử dụng trên giao diện người dùng.
  2. Khi giao diện người dùng được tải, các khách hàng phía frontend xác thực với Amazon Cognito, sau đó truy vấn dữ liệu bằng cách gọi API BFF được xây dựng với Amazon API Gateway. Sau đó, dữ liệu được truy xuất từ DynamoDB, hoặc trực tiếp bằng API Gateway hoặc thông qua một trình xử lý truy vấn BFF được xây dựng bằng AWS Lambda.
  3. Các khách hàng phía frontend đăng ký để nhận bất kỳ thay đổi dữ liệu sau này nào bằng cách kết nối vào một điểm cuối WebSocket BFF do API Gateway cung cấp, từ đó kích hoạt việc cập nhật bảng “khách hàng kết nối”.
  1. Tiếp tục tiêu thụ và xử lý tất cả các sự kiện liên quan từ ứng dụng của bạn bằng các người tiêu dùng sự kiện BFF. Những người tiêu dùng này liên tục cập nhật giao diện dữ liệu phiên bản không chuẩn hóa trong cơ sở dữ liệu BFF theo thời gian thực.
  2. Đăng ký theo dõi tất cả các sự kiện kết quả từ các thay đổi dữ liệu trong cơ sở dữ liệu BFF bằng Amazon DynamoDB Streams, sau đó đăng ký một kích hoạt trong AWS Lambda để gọi một chức năng xử lý luồng BFF một cách không đồng bộ khi phát hiện bản ghi luồng mới.
  3. Bộ xử lý luồng BFF của bạn sau đó gửi thông báo đến các khách hàng kết nối với WebSockets của API Gateway.
  4. Khi thông báo thay đổi từ API Gateway được nhận bởi các khách hàng phía frontend, họ có thể làm mới nội dung giao diện người dùng.

Xây dựng một BFF GraphQL dựa trên sự kiện sử dụng AppSync

Chúng ta thấy các tổ chức ngày càng chọn xây dựng các API với GraphQL vì nó giúp họ phát triển ứng dụng nhanh hơn, bằng cách cho phép các nhà phát triển phía frontend truy vấn nhiều cơ sở dữ liệu, dịch vụ micro, và API với một điểm cuối GraphQL duy nhất.

AWS AppSync là một dịch vụ được quản lý đầy đủ, giúp dễ dàng phát triển các API GraphQL bằng cách xử lý công việc nặng nề của việc kết nối an toàn với các nguồn dữ liệu như Amazon DynamoDB, AWS Lambda, và nhiều nguồn khác. Khi triển khai, AWS AppSync tự động mở rộng bộ máy thực thi API GraphQL của bạn lên và xuống để đáp ứng khối lượng yêu cầu API. Ngoài ra, AppSync thêm các bộ nhớ đệm để cải thiện hiệu suất, hỗ trợ các lưu trữ dữ liệu phía máy khách giữ cho các máy khách ngoại tuyến đồng bộ, và hỗ trợ cập nhật thời gian thực thông qua các đăng ký trong suốt qua WebSockets.

Biểu đồ kiến trúc dưới đây mô tả, sử dụng các khái niệm Thiết kế Định hình theo Lĩnh vực (DDD), cách tận dụng các đăng ký AppSync để tạo giao diện người dùng dựa trên sự kiện cho người dùng cuối. Để xem phiên bản PDF của biểu đồ này, hãy xem liên kết này.

Hình 3. Biểu đồ của BFF GraphQL sử dụng AppSync.

Các bước triển khai:

  1. Bắt các sự kiện từ ứng dụng của bạn bằng các người tiêu dùng sự kiện BFF được xây dựng với mục đích cụ thể. Chúng có trách nhiệm duy trì một góc nhìn không chuẩn hóa về dữ liệu trong Amazon DynamoDB để sử dụng trên giao diện người dùng.
  1. Khi giao diện người dùng được tải, các khách hàng phía frontend xác thực với Amazon Cognito, sau đó truy vấn dữ liệu bằng GraphQL bằng cách gọi API BFF được xây dựng với AWS AppSync. Sau đó, dữ liệu được truy xuất từ DynamoDB, hoặc trực tiếp bằng AWS AppSync hoặc thông qua một trình xử lý truy vấn BFF được xây dựng bằng AWS Lambda.
  1. Các khách hàng phía frontend đăng ký để nhận bất kỳ thay đổi dữ liệu sau này nào bằng cách sử dụng đăng ký AWS AppSync qua WebSockets.
  1. Tiếp tục tiêu thụ và xử lý tất cả các sự kiện liên quan từ ứng dụng của bạn bằng các người tiêu dùng sự kiện BFF. Những người tiêu dùng này liên tục cập nhật góc nhìn không chuẩn hóa về dữ liệu giao diện người dùng trong cơ sở dữ liệu BFF theo thời gian thực.
  1. Đăng ký theo dõi tất cả các sự kiện kết quả từ các thay đổi dữ liệu trong cơ sở dữ liệu BFF bằng Amazon DynamoDB Streams, sau đó đăng ký một kích hoạt trong AWS Lambda để gọi một chức năng xử lý luồng BFF một cách không đồng bộ khi phát hiện bản ghi luồng mới.
  1. Bộ xử lý luồng BFF của bạn sau đó gọi một biến đổi trống trên lược đồ GraphQL của AWS AppSync, được tạo một cách có chủ đích để kích hoạt đăng ký, từ đó gửi thông báo đến các khách hàng.
  1. Khi thông báo thay đổi từ AWS AppSync được nhận bởi các khách hàng phía frontend, họ có thể làm mới nội dung giao diện người dùng.

AWS AppSync cung cấp một trải nghiệm WebSockets đơn giản hóa với dữ liệu thời gian thực, kết nối, tính mở rộng và phát sóng, tất cả đều được xử lý bởi dịch vụ AWS AppSync, cho phép các nhà phát triển tập trung vào các trường hợp sử dụng và yêu cầu ứng dụng thay vì phải đối mặt với sự phức tạp của cơ sở hạ tầng cần thiết để quản lý kết nối WebSockets ở quy mô lớn.

Hàm xử lý luồng AWS Lambda trong bước 6 được kích hoạt khi các mục mới được chèn vào bảng Amazon DynamoDB. Nó đọc các mục này và cập nhật AWS AppSync, thông báo về dữ liệu mới. Mã mẫu dưới đây, viết bằng node.js, có sẵn trên GitHub là một phần của AWS Sample để triển khai AWS AppSync trong một thiết lập đa khu vực. Các chức năng chính đã được trích xuất và được giải thích dưới đây.

Mã dưới đây hiển thị điểm nhập vào hàm Lambda. Nó nhận một sự kiện từ DynamoDB, được kích hoạt bởi dữ liệu mới trong luồng. Đối với mỗi mục mới, nếu đó là dữ liệu đã được chèn (thay vì được cập nhật hoặc xóa), nó sẽ phân tích dữ liệu và gọi hàm executeMutation.

“`javascript

exports.handler = async (event) => {

  for (let record of event.Records) {

    switch (record.eventName) {

      case ‘INSERT’:

        // Lấy dữ liệu cần thiết từ luồng…

        let id = record.dynamodb.Keys.id.S;

        let name = record.dynamodb.NewImage.item.S;

        // … và sau đó thực hiện biến đổi publish

        await executeMutation(id, name);

        break;

      default:

        break;

    }

  }

  return { message: `Finished processing ${event.Records.length} records` }

}

“`

Mã dưới đây là hàm executeMutation thực hiện biến đổi AWS AppSync với dữ liệu mới nhận được từ luồng DynamoDB. Sử dụng thư viện bên thứ ba Axios (một trình khách HTTP dựa trên promise cho node.js) nó kết nối đến điểm cuối API AppSync và gửi yêu cầu biến đổi.

“`javascript

const executeMutation = async (id, name) => {

  const mutation = {

    query: print(publishItem),

    variables: {

      name: name,

      id: id,

    },

  };

  try {

    let response = await axios({

      url: process.env.AppSyncAPIEndpoint,

      method: ‘post’,

      headers: {

        ‘x-api-key’: process.env.AppSyncAPIKey

      },

      data: JSON.stringify(mutation)

    });

  } catch (error) {

    throw error;

  }

};

“`

Biến đổi được tạo ra bằng mã GQL sau đây:

“`graphql

const publishItem = gql`

  mutation PublishMutation($name: String!, $id: ID!) {

    publishItemsModel(input: {id: $id, item: $name}) {

      id

      item

    }

  }

`

“`

Kết luận

Mô hình BFF đề cập đến việc có một backend cho mỗi trải nghiệm người dùng cuối, thay vì chỉ có một backend API đa năng duy nhất.

Bằng cách triển khai mô hình BFF trong một kiến trúc dựa trên sự kiện, bạn có thể cải thiện trải nghiệm khách hàng cuối trên giao diện người dùng bằng cách cung cấp các cập nhật trực quan gần thời gian thực khi các dịch vụ micro tạo ra sự kiện về các thay đổi trong các tập hợp lĩnh vực.

Trong bài viết blog này, chúng tôi đã giải thích cách các nhà phát triển có thể áp dụng mô hình BFF cho các API REST và GraphQL của họ để tải các dự đoán dữ liệu sẵn sàng cho giao diện người dùng và làm mới giao diện người dùng với thông báo dựa trên sự kiện.

Bạn có thể tải về kiến trúc tham khảo chi tiết dưới dạng PDF cho Amazon API Gateway tại đây và AWS AppSync tại đây.