Interface Contracts Tập 3: API Contracts - Giao tiếp trong thế giới Microservices.
Ở hai tập trước, chúng ta đã dùng Interface để "ký hợp đồng" giữa các Class nằm trong cùng một hệ thống (codebase). Nhưng chuyện gì sẽ xảy ra nếu hệ thống của bạn phình to thành hàng chục Microservices chạy trên các server khác nhau? Service Đặt hàng (viết bằng Java) cần gọi Service Thanh toán (viết bằng Node.js) qua mạng Internet.
Lúc này, chúng ta không thể dùng chung một file interface của TypeScript hay Java được nữa. Chúng ta cần một "Bản hợp đồng phi ngôn ngữ" (Language-Agnostic Contract). Đó chính là API Contract.
1. Vấn đề: Thảm họa "Giao tiếp ngầm"
Tưởng tượng Team A (Frontend) và Team B (Backend) làm việc với nhau. Team B làm xong API Đăng nhập và ném cho Team A một tin nhắn qua Slack:
"Ê, gọi
POST /loginnhé. Truyềnusernamevớipassword. Trả về token."
Đây là một "hợp đồng bằng miệng", và nó vô cùng lỏng lẻo:
passwordcó cần mã hóa trước không? (Thiếu Pre-condition)- Nếu sai mật khẩu thì trả về HTTP Status
401hay400? Mã lỗi là gì? (Thiếu Post-condition)
Vài tháng sau, Backend âm thầm đổi username thành email và... BÙM! Toàn bộ App Mobile và Web crash hàng loạt vì gọi sai API.
2. Giải pháp 1: OpenAPI/Swagger (Dành cho REST API)
Để giải quyết bài toán trên, ngành công nghiệp phần mềm sinh ra OpenAPI Specification (trước đây gọi là Swagger). Đây là một file văn bản (định dạng JSON hoặc YAML) đóng vai trò là "Bản hợp đồng pháp lý" cho REST API.
Hãy xem cách tư duy Contract từ Tập 1 được ánh xạ vào OpenAPI:
# Hợp đồng thanh toán (payment-contract.yaml)
openapi: 3.0.0
info:
title: Payment Service API
version: 1.0.0
paths:
/payments:
post:
summary: Xử lý thanh toán
# ==========================================
# 1. PRE-CONDITIONS (Client phải gửi đúng cái này)
# ==========================================
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- orderId
- amount
properties:
orderId:
type: string
amount:
type: number
minimum: 1 # Invariant/Pre-condition: Tiền phải > 0
# ==========================================
# 2. POST-CONDITIONS (Provider cam kết trả về)
# ==========================================
responses:
'200':
description: Thanh toán thành công
content:
application/json:
schema:
type: object
properties:
transactionId:
type: string
status:
type: string
example: "SUCCESS"
'400':
description: Lỗi dữ liệu đầu vào (Vi phạm Pre-condition)
Sức mạnh của Hợp đồng OpenAPI:
- Sinh code tự động (Code Generation): Từ file YAML trên, các tool như OpenAPI Generator có thể tự động đẻ ra code Frontend (như Axios API Client) và code Backend (như interface controller). Lập trình viên không cần tự gõ tay lại model.
- Tài liệu sống (Living Documentation): UI của Swagger sẽ tự động render thành giao diện trực quan cho phép dev vào test bấm trực tiếp trên trình duyệt.
3. Giải pháp 2: gRPC và Protocol Buffers (Protobuf)
REST API dùng JSON rất dễ đọc, nhưng nó chậm và tốn băng thông. Trong nội bộ kiến trúc Microservices, khi các service gọi nhau hàng triệu lần mỗi giây, các ông lớn như Google dùng gRPC.
Bản hợp đồng của gRPC được viết bằng file .proto (Protocol Buffers). Nó cực kỳ chặt chẽ và tối ưu:
syntax = "proto3";
package payment;
// Hợp đồng Giao dịch
service PaymentService {
// Hàm xử lý
rpc ProcessPayment (PaymentRequest) returns (PaymentResponse);
}
// Pre-conditions (Dữ liệu đầu vào)
message PaymentRequest {
string order_id = 1;
double amount = 2;
}
// Post-conditions (Dữ liệu trả về)
message PaymentResponse {
string transaction_id = 1;
string status = 2;
}
Tại sao gRPC lại được gọi là "Đỉnh cao của Contract"?
Khi bạn có file .proto này, trình biên dịch của gRPC sẽ dịch nó ra mã nhị phân (nhỏ hơn JSON rất nhiều). Sau đó, nó tự động đẻ ra các class (Interface) bằng Java cho Backend và Go/Python cho các service khác. Hai service viết bằng hai ngôn ngữ khác nhau gọi nhau mượt mà như thể đang gọi một hàm cục bộ (local function).
Tổng kết Tập 4
Chuyển từ Class Interface sang API Contract, chúng ta thấy quy luật không hề thay đổi:
- Thiết kế trước (Design First): Các team Backend, Frontend, Mobile phải ngồi lại viết chung file Hợp đồng (Swagger/Protobuf) TRƯỚC KHI viết bất kỳ dòng code logic nào.
- Làm việc song song: Ký hợp đồng xong, Frontend dùng Mock Server (trả data giả theo đúng cấu trúc Swagger) để làm UI. Backend thì implement logic thật. Đến ngày ráp nối (Integration), mọi thứ sẽ khớp nhau chuẩn xác như bánh răng đồng hồ.
All rights reserved