Thiết kế API là gì? Hợp đồng dữ liệu giữa các hệ thống
API không phải tập hàm gọi được, mà là hợp đồng mà hai hệ thống phải tuân theo trong nhiều năm. Bài này chỉ ra bốn thành phần của hợp đồng, cách chọn kiểu thiết kế và chi phí thật của từng quyết định.

Mục lục bài viết
Trả lời nhanh
Thiết kế API là định nghĩa hợp đồng dữ liệu giữa hai hệ thống: tài nguyên, định dạng, lỗi và phiên bản. Kèm bảng trade-off, ba chế độ hỏng và chi phí ba năm.
Thiết kế API là quá trình định nghĩa hợp đồng dữ liệu giữa hai hệ thống: bên gọi gửi gì, bên nhận trả gì, lỗi báo thế nào và hợp đồng thay đổi ra sao theo thời gian. Hợp đồng rõ giúp hai đội phát triển độc lập; hợp đồng mơ hồ khiến mọi thay đổi nhỏ thành sự cố.
Tóm tắt nhanh
- API là hợp đồng công khai, không phải chi tiết triển khai bên trong.
- Hợp đồng gồm bốn phần: tài nguyên, định dạng dữ liệu, xử lý lỗi và chính sách phiên bản.
- Kiểu thiết kế (REST, RPC, GraphQL) là quyết định về chi phí, không phải về mức hiện đại.
- Những quyết định khó đổi nhất là phân trang, định danh và cách biểu diễn trạng thái.
- Chi phí thật của một API sai thể hiện ở vận hành, tuyển dụng và chi phí thay đổi trong ba năm.
Vì sao API là hợp đồng, không phải hàm nội bộ
Trong cùng một mã nguồn, gọi một hàm là chuyện nội bộ. Bạn có thể đổi tên, đổi tham số và sửa hành vi miễn là sửa hết chỗ gọi. Với API, bên gọi có thể nằm ở đội khác, ở ứng dụng di động đã phát hành, hoặc ở hệ thống đối tác mà bạn không kiểm soát lịch nâng cấp.
Ba đặc điểm khiến API khác hàm nội bộ:
Bên gọi không đồng thời nâng cấp. Ứng dụng di động có người dùng chưa cập nhật. Hệ thống đối tác có chu kỳ phát hành riêng. Vì vậy API phải chịu được tình trạng nhiều phiên bản cùng tồn tại.
Lỗi là một phần của hợp đồng. Với hàm nội bộ, lỗi thường là ngoại lệ và lập trình viên đọc mã để hiểu. Với API, lỗi phải được mô tả trước: mã trạng thái, cấu trúc thân lỗi và ý nghĩa từng trường. Bên gọi cần phân biệt lỗi tạm thời để thử lại với lỗi vĩnh viễn để dừng.
Thay đổi có chi phí tích luỹ. Mỗi lần đổi cấu trúc dữ liệu, bạn phải duy trì phiên bản cũ trong một khoảng thời gian. Chi phí này không biến mất; nó chuyển thành số phiên bản phải chạy song song và số nhánh mã phải kiểm thử.
Hiểu API là hợp đồng dẫn đến một hệ quả thực tế: thiết kế API là thiết kế chính sách thay đổi, không chỉ là đặt tên đường dẫn. Một API có tên đẹp nhưng không có chính sách phiên bản sẽ gây nhiều đau đớn hơn một API tên bình thường nhưng cam kết rõ ràng.
Bốn thành phần của một hợp đồng API
| Thành phần | Nội dung phải chốt | Sai sót thường gặp |
|---|---|---|
| Tài nguyên | Danh từ, định danh, quan hệ cha con | Dùng động từ trong đường dẫn, định danh mơ hồ |
| Định dạng dữ liệu | Kiểu trường, đơn vị, múi giờ, giá trị rỗng | Trộn lẫn số và chuỗi cho cùng một trường |
| Xử lý lỗi | Mã trạng thái, mã lỗi nội bộ, thông điệp | Trả 200 kèm lỗi trong thân, không phân biệt lỗi tạm thời |
| Chính sách phiên bản | Cách đánh phiên bản, thời gian hỗ trợ, cách báo ngừng | Không có chính sách, đổi hành vi âm thầm |
Bốn thành phần này phải được viết ra trước khi viết mã. Nếu chỉ một trong bốn bị bỏ qua, hợp đồng sẽ bị hiểu khác nhau ở hai phía, và khác biệt thường chỉ lộ ra khi có sự cố.
Tài nguyên: đặt tên theo thứ bên gọi cần
Tài nguyên nên là danh từ nghiệp vụ mà bên gọi quan tâm, không phải bảng trong cơ sở dữ liệu. Điều này quan trọng vì bảng có thể tách hoặc gộp mà bên gọi không cần biết. Nếu API lộ cấu trúc bảng, mọi lần tái cấu trúc dữ liệu đều thành thay đổi hợp đồng.
Quan hệ cha con nên phản ánh quyền truy cập thực tế. Một tài nguyên con chỉ truy cập được qua cha khi quyền được xác định theo cha. Nếu tài nguyên con có thể truy cập trực tiếp, cần đặt định danh toàn cục cho nó, nếu không bên gọi sẽ phải giữ nhiều định danh cho cùng một thực thể.
Định dạng dữ liệu: chốt đơn vị và giá trị rỗng
Ba câu hỏi luôn phải trả lời trong hợp đồng dữ liệu:
- Đơn vị và múi giờ. Thời gian nên ở dạng chuẩn quốc tế có múi giờ, không phải chuỗi tự do. Tiền tệ phải ghi rõ đơn vị nhỏ nhất, ví dụ đồng hoặc phần trăm của đồng.
- Giá trị rỗng. Phân biệt "không có trường", "trường có nhưng rỗng" và "trường có giá trị không". Ba trạng thái này dẫn đến hành vi khác nhau ở bên nhận.
- Kiểu số. Số thực gây sai lệch khi tính tiền. Với số tiền và số lượng, nên dùng số nguyên theo đơn vị nhỏ nhất.
Ba câu hỏi này nghe nhỏ nhưng là nguồn lỗi phổ biến nhất trong tích hợp. Chốt chúng trong hợp đồng rẻ hơn nhiều so với sửa dữ liệu đã ghi sai.
Xử lý lỗi: thiết kế cho trường hợp xấu
Hợp đồng lỗi cần ba tầng thông tin. Tầng giao thức cho biết kết quả ở mức vận chuyển. Tầng nghiệp vụ cho biết lý do cụ thể. Tầng thông điệp cho người đọc hiểu và cho nhật ký tìm kiếm.
Điều quan trọng nhất là phân biệt lỗi có thể thử lại với lỗi không thể thử lại. Bên gọi cần biết khi nào nên thử lại sau một khoảng chờ và khi nào nên dừng và báo cho người dùng. Nếu hợp đồng không nói rõ, bên gọi sẽ đoán, và đoán sai dẫn đến hoặc là mất dữ liệu, hoặc là gửi trùng.
Nên có mã lỗi nội bộ ổn định, tách khỏi thông điệp. Thông điệp có thể đổi ngôn ngữ hoặc cách diễn đạt; mã lỗi thì không. Bên gọi nên rẽ nhánh theo mã lỗi, không theo thông điệp.
Chính sách phiên bản: cam kết bằng thời gian
Chính sách phiên bản cần trả lời ba câu: phiên bản được đánh ở đâu (đường dẫn, tiêu đề hay tham số), một phiên bản được hỗ trợ bao lâu, và bên gọi được báo trước bao lâu khi ngừng hỗ trợ.
Không có câu trả lời đúng cho mọi trường hợp, nhưng phải có câu trả lời bằng văn bản. Chính sách ghi rõ giúp hai đội lập kế hoạch. Chính sách ngầm hiểu khiến mỗi lần đổi thành một cuộc thương lượng, và cuộc thương lượng thường diễn ra dưới áp lực sự cố.
Bảng trade-off ba kiểu thiết kế
| Kiểu | Điểm mạnh | Điểm yếu | Chọn khi nào |
|---|---|---|---|
| REST trên tài nguyên | Dễ hiểu, dễ cache, công cụ phổ biến | Nhiều lần gọi cho một màn hình phức tạp | Hệ thống nghiệp vụ nhiều tài nguyên, bên gọi đa dạng |
| RPC theo hành động | Gọn cho thao tác phức tạp, hợp đồng chặt | Ít tận dụng cache, khó khám phá | Thao tác nghiệp vụ rõ ràng, ít bên gọi |
| GraphQL | Bên gọi chọn trường cần, giảm lần gọi | Phức tạp về phân quyền và giới hạn truy vấn | Nhiều giao diện khác nhau cần dữ liệu khác nhau |
Bảng này không xếp hạng. Mỗi kiểu tối ưu cho một loại chi phí: REST tối ưu cho chi phí học và chi phí hạ tầng, RPC tối ưu cho chi phí diễn đạt thao tác, GraphQL tối ưu cho chi phí phối hợp giữa nhiều giao diện.
Điều đáng cân nhắc là chi phí ba năm. REST có chi phí ban đầu thấp và chi phí dài hạn ổn định. RPC có chi phí ban đầu thấp nhưng dễ tạo ra nhiều hành động rời rạc, khó tái sử dụng. GraphQL có chi phí ban đầu cao hơn về phân quyền và giới hạn tài nguyên, nhưng giảm chi phí khi số lượng giao diện tăng.
Phiên bản và tương thích ngược
Tương thích ngược nghĩa là bên gọi cũ vẫn chạy đúng khi bên nhận thay đổi. Có ba loại thay đổi:
Thay đổi an toàn. Thêm trường tuỳ chọn vào phản hồi, thêm tham số tuỳ chọn vào yêu cầu, thêm giá trị mới vào một tập giá trị mà bên gọi đã xử lý mặc định. Những thay đổi này không cần phiên bản mới, nhưng cần bên gọi chịu được trường lạ.
Thay đổi có điều kiện. Đổi ý nghĩa một trường, đổi định dạng thời gian, đổi đơn vị tiền tệ. Những thay đổi này an toàn về mặt kỹ thuật nhưng nguy hiểm về mặt ngữ nghĩa, vì bên gọi có thể vẫn chạy mà tính sai. Cần thông báo và thường cần phiên bản mới.
Thay đổi phá vỡ. Xoá trường, đổi kiểu, đổi mã trạng thái, đổi cấu trúc lỗi. Những thay đổi này bắt buộc phải có phiên bản mới và thời gian chuyển tiếp.
Cách giảm chi phí phiên bản là thiết kế phản hồi chịu được trường lạ ngay từ đầu, và giữ nguyên các trường đã công bố thay vì xoá. Một trường thừa trong phản hồi rẻ hơn nhiều so với một phiên bản phải chạy song song.
Những quyết định khó đổi nhất
Có bốn quyết định gần như không thể sửa mà không phá bên gọi.
Định danh. Nếu dùng số tăng dần, bên gọi có thể suy ra số lượng bản ghi và thứ tự tạo. Nếu sau này cần đổi sang định danh khó đoán, mọi bên gọi phải đổi kiểu dữ liệu. Nên chốt kiểu định danh ngay từ đầu, kèm cam kết không tái sử dụng.
Phân trang. Phân trang theo số trang và kích thước trang đơn giản nhưng sai lệch khi dữ liệu thay đổi giữa hai lần gọi. Phân trang theo con trỏ ổn định hơn nhưng khó dùng hơn cho giao diện có số trang. Chốt sai kiểu phân trang dẫn đến việc bỏ sót hoặc lặp bản ghi trong báo cáo.
Cách biểu diễn trạng thái. Một trường trạng thái dạng chuỗi tự do sẽ nhanh chóng có nhiều biến thể. Nên chốt tập giá trị và quy tắc thêm giá trị mới. Bên gọi phải được yêu cầu xử lý giá trị lạ bằng một nhánh mặc định.
Quan hệ và việc nhúng dữ liệu. Nhúng dữ liệu liên quan giúp giảm số lần gọi nhưng làm phản hồi phình to và tạo phụ thuộc vào cấu trúc bên trong. Tách thành tài nguyên riêng giúp phản hồi gọn nhưng tăng số lần gọi. Quyết định này ảnh hưởng trực tiếp đến hiệu năng và chi phí băng thông trong nhiều năm.
Bốn quyết định này nên được viết thành tài liệu quyết định kèm lý do, không chỉ nằm trong mã. Lý do quan trọng hơn kết luận, vì khi hoàn cảnh đổi, người sau cần biết ràng buộc ban đầu là gì.
Ba chế độ hỏng thường gặp
| Triệu chứng | Hệ quả | Cách giảm thiểu |
|---|---|---|
| Bên gọi gửi trùng đơn hàng khi mạng chập chờn | Dữ liệu sai, phải xử lý thủ công | Yêu cầu có khoá chống trùng, bên nhận ghi nhớ khoá trong một khoảng thời gian |
| Một thay đổi nhỏ làm sập ứng dụng di động cũ | Người dùng không đăng nhập được, mất doanh thu | Chỉ thêm trường tuỳ chọn, không xoá trường, kiểm thử với phiên bản cũ |
| Phản hồi quá lớn làm chậm màn hình danh sách | Trải nghiệm kém, tải máy chủ cao | Chốt giới hạn kích thước trang, buộc lọc phía máy chủ |
Ba chế độ này chiếm phần lớn sự cố tích hợp trong thực tế. Chúng không phải lỗi kỹ thuật hiếm gặp mà là hệ quả của hợp đồng thiếu quy định. Mỗi dòng trong bảng tương ứng với một điều khoản cần có trong tài liệu API.
Chi phí ba năm của một quyết định API
Khi chọn kiểu thiết kế, chi phí không dừng ở lần triển khai đầu tiên.
Chi phí vận hành. API nhiều lần gọi làm tăng tải máy chủ và băng thông. API trả phản hồi lớn làm tăng chi phí truyền dữ liệu, đặc biệt với ứng dụng di động dùng mạng di động. Chi phí này tăng theo số người dùng, nên quyết định hôm nay quyết định hoá đơn ba năm sau.
Chi phí tuyển dụng và đào tạo. REST phổ biến nên người mới làm quen nhanh. GraphQL và RPC đòi hỏi hiểu biết cụ thể hơn, nên thời gian hoà nhập dài hơn. Với đội nhỏ, chi phí này đáng kể vì mỗi người phải nắm nhiều phần.
Chi phí thay đổi. Mỗi phiên bản song song là một nhánh mã phải kiểm thử và một tập cấu hình phải duy trì. Nếu chính sách phiên bản lỏng, số phiên bản tích luỹ và chi phí thay đổi tăng dần cho đến khi không ai dám sửa.
Ba chi phí này giải thích vì sao một API "chạy được" vẫn có thể là quyết định tốn kém. Đánh giá đúng cần nhìn ba năm, không nhìn lần phát hành đầu.
Quy trình thiết kế API trong bốn giai đoạn
Thiết kế API nên nằm trong cùng quy trình phát triển sản phẩm, theo bốn giai đoạn Khám phá, Đặc tả, Xây dựng, Bàn giao và đo lường.
Khám phá. Xác định bên gọi là ai, họ cần gì và tần suất gọi dự kiến. Kết quả là danh sách tình huống sử dụng, không phải danh sách đường dẫn.
Đặc tả. Viết hợp đồng: tài nguyên, trường dữ liệu, mã lỗi, chính sách phiên bản và ví dụ yêu cầu, phản hồi. Đặc tả nên đủ để một đội khác viết mã gọi mà không cần hỏi.
Xây dựng. Triển khai theo đặc tả, kèm kiểm thử hợp đồng. Kiểm thử hợp đồng kiểm tra cấu trúc và mã lỗi, không chỉ kiểm tra kết quả đúng.
Bàn giao và đo lường. Bàn giao tài liệu, bộ ví dụ và kênh hỗ trợ. Sau khi chạy, đo tỉ lệ lỗi theo mã, thời gian phản hồi theo đường dẫn và số bên gọi còn dùng phiên bản cũ.
Bước cuối thường bị bỏ qua nhưng lại quyết định chi phí dài hạn. Không đo thì không biết khi nào có thể ngừng một phiên bản, và phiên bản cũ ở lại vô thời hạn.
Câu hỏi thường gặp
Thiết kế API có cần tài liệu riêng ngoài mã nguồn không?
Có. Mã nguồn cho biết hiện tại hệ thống làm gì, nhưng không cho biết cam kết với bên gọi là gì và cái gì được phép thay đổi. Tài liệu hợp đồng ghi rõ phần cam kết, phần nội bộ và chính sách phiên bản. Khi hai bên tranh luận về một thay đổi, tài liệu là căn cứ thay vì ký ức của người viết mã.
Nên đánh phiên bản API ở đường dẫn hay ở tiêu đề?
Cả hai cách đều dùng được. Đánh ở đường dẫn dễ thấy, dễ ghi nhật ký và dễ định tuyến, nhưng làm đường dẫn dài và khuyến khích tạo nhiều phiên bản. Đánh ở tiêu đề giữ đường dẫn ổn định nhưng khó kiểm tra bằng trình duyệt và dễ bị bỏ sót khi gọi nội bộ. Điều quan trọng hơn vị trí là có chính sách thời gian hỗ trợ rõ ràng.
API có cần kiểm thử hợp đồng không?
Nên có, đặc biệt khi có nhiều bên gọi. Kiểm thử hợp đồng kiểm tra cấu trúc phản hồi, mã lỗi và các trường bắt buộc, nên phát hiện thay đổi phá vỡ trước khi phát hành. Với API chỉ dùng nội bộ trong một ứng dụng, kiểm thử tích hợp có thể đủ, nhưng khi có bên gọi ngoài thì kiểm thử hợp đồng tiết kiệm nhiều hơn chi phí viết.
Làm sao biết hợp đồng API đã đủ rõ?
Một phép thử đơn giản: đưa tài liệu cho một lập trình viên chưa từng làm việc với hệ thống và yêu cầu viết một lệnh gọi hoàn chỉnh, gồm cả xử lý lỗi. Nếu họ phải hỏi lại về đơn vị, múi giờ, giá trị rỗng hay mã lỗi, hợp đồng còn thiếu. Mỗi câu hỏi là một điều khoản cần bổ sung.
Cập nhật và kiểm chứng
Cập nhật tháng 11/2026. Nội dung được rà soát theo thực tiễn thiết kế giao diện lập trình và các nguyên tắc tương thích ngược phổ biến. Bài viết mang tính tham khảo; quyết định cụ thể cần dựa trên ràng buộc của hệ thống và bên gọi thực tế.
Bài viết liên quan: thiết kế API chuẩn RESTful, nguyên tắc thiết kế API, kiến trúc phần mềm là gì và AI coding là gì.
Nếu bạn cần một hợp đồng API đủ rõ để đội khác tích hợp mà không phải hỏi lại, WinSell xây dựng phần mềm theo yêu cầu theo bốn giai đoạn Khám phá, Đặc tả, Xây dựng, Bàn giao và đo lường — xem dịch vụ của WinSell. Báo giá sau khi chốt phạm vi.
Cần một hệ thống chạy được, không chỉ một bài viết?
WinSell xây phần mềm theo yêu cầu và làm SEO/GEO cho chính sản phẩm đó. Phạm vi và báo giá được chốt sau bước khám phá.


