Đơn vị biên soạn: TechData.AI
Chủ đề: Data Contract và Schema Management cho Kafka
Đối tượng: Data Engineer, Backend Developer và Solution Architect
Phiên bản tham chiếu: Apache Kafka 4.x
MỤC LỤC
- Mục tiêu bài
- Vì sao Kafka cần schema
- Data Contract trong Event Streaming
- Schema Registry hoạt động như thế nào
- Subject, Schema ID và Version
- Apache Avro
- Protobuf
- JSON Schema
- So sánh ba định dạng
- Schema Evolution
- Backward, Forward và Full Compatibility
- Thiết kế schema sự kiện đơn hàng
- Producer và Consumer với Avro
- Quy trình thay đổi schema
- Schema Governance
- Những thay đổi nguy hiểm
- Kiểm thử Compatibility trong CI/CD
- Lỗi thường gặp
- Tổng kết
- Câu hỏi ôn tập
- Tài liệu tham khảo
1. Mục tiêu bài
Kafka cho phép Producer gửi byte và Consumer đọc byte. Sự linh hoạt này cũng tạo rủi ro. Nếu Producer đổi field total_amount từ số sang chuỗi mà không thông báo, hàng loạt Consumer có thể lỗi dù Kafka Cluster vẫn hoàn toàn khỏe mạnh.
Schema định nghĩa cấu trúc và kiểu dữ liệu của Event. Schema Registry lưu phiên bản schema, kiểm tra compatibility và giúp Producer cùng Consumer thống nhất cách serialize dữ liệu.
Bài này phân tích Avro, Protobuf, JSON Schema và xây dựng quy trình thay đổi Event an toàn cho hệ thống nhiều đội phát triển.
2. Vì sao Kafka cần schema

Một JSON Event nhìn có vẻ tự mô tả:
{
"order_id": "ORD-10001",
"total_amount": 1250000,
"currency": "VND"
}
Nhưng Consumer vẫn phải đoán:
- Field nào bắt buộc?
total_amountlà integer, decimal hay float?- Có thể null không?
currencytheo chuẩn nào?- Khi thêm field mới, Consumer cũ có chạy không?
- Khi xóa field, Consumer nào bị ảnh hưởng?
Không có schema, hợp đồng chỉ tồn tại trong tài liệu hoặc source code rải rác. Producer có thể thay đổi payload và chỉ phát hiện vấn đề khi Consumer Production bắt đầu thất bại.
Schema đưa những giả định đó thành cấu trúc có thể kiểm tra tự động.
3. Data Contract trong Event Streaming

Schema là một phần của Data Contract, nhưng Data Contract rộng hơn. Một Contract đầy đủ có thể bao gồm:
- Tên và ý nghĩa Event.
- Owner của Producer.
- Schema của Key và Value.
- Ý nghĩa nghiệp vụ của từng field.
- Quy tắc dữ liệu và đơn vị đo.
- Compatibility Policy.
- SLO về độ trễ và Availability.
- Retention.
- Phân loại dữ liệu nhạy cảm.
- Quy trình thay đổi và deprecation.
Ví dụ, schema có thể nói total_amount là số nguyên 64 bit nhưng không nói đơn vị là đồng hay xu. Data Contract phải giải thích rõ để Consumer không diễn giải sai.
4. Schema Registry hoạt động như thế nào

Schema Registry là dịch vụ lưu trữ và quản lý schema. Khi Producer gửi Event, serializer đăng ký hoặc tra cứu schema, nhận Schema ID rồi ghi ID cùng payload vào Kafka record.
Consumer đọc Schema ID, lấy schema tương ứng từ Registry và deserialize payload thành object.
Kafka Broker không tự hiểu nội dung Avro hay Protobuf. Broker vẫn lưu byte. Schema Registry nằm ở tầng Client và quản trị Contract.
Luồng cơ bản:
- Producer có schema.
- Serializer kiểm tra schema với Registry.
- Registry trả Schema ID.
- Producer ghi ID và payload vào Kafka.
- Consumer đọc ID.
- Deserializer lấy schema và giải mã.
Client thường cache schema để không gọi Registry cho mọi record.
5. Subject, Schema ID và Version

