FDE PulseViệc làm FDE đang mở 316Mới đăng 7 ngày qua 10Chủ đề nổi bật: Đào tạo kỹ năng FDE tại Đông Nam Á

Tờ báo của nghề Forward Deployed Engineer

Bách khoa

Thiết kế API cho giải pháp bàn giao: viết hợp đồng trước, viết code sau

Đội tích hợp của khách hàng sẽ dùng endpoint của bạn lâu hơn rất nhiều so với thời gian bạn ở lại dự án, nên mỗi tên trường bạn đặt hôm nay là một lời hứa phải giữ.

Đồ hoạThiết kế API contract-first cho giải pháp bàn giao
  1. 1Xác định ai được gọiPublic, partner hay private: chọn mức truy cập hẹp nhất mà vẫn đủ dùng
  2. 2Đặt tài nguyên theo danh từVí dụ /shipments/{id}/risk-assessment thay cho /getRiskScore
  3. 3Viết hợp đồng trướcViết OpenAPI hoặc schema GraphQL rồi review với đội sẽ gọi API
  4. 4Code lớp ánh xạKhông để response phản chiếu tên cột database; ghi có retry thì dùng PUT idempotent
  5. 5Tiến hóa có kỷ luậtThêm trường mới thay vì sửa trường cũ; khi buộc phải phá vỡ thì tăng version

Hợp đồng được viết và review với đội tích hợp trước khi viết code, còn version chỉ dùng khi có thay đổi phá vỡ.

Đồ hoạ: FDE Times

Tóm tắt nhanh

  • Với hệ thống của khách hàng, API là một hợp đồng. Vì vậy hãy viết hợp đồng (OpenAPI hoặc schema GraphQL) trước khi viết code.
  • Đừng để API phản chiếu cấu trúc database. Đặt tên tài nguyên theo danh từ nghiệp vụ, và thao tác ghi nào có thể bị retry thì phải idempotent.
  • Thêm trường mới thường không phá vỡ client. Khi buộc phải phá vỡ, chọn một chiến lược versioning và ghi rõ vào tài liệu bàn giao.
Chia sẻLinkedInFacebookX

Thử hình dung bạn đang ở tuần thứ ba tại site khách hàng. Model chấm rủi ro giao hàng đã chạy và buổi demo đã qua suôn sẻ. Rồi trưởng nhóm ERP bên khách hỏi ba câu: hệ thống của họ gọi vào đâu, nhận về dữ liệu gì, và khi bên bạn thay đổi thì họ được báo bằng cách nào.

Ba câu đó quyết định số phận của giải pháp sau khi bạn rời đi. Model có thể được thay bằng model khác, nhưng các endpoint mà đội ERP đã viết code để gọi vào thì rất khó đổi.

Việc của bạn là thiết kế lớp API đó sao cho đội khách hàng tích hợp được, vận hành được và tự nâng cấp được mà không cần gọi bạn quay lại.

API là hợp đồng chứ không phải chi tiết kỹ thuật

IBM định nghĩa API là một hợp đồng cho phép phần mềm này truy cập dữ liệu hoặc gọi chức năng mà phần mềm khác cung cấp. Với FDE, chữ “hợp đồng” nên được hiểu theo nghĩa đen. Mỗi trường trong response là một cam kết, và đội bên kia sẽ xây logic nghiệp vụ dựa trên cam kết đó.

Vì thế, câu hỏi đầu tiên cần trả lời là ai được phép gọi API, chưa phải nên dùng REST hay GraphQL. IBM phân loại API theo mức truy cập. API public được mở hoàn toàn ra internet, còn API partner và API private bị giới hạn người dùng.

Một giải pháp chấm rủi ro chỉ phục vụ ERP nội bộ và vài đối tác vận chuyển thì gần như chắc chắn không cần mở public. Xác định đúng điều này ngay từ đầu sẽ giúp bạn tránh được nhiều cuộc họp với đội bảo mật về sau.

REST hay GraphQL: chọn theo người gọi

Theo IBM, REST là kiến trúc phổ biến nhất cho web API hiện nay, còn SOAP là giao thức message dựa trên XML với những chuẩn chặt chẽ về cấu trúc và cách truyền message. Nếu ERP của khách chỉ nói được SOAP, bạn phải làm việc với SOAP, nhưng với thiết kế mới thì lựa chọn thực tế thường là REST hoặc GraphQL.

