Chương 66. Extensibility và Integration Boundary
Ở chương trước, ta nói về realtime state synchronization.
Đó là lúc hệ thống phải đồng bộ trạng thái giữa nhiều client và nhiều service.
Chương này nói về một ranh giới khác:
Hệ thống của ta được mở rộng hoặc tích hợp bởi bên khác.
Ví dụ:
Đối tác gọi API của ta.
Ta gửi webhook cho hệ thống của họ.
Khách hàng cài plugin.
Một LMS muốn đồng bộ lớp học và điểm.
Một tenant enterprise muốn workflow riêng.
Nghe hấp dẫn.
Mở API.
Mở webhook.
Cho đối tác tích hợp.
Cho plugin mở rộng sản phẩm.
Nhưng một khi mở cửa, hệ thống phải chịu thêm rủi ro:
Bên thứ ba gọi sai.
Bên thứ ba gọi quá nhiều.
Bên thứ ba chậm hoặc chết.
Schema đổi làm integration hỏng.
Webhook retry tạo bão.
Plugin chạy lỗi ảnh hưởng lõi.
API public bị abuse.
Thông điệp chính của chương:
> Extensibility không chỉ là mở thêm API. Đó là thiết kế ranh giới giữa hệ thống lõi và thế giới bên ngoài sao cho có contract rõ, versioning rõ, quota rõ, sandbox rõ, và lỗi của bên thứ ba không kéo sập hệ thống của mình.
---
66.1. Một tình huống: AI Judge tích hợp với LMS
Hãy tưởng tượng một trường đang dùng LMS riêng.
Họ muốn AI Judge tích hợp:
LMS gửi danh sách lớp sang AI Judge.
AI Judge nhận bài nộp.
AI Judge chấm bài.
Khi có điểm, AI Judge gửi điểm lại LMS.
Giáo viên xem điểm ngay trong LMS.
Ban đầu ta có thể nghĩ:
Viết vài endpoint là xong.
Ví dụ:
POST /api/submissions
GET /api/results/{id}
POST /webhooks/grading-completed
Nhưng production sẽ hỏi:
LMS gọi API quá nhanh thì sao?
LMS gửi submission trùng thì sao?
LMS downtime khi ta gửi webhook thì sao?
Webhook của ta retry bao lâu?
LMS dùng API v1, ta muốn đổi response thì sao?
Một đối tác bị lộ API key thì sao?
Đối tác test integration trên production thì sao?
Điểm gửi sang LMS rồi nhưng LMS trả timeout thì có gửi lại không?
Nếu gửi lại, LMS có bị ghi điểm trùng không?
Tích hợp là quan hệ lâu dài.
Không phải một request.
Kiến trúc integration phải chịu được lỗi, thay đổi và sự không hoàn hảo của bên ngoài.
---
66.2. Extensibility là gì?
Extensibility là khả năng hệ thống được mở rộng mà không phải sửa lõi quá nhiều.
Ví dụ:
Thêm provider thanh toán mới.
Thêm LMS integration mới.
Thêm webhook event mới.
Cho khách hàng tạo workflow riêng.
Cho đối tác đọc dữ liệu qua API.
Cho plugin thêm hành vi ở một điểm cụ thể.
Extensibility tốt không có nghĩa là:
Ai muốn làm gì cũng được.
Nó nghĩa là:
Hệ thống có những extension points rõ ràng.
Mỗi extension point có contract.
Có permission.
Có giới hạn.
Có observability.
Có versioning.
Có rollback hoặc disable.
Nếu không có ranh giới, extensibility biến thành code chắp vá.
Ví dụ:
Nếu tenant A thì chạy logic này.
Nếu partner B thì đổi response.
Nếu school C thì gọi webhook khác.
Sau một thời gian, lõi hệ thống thành một mớ điều kiện đặc biệt.
Extensibility tốt là mở rộng có kỷ luật.
---
66.3. Integration boundary là gì?
Integration boundary là ranh giới nơi hệ thống của ta giao tiếp với hệ thống khác.
Ví dụ:
Public API.
Partner API.
Webhook.
Plugin API.
File import/export.
OAuth/SSO.
Message/event integration.
SDK.
Ranh giới này cần được thiết kế kỹ vì hai bên không deploy cùng nhau.
Ta không kiểm soát code của đối tác.
Đối tác không biết toàn bộ nội bộ của ta.
Hai bên chỉ gặp nhau qua contract.
Vì vậy integration boundary cần:
Contract rõ.
Authentication.
Authorization.
Rate limit.
Idempotency.
Versioning.
Error format.
Retry policy.
Sandbox.
Monitoring.
Support/debug tooling.
Một API nội bộ có thể đổi nhanh.
Một API đối tác đã dùng thì đổi khó hơn nhiều.
Mở ranh giới ra ngoài là tạo lời hứa dài hạn.
---
66.4. Public API, private API và partner API
Không phải API nào cũng cùng loại.
Private API là API dùng nội bộ.
Ví dụ:
Frontend web của ta gọi backend của ta.
Service A gọi Service B trong hệ thống.
Private API có thể thay đổi nhanh hơn vì ta kiểm soát consumer.
Partner API là API dành cho đối tác cụ thể.
Ví dụ:
LMS của trường lớn gọi API đồng bộ lớp và điểm.
Partner API thường có hợp đồng, quota, support và migration plan rõ.
Public API là API mở cho nhiều developer/khách hàng dùng.
Ví dụ:
Ai có API key đều có thể tích hợp AI Judge vào hệ thống của họ.
Public API cần ổn định nhất.
Vì ta không biết hết consumer là ai.
Một lỗi phổ biến là:
Dùng API nội bộ làm public API.
API nội bộ thường lộ chi tiết implementation, thiếu versioning, thiếu error contract, thiếu quota.
Public/partner API nên được thiết kế như sản phẩm.
---
66.5. API public là sản phẩm
Khi mở public API, developer bên ngoài là user của sản phẩm.
Họ cần:
Docs rõ.
Authentication rõ.
SDK nếu cần.
Error message dễ hiểu.
Rate limit rõ.
Sandbox.
Versioning.
Changelog.
Deprecation policy.
Support path.
Nếu API khó dùng, integration sẽ tốn support.
Nếu API đổi bất ngờ, niềm tin giảm.
Nếu error mơ hồ, đối tác debug rất khổ.
Ví dụ error kém:
{ "error": "failed" }
Error tốt hơn:
{
"error_code": "SUBMISSION_ALREADY_EXISTS",
"message": "A submission with this external_id already exists.",
"request_id": "req_123"
}
Public API không chỉ cần chạy.
Nó cần giao tiếp tốt.
---
66.6. API key và OAuth
Integration cần authentication.
Hai cách phổ biến:
API key.
OAuth.
API key đơn giản:
Partner gửi Authorization: Bearer key_xxx
Hợp với server-to-server integration đơn giản.
Nhưng API key cần:
Scope.
Tenant binding.
Rotation.
Expiration nếu cần.
Revocation.
Audit.
Rate limit.
OAuth phù hợp khi:
Ứng dụng bên thứ ba hành động thay user.
User cấp quyền.
Scope rõ.
Token có thời hạn.
Refresh token.
Ví dụ:
LMS muốn truy cập dữ liệu lớp của giáo viên sau khi giáo viên authorize.
Không nên dùng một API key toàn quyền cho mọi thứ.
Key càng mạnh, rủi ro càng lớn.
Integration tốt bắt đầu từ quyền hẹp.
---
66.7. Scope và permission cho integration
API key hoặc OAuth token nên có scope.
Ví dụ:
submissions:read
submissions:write
results:read
classes:read
webhooks:manage
billing:read
Với multi-tenancy, scope phải gắn tenant:
key này chỉ dùng cho school_a.
Không nên có key:
Toàn quyền mọi tenant.
trừ các trường hợp rất đặc biệt và được bảo vệ cực chặt.
Ví dụ LMS của trường A chỉ nên:
Đọc/lưu dữ liệu của school_a.
Không được gọi API tenant khác.
Không được sửa billing.
Không được xem prompt logs nếu không cần.
Scope rõ giúp giảm thiệt hại nếu key bị lộ.
Nó cũng giúp sản phẩm bán theo plan:
Plan free không có API write.
Plan enterprise có webhook advanced.
---
66.8. Idempotency cho API integration
API integration phải có idempotency.
Đối tác sẽ retry khi timeout.
Network sẽ lỗi.
Webhook sẽ đến lại.
Nếu partner gửi:
POST /submissions
rồi request timeout, họ không biết submission đã được tạo chưa.
Họ retry.
Nếu API không idempotent, ta tạo hai submission.
Cách tốt hơn:
Partner gửi external_id hoặc idempotency_key.
Ví dụ:
{
"external_id": "lms_abc_submission_789",
"student_id": "stu_123",
"assignment_id": "ass_456"
}
Nếu request đến lại với cùng external_id, API trả submission cũ.
Không tạo mới.
Idempotency là phép lịch sự bắt buộc với API bên ngoài.
Nó giúp integration chịu được mạng thật.
---
66.9. Webhook cho bên thứ ba
Webhook là cách hệ thống của ta báo sự kiện cho bên thứ ba.
Ví dụ AI Judge gửi:
grading.completed
grading.failed
submission.received
credits.low
Webhook giúp đối tác không cần polling liên tục.
Ví dụ payload:
{
"event_id": "evt_123",
"event_type": "grading.completed",
"tenant_id": "school_a",
"created_at": "2026-05-12T10:00:00Z",
"data": {
"submission_id": "sub_456",
"external_submission_id": "lms_sub_789",
"score": 8.5,
"status": "graded"
}
}
Webhook cũng là contract.
Nó cần:
Event id.
Event type.
Timestamp.
Payload version.
Signature.
Retry policy.
Idempotency expectation.
Docs.
Testing tool.
Không nên gửi webhook kiểu tùy hứng.
Webhook là API đảo chiều.
---
66.10. Webhook signature
Webhook phải có signature để bên nhận xác thực:
Webhook này thật sự đến từ hệ thống của ta.
Payload chưa bị sửa.
Thường ta dùng secret riêng cho endpoint/tenant.
Header có thể chứa:
timestamp.
signature.
event_id.
Đối tác kiểm tra:
Signature đúng không?
Timestamp có quá cũ không?
Event_id đã xử lý chưa?
Nếu không có signature, ai đó có thể giả webhook:
grading.completed score = 10
Webhook là đường ghi dữ liệu vào hệ thống đối tác.
Nên phải có xác thực.
Tương tự, khi ta nhận webhook từ bên thứ ba, ta cũng phải verify signature.
---
66.11. Retry webhook
Bên nhận webhook có thể down.
Network có thể timeout.
Vì vậy webhook sender nên retry.
Nhưng retry phải có giới hạn:
Retry sau 1 phút.
Sau đó 5 phút.
Sau đó 30 phút.
Sau đó vài giờ.
Tối đa N lần hoặc trong X ngày.
Không nên retry liên tục mỗi giây.
Nếu đối tác đang down, ta sẽ tự tạo retry storm.
Webhook retry cần:
Exponential backoff.
Jitter.
DLQ hoặc failed delivery state.
Dashboard cho partner xem delivery.
Manual retry nếu cần.
Webhook receiver phải idempotent.
Vì cùng event có thể được gửi nhiều lần.
Trong docs nên nói rõ:
Partner phải dùng event_id để dedupe.
Webhook đáng tin là sự hợp tác giữa sender và receiver.
---
66.12. Plugin và extension point
Plugin cho phép bên khác thêm hành vi vào hệ thống.
Ví dụ:
Plugin xuất điểm sang hệ thống trường.
Plugin kiểm tra plagiarism.
Plugin tạo rubric từ template riêng.
Plugin gửi notification qua kênh riêng.
Extension point là điểm trong hệ thống cho phép plugin can thiệp.
Ví dụ:
on_submission_created
on_grading_completed
before_result_published
after_teacher_review
Extension point phải rõ:
Plugin được đọc gì?
Plugin được sửa gì?
Plugin chạy đồng bộ hay async?
Plugin fail thì sao?
Timeout bao lâu?
Có retry không?
Có sandbox không?
Nếu plugin chạy trong request chính, plugin chậm có thể làm app chậm.
Nếu plugin có quyền quá rộng, plugin lỗi có thể phá dữ liệu.
Plugin architecture cần ranh giới mạnh hơn API bình thường.
Vì plugin thường nằm gần lõi hơn.
---
66.13. Plugin không nên chạy trong lõi nếu không kiểm soát
Một lỗi nguy hiểm:
Cho plugin chạy code tùy ý trong process chính.
Nếu plugin crash, process crash.
Nếu plugin dùng CPU nhiều, app chậm.
Nếu plugin đọc secret, dữ liệu lộ.
Nếu plugin ghi database bừa, dữ liệu hỏng.
Cách an toàn hơn:
Plugin chạy ngoài process.
Plugin giao tiếp qua API/event.
Plugin có sandbox.
Plugin có permission scope.
Plugin có timeout.
Plugin có resource limit.
Plugin fail không làm lõi fail.
Không phải mọi hệ thống cần plugin code.
Nhiều khi chỉ cần:
Webhook.
Workflow config.
Rules engine đơn giản.
API integration.
Plugin là quyền năng lớn.
Chỉ mở khi thật sự cần và có năng lực vận hành.
---
66.14. API versioning
API đã có consumer bên ngoài thì phải có versioning strategy.
Ví dụ:
/api/v1/submissions
/api/v2/submissions
Hoặc version qua header:
API-Version: 2026-05-12
Điểm chính không phải chọn URL hay header.
Điểm chính là:
Consumer cũ không bị phá bất ngờ.
Consumer mới có đường dùng behavior mới.
Ta có cách deprecate version cũ.
Không phải thay đổi nào cũng cần version mới.
Thay đổi backward-compatible có thể thêm vào version hiện tại:
Thêm optional field.
Thêm endpoint mới.
Thêm error code mới có docs.
Breaking change cần version mới hoặc migration rõ.
Ví dụ:
score -> final_score
không nên đổi âm thầm trong v1.
---
66.15. Backward compatibility cho integration
Backward compatibility nghĩa là thay đổi mới không làm consumer cũ hỏng.
Với API bên ngoài, điều này cực kỳ quan trọng.
Cách đổi an toàn:
Thêm field mới, không xóa field cũ.
Cho request field mới là optional.
Giữ error format cũ.
Không đổi ý nghĩa enum cũ.
Không đổi unit của field cũ.
Ví dụ:
{
"score": 8.5,
"final_score": 8.5
}
giữ cả hai trong giai đoạn migration.
Sau đó:
Thông báo deprecation.
Theo dõi usage.
Cho thời gian chuyển.
Chỉ bỏ field cũ ở version mới hoặc sau deadline rõ.
Đối tác không phải service nội bộ.
Họ có lịch release riêng.
Tôn trọng backward compatibility là tôn trọng thời gian của họ.
---
66.16. Contract và schema
API, webhook, event đều cần schema.
Schema nói:
Field nào có.
Field nào required.
Kiểu dữ liệu là gì.
Enum có giá trị nào.
Error format ra sao.
Contract nói rộng hơn:
Ý nghĩa field.
Khi nào event được gửi.
Retry ra sao.
Idempotency kỳ vọng thế nào.
Rate limit thế nào.
Ví dụ contract webhook:
grading.completed được gửi sau khi result đã được lưu.
Mỗi event có event_id duy nhất.
Receiver phải xử lý idempotent theo event_id.
Nếu receiver trả 5xx hoặc timeout, sender retry.
Nếu receiver trả 2xx, xem như delivered.
Contract càng rõ, integration càng ít đoán.
Đoán là nguồn bug lớn trong integration.
Contract nên có test nếu integration quan trọng.
---
66.17. Breaking change
Breaking change là thay đổi làm consumer hiện tại hỏng hoặc hiểu sai.
Ví dụ:
Xóa field.
Đổi tên field.
Đổi kiểu dữ liệu.
Đổi enum value.
Đổi ý nghĩa field.
Đổi error format.
Đổi authentication.
Đổi retry behavior.
Đổi webhook event timing.
Breaking change trong integration đau hơn trong code nội bộ.
Vì consumer bên ngoài có thể:
Không đọc changelog.
Không deploy kịp.
Không có test tốt.
Phụ thuộc vào behavior cũ mà ta không biết.
Không phải vì vậy không bao giờ được thay đổi.
Nhưng breaking change cần quy trình:
Version mới.
Deprecation notice.
Migration guide.
Timeline.
Usage monitoring.
Support.
Sunset plan.
Breaking change không nên là bất ngờ.
---
66.18. Rate limit và quota theo partner
API public/partner phải có rate limit.
Nếu không, một partner lỗi có thể làm hệ thống chậm cho mọi người.
Ví dụ:
LMS bug retry liên tục POST /submissions.
Một đối tác import 1 triệu user trong giờ cao điểm.
Webhook receiver fail làm ta retry quá nhiều.
Rate limit theo:
Partner.
Tenant.
API key.
Endpoint.
Plan.
Quota theo:
Requests/day.
Submissions/month.
AI credits.
Webhook deliveries.
Export jobs.
Rate limit response nên rõ:
429 Too Many Requests
Retry-After: 60
Không nên chỉ timeout hoặc 500.
Rate limit tốt giúp partner tự điều chỉnh.
Nó cũng bảo vệ hệ thống khỏi abuse và bug.
---
66.19. Abuse theo partner
Không phải mọi traffic xấu đều là tấn công cố ý.
Đôi khi là integration bug.
Ví dụ:
Partner retry không backoff.
Partner gửi payload sai liên tục.
Partner quên dedupe.
Partner import dữ liệu test vào production.
Partner dùng API search như crawler.
Hệ thống nên có:
Rate limit.
Payload validation.
Abuse detection.
Partner dashboard.
Alert cho partner owner.
Temporary disable key.
Sandbox environment.
Nếu một partner gây lỗi liên tục, ta cần có khả năng:
Chặn endpoint cụ thể.
Giảm quota.
Tắt webhook delivery.
Disable API key.
mà không ảnh hưởng toàn bộ platform.
Integration boundary tốt cho phép cô lập vấn đề.
---
66.20. Sandbox environment
Sandbox environment là môi trường để đối tác test integration mà không ảnh hưởng production.
Sandbox nên có:
API endpoint riêng.
API keys riêng.
Dữ liệu giả.
Webhook test.
Payment/test mode nếu có.
Rate limit riêng.
Docs rõ.
Reset data nếu cần.
Ví dụ:
https://sandbox.api.aijudge.example
Đối tác có thể:
Tạo lớp test.
Tạo submission test.
Nhận webhook test.
Kiểm tra error handling.
Không nên để đối tác test mọi thứ trên production bằng cờ:
is_test = true
rồi hy vọng không lẫn.
Sandbox tách biệt giúp giảm rủi ro.
Nhưng sandbox cũng phải đủ giống production.
Nếu sandbox quá giả, integration pass sandbox rồi fail production.
---
66.21. Test webhook và replay
Đối tác cần cách test webhook.
Ví dụ dashboard cho partner:
Gửi event test.
Xem delivery history.
Xem response code.
Xem retry attempts.
Replay event.
Rotate signing secret.
Webhook replay rất hữu ích.
Ví dụ:
Partner sửa bug endpoint.
Muốn nhận lại event grading.completed bị fail.
Nhưng replay phải rõ:
Cùng event_id hay event_id mới?
Receiver có idempotent không?
Replay có giới hạn không?
Ai được replay?
Replay có audit không?
Thường nên giữ cùng event_id nếu replay cùng sự kiện.
Receiver dùng event_id để dedupe.
Nếu tạo event_id mới, partner có thể xử lý như sự kiện mới và tạo duplicate.
Design nhỏ này ảnh hưởng lớn.
---
66.22. Khi bên thứ ba lỗi thì hệ thống của mình nên phản ứng thế nào?
Bên thứ ba sẽ lỗi.
Webhook receiver sẽ down.
Partner API sẽ timeout.
Plugin sẽ crash.
OAuth provider sẽ chậm.
Nếu hệ thống của ta phụ thuộc trực tiếp vào bên thứ ba trong request chính, user của ta sẽ bị kéo theo.
Ví dụ nguy hiểm:
User submit bài.
AI Judge phải gọi LMS ngay để xác nhận.
LMS chậm.
Submit bài của user chậm hoặc fail.
Cách tốt hơn nếu nghiệp vụ cho phép:
Nhận bài trước.
Lưu trạng thái pending_sync.
Đẩy job sync LMS chạy nền.
Nếu LMS fail, retry và hiển thị trạng thái rõ.
Nguyên tắc:
Bên thứ ba lỗi không nên làm hỏng lõi nếu có thể tách async.
Không phải lúc nào cũng tách được.
Nhưng phải chủ động chọn.
---
66.23. Circuit breaker cho integration
Nếu một partner API đang lỗi, tiếp tục gọi liên tục có thể làm hệ thống của ta chậm.
Circuit breaker giúp tạm ngừng gọi khi lỗi quá nhiều.
Ví dụ:
LMS partner B timeout 80% trong 5 phút.
Circuit breaker mở.
Các sync job mới tạm hoãn.
Dashboard hiển thị sync delayed.
Sau 1 phút, thử lại một ít.
Điều này bảo vệ:
Worker.
Connection pool.
Queue.
Provider quota.
User-facing latency.
Integration cần timeout, retry, backoff, jitter và circuit breaker giống external provider khác.
Đối tác không phải lúc nào cũng ổn.
Thiết kế tốt không tức giận vì họ lỗi.
Nó cô lập lỗi.
---
66.24. Data ownership trong integration
Khi hai hệ thống tích hợp, cần rõ dữ liệu nào do ai sở hữu.
Ví dụ:
LMS là source of truth cho danh sách lớp.
AI Judge là source of truth cho grading result.
Payment provider là source of truth cho payment status.
Nếu không rõ, hai hệ thống có thể ghi đè nhau.
Ví dụ:
LMS đổi tên học sinh.
AI Judge cũng cho sửa tên.
Sync hai chiều gây conflict.
Cần quyết định:
Dữ liệu nào one-way sync?
Dữ liệu nào two-way sync?
Conflict xử lý ra sao?
Field nào chỉ đọc?
Field nào partner được update?
Integration càng nhiều chiều, càng phức tạp.
Một chiều rõ ràng thường dễ vận hành hơn hai chiều mơ hồ.
Sync hai chiều chỉ nên làm khi thật sự cần và có conflict strategy.
---
66.25. File import/export cũng là integration
Không phải integration nào cũng là API.
Nhiều doanh nghiệp vẫn dùng:
CSV import.
Excel export.
SFTP.
Batch file.
File integration cũng cần contract:
Schema cột.
Encoding.
Timezone.
Required fields.
Error report.
Idempotency.
Version.
Validation.
Partial failure.
Ví dụ import danh sách học sinh:
Row 1 hợp lệ.
Row 2 thiếu email.
Row 3 trùng external_id.
Hệ thống nên trả report rõ:
Import thành công 998 row.
2 row lỗi.
File lỗi có line number và error_code.
Không nên fail mơ hồ:
Import failed.
File import/export là API không có HTTP.
Vẫn cần thiết kế như integration boundary.
---
66.26. SDK
SDK giúp developer dùng API dễ hơn.
Ví dụ:
aijudge.submissions.create(...)
aijudge.webhooks.verify_signature(...)
SDK tốt giảm lỗi integration.
Nó có thể xử lý:
Authentication.
Retries an toàn.
Idempotency key.
Error parsing.
Pagination.
Webhook signature verification.
Type definitions.
Nhưng SDK cũng là thứ cần maintain.
Nếu API đổi mà SDK không cập nhật, developer bị kẹt.
Nếu có nhiều ngôn ngữ:
JavaScript.
Python.
Java.
PHP.
C#.
chi phí càng tăng.
Không phải platform nào cũng cần SDK sớm.
Nhưng nếu public API là phần quan trọng của sản phẩm, SDK có thể rất đáng giá.
---
66.27. Pagination và filtering
API public cần pagination.
Không nên để partner gọi:
GET /submissions
rồi trả toàn bộ 10 triệu records.
Pagination có thể dùng:
page/limit.
cursor.
created_after.
updated_after.
Cursor thường tốt hơn cho dữ liệu lớn và thay đổi liên tục.
Ví dụ:
GET /submissions?limit=100&cursor=abc
Filtering cũng cần rõ:
tenant.
assignment_id.
status.
updated_after.
API tích hợp thường cần incremental sync:
Cho tôi mọi submission thay đổi sau timestamp X.
Nhưng timestamp có thể tricky.
Cursor/change feed đáng tin hơn nếu sync quan trọng.
Design pagination kém sẽ trở thành vấn đề scale rất nhanh.
---
66.28. Error contract
Error contract rất quan trọng với integration.
Đối tác cần biết lỗi nào retry được, lỗi nào phải sửa request.
Ví dụ:
400 invalid request: không retry.
401 unauthorized: refresh token hoặc fix key.
403 forbidden: không có quyền.
404 not found: resource không tồn tại hoặc không thuộc scope.
409 conflict: duplicate/existing resource.
422 validation error: sửa payload.
429 rate limited: retry sau.
500/503: có thể retry với backoff.
Response nên có:
error_code.
message.
details nếu an toàn.
request_id.
retry_after nếu phù hợp.
Không nên bắt partner parse text message để biết lỗi.
Ví dụ không tốt:
"Something went wrong"
Ví dụ tốt:
{
"error_code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests for this API key.",
"retry_after_seconds": 60,
"request_id": "req_abc"
}
Error cũng là API.
---
66.29. Observability cho integration
Integration cần observability riêng.
Cần biết:
Partner nào gọi API nhiều nhất?
Endpoint nào lỗi nhiều?
API key nào bị rate limit?
Webhook nào fail?
Delivery lag bao lâu?
Partner nào đang dùng API version cũ?
Payload validation lỗi gì?
Sandbox vs production traffic ra sao?
Logs/traces nên có:
partner_id.
tenant_id.
api_key_id.
request_id.
endpoint.
version.
external_id.
webhook_event_id.
Khi partner báo:
Chúng tôi không nhận được điểm của submission X.
Ta cần tra được:
Result đã tạo chưa?
Webhook event_id là gì?
Gửi lúc nào?
Endpoint trả status gì?
Retry mấy lần?
Partner response body gì?
Không có observability, support integration sẽ rất đau.
---
66.30. Partner dashboard
Nếu integration là phần quan trọng, partner dashboard rất hữu ích.
Nó có thể hiển thị:
API keys.
Scopes.
Usage.
Rate limit.
Webhook endpoints.
Webhook delivery history.
Failed webhooks.
Replay button.
Docs links.
Sandbox credentials.
Changelog.
Partner dashboard giảm support.
Nó giúp đối tác tự debug.
Ví dụ:
Webhook grading.completed fail vì endpoint trả 500.
Partner nhìn thấy ngay.
Không cần hỏi support.
Dashboard cũng giúp bảo mật:
Rotate key.
Revoke key.
View last used.
Nếu public/partner API là sản phẩm, developer experience cũng là kiến trúc.
---
66.31. Khi nào mở platform là quá sớm?
Mở platform nghe rất hấp dẫn.
Nhưng mở quá sớm có thể làm sản phẩm chậm lại.
Vì một khi có API/plugin public, ta phải giữ lời hứa.
Mở platform quá sớm khi:
Core product chưa ổn định.
Data model còn đổi liên tục.
Permission chưa rõ.
Tenant isolation chưa chắc.
Không có đội support integration.
Không có docs/changelog/versioning.
Không có rate limit/abuse control.
Không có sandbox.
Không biết use case partner thật là gì.
API public đóng băng một phần thiết kế.
Nếu còn đang học product-market fit, có thể bắt đầu bằng:
Manual export.
Private beta API.
Partner-specific integration.
Webhook rất nhỏ.
Zapier/no-code integration có giới hạn.
Sau khi pattern rõ, mới mở rộng.
Extensibility tốt cần đúng thời điểm.
Không phải càng sớm càng hay.
---
66.32. Một chiến lược thực dụng cho AI Judge
Nếu AI Judge muốn mở integration, có thể đi theo từng bước.
Giai đoạn 1:
CSV import/export lớp, học sinh, điểm.
Webhook nội bộ hoặc partner beta cho grading.completed.
API key per tenant.
Rate limit đơn giản.
Manual docs.
Giai đoạn 2:
Partner API cho submissions/results/classes.
Webhook signature.
Delivery history.
Sandbox environment.
Idempotency external_id.
Versioned API.
Error contract chuẩn.
Giai đoạn 3:
Developer portal.
SDK.
OAuth nếu app thay user.
Partner dashboard.
Contract testing cho partner lớn.
Plugin/extension points có sandbox.
Usage/quota theo partner.
Giai đoạn 4:
Public platform.
Marketplace plugins nếu có nhu cầu thật.
Formal deprecation policy.
Advanced abuse detection.
Enterprise integration features.
Điểm quan trọng:
Không mở toàn bộ ngay.
Mở từ ranh giới nhỏ, đo nhu cầu, rồi chuẩn hóa.
---
66.33. Những sai lầm phổ biến
Sai lầm thứ nhất:
Dùng API nội bộ làm public API.
API nội bộ thường thiếu contract, versioning và bảo vệ.
Sai lầm thứ hai:
Không có idempotency.
Retry từ partner tạo dữ liệu trùng.
Sai lầm thứ ba:
Webhook không signature.
Receiver không xác thực được nguồn.
Sai lầm thứ tư:
Retry webhook quá hung hăng.
Đối tác down làm hệ thống mình tạo retry storm.
Sai lầm thứ năm:
Không có rate limit theo partner.
Một partner lỗi ảnh hưởng toàn hệ thống.
Sai lầm thứ sáu:
Không có sandbox.
Đối tác test trên production và tạo dữ liệu rác hoặc lỗi thật.
Sai lầm thứ bảy:
Breaking change không báo trước.
Integration bên ngoài hỏng hàng loạt.
Sai lầm thứ tám:
Mở plugin quá sớm.
Core chưa ổn nhưng đã phải support extension phức tạp.
---
66.34. Checklist integration boundary
Khi mở API/webhook/plugin cho bên ngoài, hãy hỏi:
- API này private, partner hay public?
- Consumer là ai?
- Có docs rõ không?
- Có versioning strategy không?
- Thay đổi có backward-compatible không?
- Có deprecation policy không?
- Authentication dùng gì?
- Token/key có scope không?
- Token/key có gắn tenant/partner không?
- Có rate limit/quota không?
- Có idempotency key/external_id không?
- Error contract có rõ không?
- Webhook có signature không?
- Webhook retry policy là gì?
- Webhook receiver có phải idempotent không?
- Có delivery history/replay không?
- Có sandbox environment không?
- Có observability theo partner không?
- Có cách disable partner/key khi lỗi không?
- Bên thứ ba lỗi có làm lõi hệ thống lỗi không?
- Plugin/extension có sandbox và permission không?
- Có thật sự cần mở platform ngay không?
Nếu nhiều câu trả lời là "chưa biết", integration boundary chưa đủ trưởng thành.
---
66.35. Bảng nhìn nhanh
| Khái niệm | Hiểu đơn giản | |---|---| | Extensibility | Khả năng hệ thống được mở rộng có kiểm soát | | Integration boundary | Ranh giới giao tiếp với hệ thống bên ngoài | | Private API | API dùng nội bộ | | Partner API | API cho đối tác cụ thể | | Public API | API mở cho developer/khách hàng rộng rãi | | Webhook | Hệ thống gửi event sang bên thứ ba | | Plugin | Thành phần mở rộng hành vi hệ thống | | Extension point | Điểm cho phép plugin/hook can thiệp | | API versioning | Quản lý nhiều phiên bản API | | Backward compatibility | Không làm consumer cũ hỏng | | Sandbox | Môi trường test integration an toàn | | Rate limit/quota | Giới hạn tốc độ/tổng usage | | Contract/schema | Lời hứa về request, response, event |
---
66.36. Kết luận của chương
Extensibility và integration giúp sản phẩm trở thành một phần trong hệ sinh thái lớn hơn.
Nhưng mở cửa hệ thống là tạo ranh giới mới cần được bảo vệ.
API public/partner cần contract, versioning, docs, auth, scope, rate limit, error format và idempotency.
Webhook cần signature, retry policy, delivery history và idempotency.
Plugin cần extension point rõ, sandbox, permission và resource limit.
Sandbox giúp đối tác test mà không phá production.
Observability theo partner giúp debug và support.
Và quan trọng:
Bên thứ ba lỗi là chuyện bình thường.
Hệ thống của ta phải cô lập lỗi đó thay vì để lỗi lan vào lõi.
Thông điệp cần nhớ:
> Mở API không khó. Giữ lời hứa API trong nhiều năm, chịu được partner lỗi, tránh breaking change, giới hạn abuse và vẫn bảo vệ lõi hệ thống mới là phần khó của extensibility.
Ở chương tiếp theo, ta sẽ nói về Abuse Prevention và Trust Boundary: vì khi hệ thống mở ra cho user, bot, file upload, URL, comment, search hoặc partner traffic, ta không thể mặc định mọi input và mọi traffic đều tốt.