Schema Registry nhóm các phiên bản dưới Subject. Subject Naming Strategy quyết định cách Topic, Record Name và schema liên kết với nhau.
Các chiến lược phổ biến:
- Topic Name Strategy.
- Record Name Strategy.
- Topic Record Name Strategy.
Topic Name Strategy thường tạo Subject riêng cho Key và Value của mỗi Topic. Nó đơn giản nhưng thường giả định một loại record chính trong Topic.
Record Name Strategy cho phép cùng Record Type xuất hiện ở nhiều Topic và một Topic chứa nhiều loại Record, nhưng quản trị phức tạp hơn.
Schema ID thường là định danh cho nội dung schema trong Registry. Version là số thứ tự của schema trong Subject. Không nên nhầm Version của Registry với event_version mang ý nghĩa nghiệp vụ trong Event Envelope.
6. Apache Avro

Avro là định dạng phổ biến trong hệ sinh thái Kafka. Schema được mô tả bằng JSON, còn payload được mã hóa nhị phân.
Ví dụ schema:
{
"type": "record",
"name": "OrderCreated",
"namespace": "ai.techdata.ecommerce",
"fields": [
{"name": "event_id", "type": "string"},
{"name": "order_id", "type": "string"},
{"name": "customer_id", "type": "string"},
{"name": "total_amount", "type": "long"},
{"name": "currency", "type": "string", "default": "VND"}
]
}
Avro phân biệt Writer Schema và Reader Schema. Producer ghi theo Writer Schema, Consumer đọc bằng Reader Schema. Cơ chế schema resolution giúp Consumer mới đọc dữ liệu cũ hoặc Consumer cũ đọc dữ liệu mới trong những thay đổi tương thích.
Ưu điểm của Avro:
- Payload nhỏ.
- Hỗ trợ Schema Evolution tốt.
- Phù hợp Data Platform.
- Schema có thể quản lý độc lập.
Hạn chế là dữ liệu nhị phân không đọc trực tiếp như JSON và hệ thống cần tooling phù hợp.
7. Protobuf

Protocol Buffers mô tả message bằng file .proto:
syntax = "proto3";
package ai.techdata.ecommerce;
message OrderCreated {
string event_id = 1;
string order_id = 2;
string customer_id = 3;
int64 total_amount = 4;
string currency = 5;
}
Mỗi field có số định danh. Khi xóa field, không nên tái sử dụng số cũ cho field mới vì dữ liệu lịch sử có thể bị diễn giải sai.
Protobuf hỗ trợ code generation mạnh và phù hợp với hệ thống microservice đa ngôn ngữ. Schema có cấu trúc rõ, payload nhỏ và hiệu suất tốt.
Quản lý import, package, field number và compatibility đòi hỏi kỷ luật. Schema Registry có thể hỗ trợ lưu và kiểm tra Protobuf schema.
8. JSON Schema

JSON Schema định nghĩa ràng buộc cho payload JSON:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "OrderCreated",
"type": "object",
"required": ["event_id", "order_id", "total_amount"],
"properties": {
"event_id": {"type": "string"},
"order_id": {"type": "string"},
"total_amount": {"type": "integer", "minimum": 0},
"currency": {"type": "string", "default": "VND"}
}
}
JSON Schema giữ trải nghiệm JSON quen thuộc, dễ debug và tích hợp với API. Payload thường lớn hơn định dạng nhị phân và quy tắc compatibility cần được hiểu đúng theo implementation của Registry.
JSON Schema phù hợp khi tính dễ đọc và hệ sinh thái JSON quan trọng hơn tối ưu kích thước tuyệt đối.
9. So sánh ba định dạng

| Tiêu chí | Avro | Protobuf | JSON Schema |
|---|---|---|---|
| Payload | Nhị phân, nhỏ | Nhị phân, nhỏ | JSON, thường lớn hơn |
| Dễ đọc trực tiếp | Thấp | Thấp | Cao |
| Code generation | Có | Rất mạnh | Tùy công cụ |
| Schema Evolution | Mạnh | Mạnh nếu quản lý field number | Tốt khi có quy ước |
| Phù hợp | Data Platform | Microservice đa ngôn ngữ | API và hệ thống ưu tiên JSON |
Không có định dạng tốt nhất cho mọi tổ chức. Hãy chọn dựa trên ngôn ngữ, hệ sinh thái, kỹ năng đội ngũ, yêu cầu hiệu suất và quy trình Governance.
Điều quan trọng hơn định dạng là doanh nghiệp có enforce compatibility, review schema và theo dõi owner hay không.
10. Schema Evolution