Tài liệu chính thức của GraphQL mô tả nó là cách xây API linh hoạt, không cần version, dựa trên một hệ thống kiểu mạnh. Schema định nghĩa hình dạng dữ liệu, và client chỉ yêu cầu đúng những trường mình cần.

Có một chi tiết dễ bị bỏ qua: đặc tả GraphQL cố ý không quy định cách xử lý mạng, phân quyền và phân trang, nên đội của bạn phải tự thiết kế cả ba.

REST

  • Kiến trúc phổ biến nhất cho web API, đội ERP gần như chắc chắn đã quen
  • Tài nguyên đặt theo danh từ, hành vi đi theo HTTP method
  • Thay đổi phá vỡ thì xử lý bằng version: URI, query string, header hoặc media type

GraphQL

  • Client lấy đúng các trường mình cần, phù hợp khi có nhiều màn hình với nhu cầu dữ liệu khác nhau
  • Tiến hóa schema theo thời gian thay vì đánh version
  • Phân quyền, phân trang và xử lý mạng phải tự thiết kế vì đặc tả không quy định

Với một đội tích hợp ERP chỉ cần gọi vài thao tác cố định, REST thường là lựa chọn ít rủi ro hơn khi bàn giao. GraphQL hợp lý hơn khi người dùng chính là nhiều frontend, mỗi frontend cần một lát cắt dữ liệu khác nhau.

Ví dụ từ đầu đến cuối: API chấm rủi ro lô hàng

Quay lại khách hàng logistics trong tình huống giả định. Bản nháp đầu tiên của đội thường trông như sau: endpoint POST /getRiskScore, response trả về {"shp_id": 8812, "rsk_v2": 0.83, "flg_manual": 1}. Bản này gọi được, nhưng có ba lỗi sẽ gây hậu quả sau khi bàn giao.

Lỗi thứ nhất là đặt động từ trong URI. Hướng dẫn của Microsoft khuyên đặt URI theo danh từ, tức tài nguyên, thay vì theo động từ, tức thao tác trên tài nguyên.

Lỗi thứ hai là các tên rsk_v2 và flg_manual lấy thẳng từ tên cột trong bảng. Microsoft khuyên không thiết kế API phản chiếu cấu trúc bên trong của database. Lý do là ngày bạn đổi tên cột, mọi client sẽ hỏng theo.

Lỗi thứ ba khó thấy nhất. ERP gửi dữ liệu lô hàng sang, gặp timeout và tự động retry. Nếu endpoint ghi dùng POST, mỗi lần retry có thể tạo thêm một bản ghi.

Microsoft lưu ý PUT bắt buộc phải idempotent, tức là gửi cùng một request nhiều lần vẫn chỉ sửa đúng một tài nguyên với cùng giá trị, còn POST và PATCH thì không được đảm bảo như vậy.

Bản sửa lại được viết thành hợp đồng trước khi viết code. Microsoft gọi cách làm này là contract-first: thiết kế hợp đồng, tức giao diện, rồi mới viết code hiện thực hợp đồng đó. Phần đầu hợp đồng khai báo thao tác ghi:

openapi: 3.0.3
info:
  title: Shipment Risk API
  version: 1.0.0
paths:
  /v1/shipments/{shipmentId}:
    put:
      summary: Tạo hoặc cập nhật lô hàng (idempotent, an toàn khi retry)

Phần tiếp theo, nằm cùng trong mục paths, mô tả thao tác đọc và hình dạng response mà ERP sẽ nhận:

  /v1/shipments/{shipmentId}/risk-assessment:
    get:
      summary: Lấy đánh giá rủi ro mới nhất
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                required: [shipmentId, riskLevel, score]
                properties:
                  shipmentId: { type: string }
                  riskLevel: { type: string, enum: [low, medium, high] }
                  score: { type: number, minimum: 0, maximum: 1 }
                  reasons: { type: array, items: { type: string } }

Hãy để ý những gì đã thay đổi: tài nguyên giờ là shipments và risk-assessment, đều là danh từ mà người bên ERP hiểu ngay. Ghi dữ liệu dùng PUT theo shipmentId do ERP cung cấp, nên retry bao nhiêu lần cũng chỉ có một lô hàng.