Schema Evolution là khả năng thay đổi schema mà vẫn duy trì tương thích với Producer, Consumer và dữ liệu lịch sử.
Các thay đổi thường an toàn hơn:
- Thêm field optional.
- Thêm field có default phù hợp.
- Bổ sung enum value khi Consumer xử lý unknown an toàn.
- Mở rộng kiểu theo quy tắc định dạng hỗ trợ.
Các thay đổi rủi ro:
- Xóa field bắt buộc.
- Đổi tên field.
- Đổi kiểu không tương thích.
- Thay đổi ý nghĩa nhưng giữ tên và kiểu.
- Đổi đơn vị tiền tệ hoặc đơn vị đo.
- Tái sử dụng field number Protobuf.
Thay đổi ý nghĩa nhưng không đổi schema là Semantic Breaking Change. Công cụ compatibility có thể không phát hiện. Đây là lý do Data Contract và review con người vẫn cần thiết.
11. Backward, Forward và Full Compatibility

Backward Compatibility nghĩa Consumer dùng schema mới có thể đọc dữ liệu được ghi bằng schema cũ. Nó phù hợp khi Consumer được nâng cấp trước hoặc cần đọc lịch sử.
Forward Compatibility nghĩa Consumer dùng schema cũ có thể đọc dữ liệu do Producer mới ghi. Nó hữu ích khi Producer được nâng cấp trước Consumer.
Full Compatibility yêu cầu cả hai chiều.
Compatibility có thể kiểm tra với phiên bản gần nhất hoặc theo dạng transitive với tất cả phiên bản trước. Transitive chặt hơn và phù hợp khi dữ liệu lịch sử lâu dài vẫn được replay.
Chọn Policy theo chiến lược deploy. Nếu nhiều Consumer độc lập nâng cấp chậm, chỉ kiểm tra với phiên bản gần nhất có thể không đủ.
12. Thiết kế schema sự kiện đơn hàng

Một schema tốt tách Envelope và dữ liệu nghiệp vụ:
{
"event_id": "evt-0001",
"event_type": "order_created",
"event_version": 1,
"event_time": "2026-09-18T08:30:15Z",
"source": "order-service",
"correlation_id": "req-8888",
"data": {
"order_id": "ORD-10001",
"customer_id": "CUS-001",
"total_amount_minor": 1250000,
"currency": "VND"
}
}
Tên total_amount_minor làm rõ số tiền được lưu theo đơn vị nhỏ nhất, tránh dùng float. Timestamp nên có timezone và quy ước ISO 8601 hoặc logical type rõ ràng.
Không nên đưa toàn bộ object database vào Event. Event chỉ chứa dữ liệu cần cho sự kiện và Consumer đã xác định, giúp giảm coupling với schema nội bộ của service.
13. Producer và Consumer với Avro

Pseudo code Producer Python:
from confluent_kafka import SerializingProducer
from confluent_kafka.schema_registry import SchemaRegistryClient
from confluent_kafka.schema_registry.avro import AvroSerializer
registry = SchemaRegistryClient({
"url": "http://localhost:8081"
})
serializer = AvroSerializer(
schema_registry_client=registry,
schema_str=order_schema,
)
producer = SerializingProducer({
"bootstrap.servers": "localhost:9092",
"value.serializer": serializer,
})
Consumer sử dụng AvroDeserializer và cùng Registry. Production cần TLS, authentication, timeout, cache và quyền riêng cho Registry.
Không nên cho mọi Producer tự động đăng ký bất kỳ schema nào trong Production. Một số tổ chức tắt Auto Register và yêu cầu schema đi qua CI/CD trước.
14. Quy trình thay đổi schema

Một quy trình an toàn:
- Producer owner tạo đề xuất schema.
- Hệ thống chạy lint và compatibility test.
- Consumer owner đánh giá Semantic Change.
- Schema được đăng ký ở môi trường phát triển.
- Contract Test chạy với dữ liệu cũ và mới.
- Consumer được triển khai trước nếu chiến lược yêu cầu.
- Producer mới được triển khai dần.
- Theo dõi lỗi deserialize và field usage.
- Field cũ chỉ bị xóa sau giai đoạn deprecation.
Thay đổi breaking có thể cần Topic phiên bản mới, chẳng hạn order-created-v2. Producer ghi song song trong giai đoạn migration, Consumer chuyển dần và Topic cũ chỉ ngừng sau khi không còn người dùng.
15. Schema Governance