Bên trong, một lớp ánh xạ dịch rsk_v2 thành score, nên bạn có thể đổi schema database mà hợp đồng vẫn giữ nguyên.

Khi nào phải đánh version?

Sáu tháng sau, khách muốn thêm trường estimatedDelayHours. Thêm một trường mới thường không phá vỡ client, vì code cũ chỉ bỏ qua trường nó không biết. Ngược lại, đổi riskLevel từ chuỗi sang số, hoặc xóa reasons, là thay đổi phá vỡ.

Với thay đổi phá vỡ, Microsoft liệt kê bốn chiến lược: version trong URI, trong query string, trong header, hoặc trong media type. Microsoft đánh giá version trong URI như /v1/ là cách đơn giản. Nhưng họ cũng cảnh báo cách này sẽ trở nên cồng kềnh khi API trải qua nhiều vòng thay đổi và server phải duy trì nhiều version cùng lúc.

GraphQL đi theo hướng khác: tiến hóa hệ thống kiểu theo thời gian mà không đánh version. Trong thực hành, bạn thêm trường mới, giữ trường cũ, và chỉ loại bỏ trường cũ khi chắc chắn không còn client nào dùng.

Dù chọn REST hay GraphQL, quy tắc này cần được ghi vào tài liệu bàn giao để đội khách biết khi nào một trường bị xóa và họ được báo trước bằng cách nào.

Quy trình làm cho dự án tiếp theo

Bắt đầu bằng việc liệt kê các danh từ nghiệp vụ mà khách hàng hay dùng khi nói chuyện, ví dụ lô hàng, đơn hàng, đánh giá; đó là ứng viên cho tài nguyên. Tiếp theo, với mỗi thao tác ghi, hỏi đội tích hợp xem client của họ có retry khi timeout không.

Nếu có, thiết kế thao tác đó thành PUT trên một ID do client cung cấp. Khi buộc phải giữ POST, cách làm phổ biến là yêu cầu client gửi kèm một idempotency key trong header, để server nhận ra request lặp lại và trả về kết quả cũ thay vì tạo bản ghi mới.

Sau đó viết hợp đồng OpenAPI hoặc schema GraphQL và mang đi review với chính những người sẽ gọi API. Nếu chọn GraphQL, hãy đưa phần phân quyền và phân trang vào cuộc review đó, vì đặc tả không làm thay bạn. Chỉ khi hai bên đã thống nhất, bạn mới viết lớp ánh xạ và code xử lý.

Những lỗi hay gặp nhất đều xuất phát từ những cách làm ẩu cho nhanh: trả thẳng entity của ORM ra response, dùng POST cho mọi thao tác ghi, đổi kiểu dữ liệu một trường mà không tăng version, mở API public trong khi partner là đủ, và chọn GraphQL với giả định nó đã có sẵn phân quyền.

Mỗi lỗi đều tiết kiệm được một buổi chiều lúc viết code, rồi tốn của khách hàng nhiều tuần sau khi bàn giao.

Thể hiện kỹ năng này khi đi xin việc

Khi đọc JD của một vị trí FDE, hãy để ý các cụm như “integration”, “API design” hay “customer-facing technical delivery”. Đó là dấu hiệu công việc sẽ dùng đúng kỹ năng này.

Trong CV, thay vì viết “xây REST API”, hãy mô tả cụ thể: viết hợp đồng OpenAPI theo kiểu contract-first, review với đội tích hợp của đối tác, và thiết kế endpoint ghi idempotent để client retry an toàn.

Trước buổi phỏng vấn, hãy chuẩn bị sẵn câu trả lời cho tình huống kiểu: khách hàng cần đổi một trường mà nhiều hệ thống đang gọi vào thì bạn làm gì? Câu trả lời thuyết phục nên bắt đầu từ một hợp đồng đã được viết ra từ ngày đầu.

4 nguồn
Đọc tiếp trên lộ trình · Chặng 2: Kỹ thuật rộngKhi ERP không có API tử tế: cách FDE tích hợp mà không kéo theo cả hệ thống cũLối tắt nhanh nhất vào một hệ thống cũ thường là lối gỡ ra đắt nhất, vì vậy cần chọn cửa vào trước khi bắt đầu viết code.