Schema Registry không tự tạo Governance. Tổ chức cần quy định:
- Ai sở hữu Subject.
- Ai được đăng ký schema.
- Compatibility mặc định.
- Quy tắc đặt tên.
- Cách mô tả field.
- Cách phân loại PII.
- Thời gian deprecation.
- Cách theo dõi Consumer phụ thuộc.
- Quy trình xử lý breaking change.
Schema Catalog nên liên kết Topic, owner, Contract, lineage và hệ thống sử dụng. Khi một field thay đổi, đội phát triển cần biết Dashboard, Model hoặc API nào bị ảnh hưởng.
16. Những thay đổi nguy hiểm
Đổi tên field
Về kỹ thuật, đây thường là xóa field cũ và thêm field mới. Consumer cũ có thể không nhận dữ liệu.
Đổi int thành string
Consumer deserialize có thể lỗi. Hãy thêm field mới rồi migrate.
Thay đổi ý nghĩa
Giữ amount nhưng đổi từ VND sang USD là breaking change ngữ nghĩa dù schema không đổi.
Thêm field bắt buộc không có default
Consumer mới có thể không đọc được dữ liệu lịch sử.
Xóa enum value
Dữ liệu cũ vẫn có giá trị đó và có thể làm Consumer mới lỗi.
17. Kiểm thử Compatibility trong CI/CD
Pipeline CI nên:
- Validate cú pháp schema.
- Kiểm tra naming convention.
- So sánh schema với Registry.
- Chạy compatibility check.
- Sinh sample record.
- Kiểm tra Consumer deserialize dữ liệu cũ.
- Kiểm tra dữ liệu mới với Consumer phiên bản đang Production.
- Chặn PII không được phép.
Contract Test nên nằm cùng source code và chạy trước khi merge. Không đợi đến lúc Producer triển khai rồi mới phát hiện Registry từ chối schema.
18. Lỗi thường gặp
- Dùng JSON không schema vì cho rằng JSON tự mô tả.
- Chỉ kiểm tra kiểu dữ liệu mà bỏ qua ý nghĩa nghiệp vụ.
- Cho phép Producer tự đăng ký schema không kiểm soát.
- Đổi compatibility để triển khai gấp mà không đánh giá Consumer.
- Dùng float cho tiền.
- Xóa field ngay khi Producer không còn dùng.
- Không có owner cho Subject.
- Nhầm Schema Version với Event Version.
- Không bảo vệ Schema Registry bằng TLS và quyền truy cập.
19. Tổng kết
Schema giúp Producer và Consumer thống nhất cấu trúc Event. Schema Registry quản lý phiên bản, Schema ID và Compatibility Policy, nhưng Data Contract cùng Governance mới bảo vệ đầy đủ ý nghĩa dữ liệu.
Các điểm cần nhớ:
- Kafka Broker lưu byte và không tự kiểm tra payload.
- Avro phù hợp Data Platform và Schema Evolution.
- Protobuf có code generation mạnh.
- JSON Schema giữ payload dễ đọc.
- Backward và Forward phụ thuộc hướng nâng cấp Producer, Consumer.
- Semantic Breaking Change có thể vượt qua kiểm tra kỹ thuật.
- Schema thay đổi cần CI/CD, Contract Test và owner phê duyệt.
- Breaking Change lớn nên cân nhắc Topic phiên bản mới.
Bài tiếp theo sẽ xử lý Event theo thời gian thực bằng Kafka Streams với KStream, KTable, Window, Join và State Store.
20. Câu hỏi ôn tập
- Vì sao JSON vẫn cần schema?
- Schema ID và Schema Version khác nhau thế nào?
- Writer Schema và Reader Schema có vai trò gì trong Avro?
- Vì sao không được tái sử dụng field number Protobuf?
- Backward Compatibility hỗ trợ hướng nâng cấp nào?
- Semantic Breaking Change là gì?
- Khi nào nên tạo Topic phiên bản mới?
- CI/CD cần kiểm tra schema như thế nào?
21. Tài liệu tham khảo
- Apache Avro Specification: https://avro.apache.org/docs/current/specification/
- Protocol Buffers Documentation: https://protobuf.dev/programming-guides/proto3/
- JSON Schema Specification: https://json-schema.org/specification
- Confluent Schema Registry: https://docs.confluent.io/platform/current/schema-registry/index.html
- Confluent Schema Evolution and Compatibility: https://docs.confluent.io/platform/current/schema-registry/fundamentals/schema-evolution.html
