# Chương 2: Hướng dẫn phát triển (Developer)

Chương này mô tả cấu trúc source code, quy ước coding, cách tạo module mới, cách viết API/UI, quy trình commit/branch, và cách chạy/debug dự án trong quá trình phát triển. Mục tiêu giúp developer hiểu kiến trúc và đóng góp code đúng chuẩn.

# Luồng Xử Lý Dịch Vụ API Tích Hợp Kafka (Kafka API Processing Flow)

## 1.1. Mục Tiêu

Tài liệu này mô tả luồng xử lý chuẩn của dịch vụ **Loadbalance\_kafka** trên nền tảng **WSO2 Micro Integrator (WSO2 MI)**, từ khi Client gửi request đến khi dữ liệu được xử lý và phản hồi về. Mục tiêu:

- Thống nhất cách hiểu kiến trúc giữa các thành viên
- Đảm bảo đúng luồng dữ liệu qua từng lớp Kafka
- Dễ dàng trace lỗi và mở rộng sau này

---

## 1.2. Phạm Vi Áp Dụng

- Dự án: `Loadbalance_kafka` (WSO2 MI v4.4.0)
- Ngôn ngữ: Synapse XML (WSO2 Mediation)
- Các component: API, Inbound Endpoint, Sequence, Local Entry
- Kafka Connector: `mi-inbound-kafka` v2.0.6, `mi-connector-kafka` v3.3.10

---

## 1.3. Tổng Quan Luồng Xử Lý

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-client-%28http-post%29-%E2%86%93" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">  
</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">Client (HTTP POST)</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1">KafkaProducerApi (/kafka-producer)</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1">kafkaTransport.init (KafkaConnection)</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1">Kafka Topic: test\_topic\_01</span></div></div><div class="code-line" data-line-end="8" data-line-number="8" data-line-start="8"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="9" data-line-number="9" data-line-start="9"><div class="line-content"><span class="mtk1">Inbound Endpoint (KafkaMessageConsumer)</span></div></div><div class="code-line" data-line-end="10" data-line-number="10" data-line-start="10"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="11" data-line-number="11" data-line-start="11"><div class="line-content"><span class="mtk1">inboundSequence (Load\_balance\_example-inboundSequence)</span></div></div><div class="code-line" data-line-end="12" data-line-number="12" data-line-start="12"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="13" data-line-number="13" data-line-start="13"><div class="line-content"><span class="mtk1">Backend API (POST http://192.168.0.133:8080/api/v1/plannings)</span></div></div><div class="code-line" data-line-end="14" data-line-number="14" data-line-start="14"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="15" data-line-number="15" data-line-start="15"><div class="line-content"><span class="mtk1">payloadFactory (Đóng gói lại kết quả)</span></div></div><div class="code-line" data-line-end="16" data-line-number="16" data-line-start="16"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="17" data-line-number="17" data-line-start="17"><div class="line-content"><span class="mtk1">Kafka Topic: processed\_topic</span></div></div></div></div></div></div>> 📸 **\[Ảnh minh họa\]** — Chụp màn hình sơ đồ luồng WSO2 MI Studio hoặc Kafka Manager

---

## 1.4. Các Bước Xử Lý Chi Tiết

---

### Bước 1. Client Gửi Request

Client gọi API qua HTTP:

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-http-post-http%3A%2F%2F%3Cws" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">http</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">POST http://&lt;wso2mi-host&gt;:8290/kafka-producer</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1">Content-Type: application/json</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1">Authorization: Bearer &lt;jwt\_token&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content">  
</div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1">{</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> "field1": "value1",</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1"> "field2": "value2"</span></div></div><div class="code-line" data-line-end="8" data-line-number="8" data-line-start="8"><div class="line-content"><span class="mtk1">}</span></div></div></div></div></div></div>**Quy định:**

- Phương thức bắt buộc: `POST`
- Content-Type: `application/json`
- Payload là JSON tuỳ ý — không bị validate tại lớp này

> 📸 **\[Ảnh minh họa\]** — Ảnh chụp Postman/Curl gửi request thành công

---

### Bước 2. KafkaProducerApi Nhận Request

**File:**

<div id="bkmrk--6">  
</div><span class="inline-flex break-all">KafkaProducerApi.xml</span>

API nhận request và bắt đầu luồng xử lý `inSequence`.

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Capi-context%3D%22%2Fk" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">api</span> <span class="mtk5 mtki">context</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">/kafka-producer</span><span class="mtk11">"</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">KafkaProducerApi</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">resource</span> <span class="mtk5 mtki">methods</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">POST</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">inSequence</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk4">&lt;!-- Log request vào --&gt;</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">log</span> <span class="mtk5 mtki">level</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">custom</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">API\_NAME</span><span class="mtk11">"</span> <span class="mtk5 mtki">value</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">KafkaProducerApi</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">STATUS</span><span class="mtk11">"</span> <span class="mtk5 mtki">value</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Processing incoming message...</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="8" data-line-number="8" data-line-start="8"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">PAYLOAD</span><span class="mtk11">"</span> <span class="mtk5 mtki">expression</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">json-eval($)</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="9" data-line-number="9" data-line-start="9"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">log</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="10" data-line-number="10" data-line-start="10"><div class="line-content"><span class="mtk1"> ...</span></div></div><div class="code-line" data-line-end="11" data-line-number="11" data-line-start="11"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">inSequence</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="12" data-line-number="12" data-line-start="12"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">faultSequence</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="13" data-line-number="13" data-line-start="13"><div class="line-content"><span class="mtk4">&lt;!-- Trả lỗi nếu Kafka fail --&gt;</span></div></div><div class="code-line" data-line-end="14" data-line-number="14" data-line-start="14"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">faultSequence</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="15" data-line-number="15" data-line-start="15"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">resource</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="16" data-line-number="16" data-line-start="16"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">api</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Log ghi nhận:**

- `API_NAME`: Tên API xác định nguồn log
- `STATUS`: Trạng thái hiện tại của luồng
- `PAYLOAD`: Toàn bộ JSON body của request

> 📸 **\[Ảnh minh họa\]** — Ảnh log WSO2 MI Console hoặc file log khi request vào

---

### Bước 3. Khởi Tạo Kết Nối Kafka (kafkaTransport.init)

**File:**

<div id="bkmrk--8">  
</div><span class="inline-flex break-all">KafkaProducerApi.xml</span> (trong `inSequence`)

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Ckafkatransport." node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">kafkaTransport.init</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">name</span><span class="mtk1">&gt;KafkaConnection&lt;/</span><span class="mtk10">name</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">bootstrapServers</span><span class="mtk1">&gt;hdp-master:9092&lt;/</span><span class="mtk10">bootstrapServers</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">keySerializerClass</span><span class="mtk1">&gt;org.apache.kafka.common.serialization.StringSerializer&lt;/</span><span class="mtk10">keySerializerClass</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">valueSerializerClass</span><span class="mtk1">&gt;org.apache.kafka.common.serialization.StringSerializer&lt;/</span><span class="mtk10">valueSerializerClass</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">acks</span><span class="mtk1">&gt;all&lt;/</span><span class="mtk10">acks</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">requestTimeout</span><span class="mtk1">&gt;10000&lt;/</span><span class="mtk10">requestTimeout</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="8" data-line-number="8" data-line-start="8"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">maxBlock</span><span class="mtk1">&gt;5000&lt;/</span><span class="mtk10">maxBlock</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="9" data-line-number="9" data-line-start="9"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">kafkaTransport.init</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Giải thích từng trường:**

<div class="my-4" id="bkmrk-tr%C6%B0%E1%BB%9Dng-gi%C3%A1-tr%E1%BB%8B-m%C3%B4-t%E1%BA%A3"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Trường</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`name`</td><td class="px-6 py-2">`KafkaConnection`</td><td class="px-6 py-2">Tên kết nối dùng để Sequence tham chiếu (`configKey`).</td></tr><tr><td class="px-6 py-2">`bootstrapServers`</td><td class="px-6 py-2">`hdp-master:9092`</td><td class="px-6 py-2">Địa chỉ Kafka Broker.</td></tr><tr><td class="px-6 py-2">`keySerializerClass`</td><td class="px-6 py-2">`StringSerializer`</td><td class="px-6 py-2">Class serialize Key của message (kiểu chuỗi).</td></tr><tr><td class="px-6 py-2">`valueSerializerClass`</td><td class="px-6 py-2">`StringSerializer`</td><td class="px-6 py-2">Class serialize Value của message (kiểu chuỗi).</td></tr><tr><td class="px-6 py-2">`acks`</td><td class="px-6 py-2">`all`</td><td class="px-6 py-2">Broker phải xác nhận ghi đủ trên mọi replica mới coi là thành công.</td></tr><tr><td class="px-6 py-2">`requestTimeout`</td><td class="px-6 py-2">`10000`</td><td class="px-6 py-2">Timeout cho mỗi request gửi lên Kafka (ms).</td></tr><tr><td class="px-6 py-2">`maxBlock`</td><td class="px-6 py-2">`5000`</td><td class="px-6 py-2">Thời gian tối đa chờ nếu buffer đầy hoặc Kafka chưa sẵn sàng (ms).</td></tr></tbody></table>

</div>---

### Bước 4. Publish Message vào Kafka

**File:**

<div id="bkmrk--10">  
</div><span class="inline-flex break-all">KafkaProducerApi.xml</span>

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Ckafkatransport.-1" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">kafkaTransport.publishMessages</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">topic</span><span class="mtk1">&gt;test\_topic\_01&lt;/</span><span class="mtk10">topic</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">kafkaTransport.publishMessages</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Giải thích:**

- `topic`: `test_topic_01` — Topic nhận dữ liệu đầu vào.
- Nội dung message là toàn bộ `payload` nhận từ Client.
- Không có key, partition cụ thể → Kafka tự điều phối vào các partition.

---

### Bước 5. Trả Response Cho Client (Producer)

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Cpayloadfactory-" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">payloadFactory</span> <span class="mtk5 mtki">media-type</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">json</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">format</span><span class="mtk1">&gt;{"success": "true", "message": "Hồ sơ đã được gửi thành công"}&lt;/</span><span class="mtk10">format</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">payloadFactory</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">respond</span><span class="mtk1">/&gt;</span></div></div></div></div></div></div>Sau khi publish xong, API trả ngay về Client mà không chờ xử lý phía Consumer.

**Response thành công:**

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-json-%7B-%22success%22%3A-%22t" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">json</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">{</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk12">"</span><span class="mtk9">success</span><span class="mtk12">"</span><span class="mtk10">:</span> <span class="mtk11">"</span><span class="mtk8">true</span><span class="mtk11">"</span><span class="mtk1">,</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk12">"</span><span class="mtk9">message</span><span class="mtk12">"</span><span class="mtk10">:</span> <span class="mtk11">"</span><span class="mtk8">Hồ sơ đã được gửi thành công</span><span class="mtk11">"</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1">}</span></div></div></div></div></div></div>**Response thất bại (faultSequence):**

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-json-%7B-%22status%22%3A-%22fa" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">json</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">{</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk12">"</span><span class="mtk9">status</span><span class="mtk12">"</span><span class="mtk10">:</span> <span class="mtk11">"</span><span class="mtk8">Failed</span><span class="mtk11">"</span><span class="mtk1">,</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk12">"</span><span class="mtk9">error</span><span class="mtk12">"</span><span class="mtk10">:</span> <span class="mtk11">"</span><span class="mtk8">&lt;error\_message&gt;</span><span class="mtk11">"</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1">}</span></div></div></div></div></div></div>---

### Bước 6. Inbound Endpoint Lắng Nghe Kafka

**File:**

<div id="bkmrk--13">  
</div><span class="inline-flex break-all">Load\_balance\_example.xml</span>

Đây là thành phần chạy ngầm liên tục, tự động poll message từ Kafka.

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Cinboundendpoint" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">inboundEndpoint</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Load\_balance\_example</span><span class="mtk11">"</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk5 mtki">class</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">org.wso2.carbon.inbound.kafka.KafkaMessageConsumer</span><span class="mtk11">"</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk5 mtki">sequence</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Load\_balance\_example-inboundSequence</span><span class="mtk11">"</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk5 mtki">onError</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Load\_balance\_example-inboundErrorSequence</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Toàn bộ tham số cấu hình:**

<div class="my-4" id="bkmrk-tham-s%E1%BB%91-gi%C3%A1-tr%E1%BB%8B-m%C3%B4-t"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Tham số</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`interval`</td><td class="px-6 py-2">`100`</td><td class="px-6 py-2">Khoảng cách giữa 2 lần poll (ms).</td></tr><tr><td class="px-6 py-2">`sequential`</td><td class="px-6 py-2">`false`</td><td class="px-6 py-2">`false` = đa luồng, tận dụng tối đa CPU.</td></tr><tr><td class="px-6 py-2">`coordination`</td><td class="px-6 py-2">`true`</td><td class="px-6 py-2">Tránh consume trùng khi chạy cluster nhiều node.</td></tr><tr><td class="px-6 py-2">`suspend`</td><td class="px-6 py-2">`false`</td><td class="px-6 py-2">`false` = khởi động ngay, không dừng.</td></tr><tr><td class="px-6 py-2">`bootstrap.servers`</td><td class="px-6 py-2">`hdp-master:9092`</td><td class="px-6 py-2">Địa chỉ Kafka Broker kết nối đến.</td></tr><tr><td class="px-6 py-2">`topic.name`</td><td class="px-6 py-2">`test_topic_01`</td><td class="px-6 py-2">Topic mà Inbound Endpoint theo dõi.</td></tr><tr><td class="px-6 py-2">`group.id`</td><td class="px-6 py-2">`group1`</td><td class="px-6 py-2">Tên Consumer Group. Nhiều node cùng group sẽ chia partition.</td></tr><tr><td class="px-6 py-2">`contentType`</td><td class="px-6 py-2">`application/json`</td><td class="px-6 py-2">Định dạng nội dung message nhận về.</td></tr><tr><td class="px-6 py-2">`poll.timeout`</td><td class="px-6 py-2">`5000`</td><td class="px-6 py-2">Thời gian chờ khi không có dữ liệu (ms).</td></tr><tr><td class="px-6 py-2">`key.deserializer`</td><td class="px-6 py-2">`StringDeserializer`</td><td class="px-6 py-2">Class giải mã Key của message.</td></tr><tr><td class="px-6 py-2">`value.deserializer`</td><td class="px-6 py-2">`StringDeserializer`</td><td class="px-6 py-2">Class giải mã Value của message.</td></tr><tr><td class="px-6 py-2">`avro.use.logical.type.converters`</td><td class="px-6 py-2">`false`</td><td class="px-6 py-2">Không dùng Avro logical type.</td></tr><tr><td class="px-6 py-2">`enable.auto.commit`</td><td class="px-6 py-2">`true`</td><td class="px-6 py-2">Tự commit offset sau khi đọc xong.</td></tr><tr><td class="px-6 py-2">`auto.commit.interval.ms`</td><td class="px-6 py-2">`5000`</td><td class="px-6 py-2">Chu kỳ tự commit offset (ms).</td></tr><tr><td class="px-6 py-2">`auto.offset.reset`</td><td class="px-6 py-2">`latest`</td><td class="px-6 py-2">Bắt đầu đọc từ message mới nhất nếu không có offset cũ.</td></tr><tr><td class="px-6 py-2">`exclude.internal.topics`</td><td class="px-6 py-2">`true`</td><td class="px-6 py-2">Bỏ qua các topic nội bộ của Kafka.</td></tr><tr><td class="px-6 py-2">`check.crcs`</td><td class="px-6 py-2">`true`</td><td class="px-6 py-2">Kiểm tra tính toàn vẹn dữ liệu (CRC).</td></tr><tr><td class="px-6 py-2">`partition.assignment.strategy`</td><td class="px-6 py-2">`RangeAssignor`</td><td class="px-6 py-2">Chiến lược phân chia partition cho consumer.</td></tr><tr><td class="px-6 py-2">`max.poll.interval.ms`</td><td class="px-6 py-2">`300000`</td><td class="px-6 py-2">Thời gian tối đa xử lý một đợt poll (5 phút).</td></tr><tr><td class="px-6 py-2">`max.poll.records`</td><td class="px-6 py-2">`500`</td><td class="px-6 py-2">Số message tối đa lấy về mỗi đợt poll.</td></tr><tr><td class="px-6 py-2">`fetch.max.wait.ms`</td><td class="px-6 py-2">`500`</td><td class="px-6 py-2">Thời gian chờ tối đa fetch từ broker nếu chưa đủ data (ms).</td></tr><tr><td class="px-6 py-2">`receive.buffer.bytes`</td><td class="px-6 py-2">`65536`</td><td class="px-6 py-2">Kích thước buffer nhận dữ liệu TCP (64KB).</td></tr><tr><td class="px-6 py-2">`send.buffer.bytes`</td><td class="px-6 py-2">`131072`</td><td class="px-6 py-2">Kích thước buffer gửi dữ liệu TCP (128KB).</td></tr><tr><td class="px-6 py-2">`request.timeout.ms`</td><td class="px-6 py-2">`305000`</td><td class="px-6 py-2">Timeout cho mỗi request gửi đến Broker (ms).</td></tr><tr><td class="px-6 py-2">`reconnect.backoff.ms`</td><td class="px-6 py-2">`50`</td><td class="px-6 py-2">Thời gian chờ trước khi kết nối lại sau lỗi (ms).</td></tr><tr><td class="px-6 py-2">`retry.backoff.ms`</td><td class="px-6 py-2">`100`</td><td class="px-6 py-2">Thời gian chờ trước khi thử lại request thất bại (ms).</td></tr><tr><td class="px-6 py-2">`connections.max.idle.ms`</td><td class="px-6 py-2">`540000`</td><td class="px-6 py-2">Đóng kết nối nếu idle quá thời gian này (9 phút).</td></tr><tr><td class="px-6 py-2">`security.protocol`</td><td class="px-6 py-2">`PLAINTEXT`</td><td class="px-6 py-2">Giao thức bảo mật (tắt mã hóa).</td></tr><tr><td class="px-6 py-2">`metrics.num.samples`</td><td class="px-6 py-2">`2`</td><td class="px-6 py-2">Số mẫu dùng để tính metrics.</td></tr><tr><td class="px-6 py-2">`metrics.recording.level`</td><td class="px-6 py-2">`INFO`</td><td class="px-6 py-2">Mức độ ghi metrics.</td></tr><tr><td class="px-6 py-2">`metrics.sample.window.ms`</td><td class="px-6 py-2">`30000`</td><td class="px-6 py-2">Cửa sổ thời gian lấy mẫu metrics (30 giây).</td></tr></tbody></table>

</div>---

### Bước 7. Sequence Xử Lý Message Nhận Được

**File:**

<div id="bkmrk--15">  
</div><span class="inline-flex break-all">Load\_balance\_example-inboundSequence.xml</span>

#### 7.1. Lấy Payload từ Kafka

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Cproperty-name%3D%22" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">PAYLOAD</span><span class="mtk11">"</span> <span class="mtk5 mtki">expression</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">json-eval($)</span><span class="mtk11">"</span><span class="mtk1"> /&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">log</span> <span class="mtk5 mtki">level</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">custom</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">STATUS</span><span class="mtk11">"</span> <span class="mtk5 mtki">value</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">\[KafkaConsumer\] Message du lieu nhan duoc</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">TOPIC</span><span class="mtk11">"</span> <span class="mtk5 mtki">value</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">test\_topic\_01</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">PAYLOAD</span><span class="mtk11">"</span> <span class="mtk5 mtki">expression</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">get-property('PAYLOAD')</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">log</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Các property được tạo:**

<div class="my-4" id="bkmrk-property-gi%C3%A1-tr%E1%BB%8B-%2F-e"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Property</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị / Expression</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`PAYLOAD`</td><td class="px-6 py-2">`json-eval($)`</td><td class="px-6 py-2">Nội dung JSON toàn bộ của message từ Kafka.</td></tr></tbody></table>

</div>#### 7.2. Gọi Backend API

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Cheader-name%3D%22au" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">header</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Authorization</span><span class="mtk11">"</span> <span class="mtk5 mtki">scope</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">transport</span><span class="mtk11">"</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk5 mtki">value</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Bearer eyJhbGciOiJIUzI1NiJ9...</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Content-Type</span><span class="mtk11">"</span> <span class="mtk5 mtki">value</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">application/json</span><span class="mtk11">"</span> <span class="mtk5 mtki">scope</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">transport</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content">  
</div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">call</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">endpoint</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">address</span> <span class="mtk5 mtki">uri</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">http://192.168.0.133:8080/api/v1/plannings</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="8" data-line-number="8" data-line-start="8"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">suspendOnFailure</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="9" data-line-number="9" data-line-start="9"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">initialDuration</span><span class="mtk1">&gt;1000&lt;/</span><span class="mtk10">initialDuration</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="10" data-line-number="10" data-line-start="10"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">progressionFactor</span><span class="mtk1">&gt;1.0&lt;/</span><span class="mtk10">progressionFactor</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="11" data-line-number="11" data-line-start="11"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">maximumDuration</span><span class="mtk1">&gt;60000&lt;/</span><span class="mtk10">maximumDuration</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="12" data-line-number="12" data-line-start="12"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">suspendOnFailure</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="13" data-line-number="13" data-line-start="13"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">address</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="14" data-line-number="14" data-line-start="14"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">endpoint</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="15" data-line-number="15" data-line-start="15"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">call</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Thông tin endpoint:**

<div class="my-4" id="bkmrk-tr%C6%B0%E1%BB%9Dng-gi%C3%A1-tr%E1%BB%8B-m%C3%B4-t%E1%BA%A3-1"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Trường</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`uri`</td><td class="px-6 py-2">`http://192.168.0.133:8080/api/v1/plannings`</td><td class="px-6 py-2">URL Backend API nhận dữ liệu.</td></tr><tr><td class="px-6 py-2">`Authorization`</td><td class="px-6 py-2">`Bearer <jwt_token>`</td><td class="px-6 py-2">Token xác thực gửi kèm header.</td></tr><tr><td class="px-6 py-2">`Content-Type`</td><td class="px-6 py-2">`application/json`</td><td class="px-6 py-2">Định dạng body gửi đến Backend.</td></tr><tr><td class="px-6 py-2">`initialDuration`</td><td class="px-6 py-2">`1000`</td><td class="px-6 py-2">Chờ 1 giây trước khi retry khi lỗi (ms).</td></tr><tr><td class="px-6 py-2">`progressionFactor`</td><td class="px-6 py-2">`1.0`</td><td class="px-6 py-2">Hệ số tăng thời gian retry (1.0 = không tăng).</td></tr><tr><td class="px-6 py-2">`maximumDuration`</td><td class="px-6 py-2">`60000`</td><td class="px-6 py-2">Thời gian chờ tối đa khi retry (60 giây).</td></tr></tbody></table>

</div>#### 7.3. Lưu Response từ Backend

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Cproperty-name%3D%22-1" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">API\_RESPONSE</span><span class="mtk11">"</span> <span class="mtk5 mtki">expression</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">json-eval($)</span><span class="mtk11">"</span> <span class="mtk5 mtki">scope</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">default</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">property</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">HTTP\_STATUS</span><span class="mtk11">"</span> <span class="mtk5 mtki">expression</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">$axis2:HTTP\_SC</span><span class="mtk11">"</span> <span class="mtk5 mtki">scope</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">default</span><span class="mtk11">"</span><span class="mtk1">/&gt;</span></div></div></div></div></div></div>**Các property lưu kết quả:**

<div class="my-4" id="bkmrk-property-expression-"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Property</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Expression</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`API_RESPONSE`</td><td class="px-6 py-2">`json-eval($)`</td><td class="px-6 py-2">Toàn bộ JSON response từ Backend API.</td></tr><tr><td class="px-6 py-2">`HTTP_STATUS`</td><td class="px-6 py-2">`$axis2:HTTP_SC`</td><td class="px-6 py-2">Mã HTTP status code (200, 500...) của Backend.</td></tr><tr><td class="px-6 py-2">`RESPONSE_SIZE`</td><td class="px-6 py-2">`fn:string-length(...)`</td><td class="px-6 py-2">Độ dài chuỗi JSON của response (dùng để log).</td></tr></tbody></table>

</div>#### 7.4. Đóng Gói Lại Dữ Liệu (payloadFactory)

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Cpayloadfactory--1" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">payloadFactory</span> <span class="mtk5 mtki">media-type</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">json</span><span class="mtk11">"</span> <span class="mtk5 mtki">template-type</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">default</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">format</span><span class="mtk1">&gt;{</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1"> "eventType": "transaction",</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> "source": "bank-transactions",</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1"> "data": ${payload}</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> }&lt;/</span><span class="mtk10">format</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">payloadFactory</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Cấu trúc JSON đầu ra:**

<div class="my-4" id="bkmrk-tr%C6%B0%E1%BB%9Dng-gi%C3%A1-tr%E1%BB%8B-ki%E1%BB%83u-"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Trường</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Kiểu</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`eventType`</td><td class="px-6 py-2">`transaction`</td><td class="px-6 py-2">`String` (cố định)</td><td class="px-6 py-2">Phân loại sự kiện.</td></tr><tr><td class="px-6 py-2">`source`</td><td class="px-6 py-2">`bank-transactions`</td><td class="px-6 py-2">`String` (cố định)</td><td class="px-6 py-2">Nguồn gốc dữ liệu.</td></tr><tr><td class="px-6 py-2">`data`</td><td class="px-6 py-2">`${payload}`</td><td class="px-6 py-2">`Object` (JSON động)</td><td class="px-6 py-2">Toàn bộ message gốc nhận từ Kafka.</td></tr></tbody></table>

</div>#### 7.5. Publish Kết Quả vào Topic Phản Hồi

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Ckafkatransport.-2" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">kafkaTransport.publishMessages</span> <span class="mtk5 mtki">configKey</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">KafkaConnection</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">topic</span><span class="mtk1">&gt;processed\_topic&lt;/</span><span class="mtk10">topic</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">partitionNo</span><span class="mtk1">&gt;0&lt;/</span><span class="mtk10">partitionNo</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">forwardExistingHeaders</span><span class="mtk1">&gt;None&lt;/</span><span class="mtk10">forwardExistingHeaders</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">customHeaders</span><span class="mtk1">&gt;\[\]&lt;/</span><span class="mtk10">customHeaders</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">kafkaTransport.publishMessages</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Các trường publish:**

<div class="my-4" id="bkmrk-tr%C6%B0%E1%BB%9Dng-gi%C3%A1-tr%E1%BB%8B-m%C3%B4-t%E1%BA%A3-2"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Trường</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`configKey`</td><td class="px-6 py-2">`KafkaConnection`</td><td class="px-6 py-2">Tham chiếu đến Local Entry kết nối Kafka.</td></tr><tr><td class="px-6 py-2">`topic`</td><td class="px-6 py-2">`processed_topic`</td><td class="px-6 py-2">Topic nhận kết quả đã xử lý.</td></tr><tr><td class="px-6 py-2">`partitionNo`</td><td class="px-6 py-2">`0`</td><td class="px-6 py-2">Partition cố định (0 = partition đầu tiên).</td></tr><tr><td class="px-6 py-2">`forwardExistingHeaders`</td><td class="px-6 py-2">`None`</td><td class="px-6 py-2">Không chuyển tiếp header gốc.</td></tr><tr><td class="px-6 py-2">`customHeaders`</td><td class="px-6 py-2">`[]`</td><td class="px-6 py-2">Không thêm header tuỳ chỉnh.</td></tr></tbody></table>

</div>---

### Bước 8. Local Entry - Cấu Hình Kết Nối Kafka Tái Sử Dụng

**File:**

<div id="bkmrk--17">  
</div><span class="inline-flex break-all">KafkaConnection.xml</span>

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Clocalentry-key%3D" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">localEntry</span> <span class="mtk5 mtki">key</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">KafkaConnection</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">kafkaTransport.init</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">connectionType</span><span class="mtk1">&gt;KAFKA&lt;/</span><span class="mtk10">connectionType</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">bootstrapServers</span><span class="mtk1">&gt;hdp-master:9092&lt;/</span><span class="mtk10">bootstrapServers</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">keySerializerClass</span><span class="mtk1">&gt;...StringSerializer&lt;/</span><span class="mtk10">keySerializerClass</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">valueSerializerClass</span><span class="mtk1">&gt;...StringSerializer&lt;/</span><span class="mtk10">valueSerializerClass</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">poolingEnabled</span><span class="mtk1">&gt;false&lt;/</span><span class="mtk10">poolingEnabled</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="8" data-line-number="8" data-line-start="8"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">name</span><span class="mtk1">&gt;KafkaConnection&lt;/</span><span class="mtk10">name</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="9" data-line-number="9" data-line-start="9"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">kafkaTransport.init</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="10" data-line-number="10" data-line-start="10"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">localEntry</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>**Giải thích:**

<div class="my-4" id="bkmrk-tr%C6%B0%E1%BB%9Dng-gi%C3%A1-tr%E1%BB%8B-m%C3%B4-t%E1%BA%A3-3"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Trường</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`key`</td><td class="px-6 py-2">`KafkaConnection`</td><td class="px-6 py-2">Tên Local Entry dùng để gọi qua `configKey`.</td></tr><tr><td class="px-6 py-2">`connectionType`</td><td class="px-6 py-2">`KAFKA`</td><td class="px-6 py-2">Loại kết nối.</td></tr><tr><td class="px-6 py-2">`bootstrapServers`</td><td class="px-6 py-2">`hdp-master:9092`</td><td class="px-6 py-2">Kafka Broker.</td></tr><tr><td class="px-6 py-2">`poolingEnabled`</td><td class="px-6 py-2">`false`</td><td class="px-6 py-2">Không dùng connection pool.</td></tr></tbody></table>

</div>---

### Bước 9. Xử Lý Lỗi (Error Sequence)

**File:**

<div id="bkmrk--19">  
</div><span class="inline-flex break-all">Load\_balance\_example-inboundErrorSequence.xml</span>

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-xml-%3Csequence-name%3D%22" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">xml</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">&lt;</span><span class="mtk10">sequence</span> <span class="mtk5 mtki">name</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">Load\_balance\_example-inboundErrorSequence</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">log</span> <span class="mtk5 mtki">category</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">INFO</span><span class="mtk11">"</span> <span class="mtk5 mtki">logMessageID</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">false</span><span class="mtk11">"</span> <span class="mtk5 mtki">logFullPayload</span><span class="mtk1">=</span><span class="mtk11">"</span><span class="mtk8">false</span><span class="mtk11">"</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1"> &lt;</span><span class="mtk10">message</span><span class="mtk1">&gt;Lỗi khi lắng nghe lấy dữ liệu từ kafka&lt;/</span><span class="mtk10">message</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> &lt;/</span><span class="mtk10">log</span><span class="mtk1">&gt;</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1">&lt;/</span><span class="mtk10">sequence</span><span class="mtk1">&gt;</span></div></div></div></div></div></div>Khi Inbound Endpoint bị lỗi (không parse được message, mất kết nối...), WSO2 MI sẽ kích hoạt sequence lỗi này.

> ⚠️ **Ghi chú**: Hiện tại sequence lỗi chỉ log thông báo. Cần bổ sung logic retry hoặc dead-letter queue cho production.

---

## 1.5. Luồng Phát Triển &amp; Deploy

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-dev-code-xml-%E2%86%92-build" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">  
</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">Dev Code XML → Build CAR → Deploy lên WSO2 MI</span></div></div></div></div></div></div>### Bước 10. Build Project

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-bash-mvn-clean-insta" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">bash</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk5">mvn</span> <span class="mtk8">clean</span> <span class="mtk8">install</span></div></div></div></div></div></div>Output: File `Loadbalance_kafka_1.0.0.car` trong thư mục `target/`.

### Bước 11. Deploy lên WSO2 MI Server

Tự động qua Maven:

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-bash-mvn-deploy" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">bash</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk5">mvn</span> <span class="mtk8">deploy</span></div></div></div></div></div></div>Hoặc copy file `.car` vào thư mục `<WSO2MI_HOME>/repository/deployment/server/carbonapps/`.

**Cấu hình server deploy (trong**

<div id="bkmrk--21">  
</div>**<span class="inline-flex break-all">pom.xml</span>):**

<div class="my-4" id="bkmrk-tr%C6%B0%E1%BB%9Dng-gi%C3%A1-tr%E1%BB%8B-m%C3%B4-t%E1%BA%A3-4"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Trường</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`serverUrl`</td><td class="px-6 py-2">`http://192.168.0.167:9201`</td><td class="px-6 py-2">địa chỉ WSO2 MI Management API.</td></tr><tr><td class="px-6 py-2">`userName`</td><td class="px-6 py-2">`adminvp`</td><td class="px-6 py-2">Tài khoản admin MI.</td></tr><tr><td class="px-6 py-2">`serverType`</td><td class="px-6 py-2">`mi`</td><td class="px-6 py-2">Loại server WSO2.</td></tr><tr><td class="px-6 py-2">`operation`</td><td class="px-6 py-2">`deploy`</td><td class="px-6 py-2">Hành động thực hiện.</td></tr></tbody></table>

</div>> 📸 **\[Ảnh minh họa\]** — Ảnh chụp Deploy thành công trong WSO2 MI Dashboard

---

## 1.6. Sơ Đồ Tổng Thể API Lifecycle

<div class="relative whitespace-pre-wrap word-break-all my-2 rounded-lg bg-list-hover-subtle border border-gray-500/20" id="bkmrk-client-post-request-" node="[object Object]"><div class="min-h-7 relative box-border flex flex-row items-center justify-between rounded-t border-b border-gray-500/20 px-2 py-0.5"><div class="font-sans text-sm text-ide-text-color opacity-60">  
</div><div class="flex flex-row gap-2 justify-end"></div></div><div class="p-3"><div class="w-full h-full text-xs cursor-text"><div class="code-block"><div class="code-line" data-line-end="1" data-line-number="1" data-line-start="1"><div class="line-content"><span class="mtk1">Client POST Request</span></div></div><div class="code-line" data-line-end="2" data-line-number="2" data-line-start="2"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="3" data-line-number="3" data-line-start="3"><div class="line-content"><span class="mtk1">KafkaProducerApi (context: /kafka-producer)</span></div></div><div class="code-line" data-line-end="4" data-line-number="4" data-line-start="4"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="5" data-line-number="5" data-line-start="5"><div class="line-content"><span class="mtk1">kafkaTransport.init (KafkaConnection Local Entry)</span></div></div><div class="code-line" data-line-end="6" data-line-number="6" data-line-start="6"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="7" data-line-number="7" data-line-start="7"><div class="line-content"><span class="mtk1">Publish → Kafka: test\_topic\_01</span></div></div><div class="code-line" data-line-end="8" data-line-number="8" data-line-start="8"><div class="line-content"><span class="mtk1"> ↓ ↓ (response lại Client ngay)</span></div></div><div class="code-line" data-line-end="9" data-line-number="9" data-line-start="9"><div class="line-content"><span class="mtk1"> ↓ {"success": "true", ...}</span></div></div><div class="code-line" data-line-end="10" data-line-number="10" data-line-start="10"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="11" data-line-number="11" data-line-start="11"><div class="line-content"><span class="mtk1">Inbound Endpoint (poll interval: 100ms)</span></div></div><div class="code-line" data-line-end="12" data-line-number="12" data-line-start="12"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="13" data-line-number="13" data-line-start="13"><div class="line-content"><span class="mtk1">inboundSequence</span></div></div><div class="code-line" data-line-end="14" data-line-number="14" data-line-start="14"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="15" data-line-number="15" data-line-start="15"><div class="line-content"><span class="mtk1">Call Backend: POST /api/v1/plannings</span></div></div><div class="code-line" data-line-end="16" data-line-number="16" data-line-start="16"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="17" data-line-number="17" data-line-start="17"><div class="line-content"><span class="mtk1">payloadFactory: {eventType, source, data}</span></div></div><div class="code-line" data-line-end="18" data-line-number="18" data-line-start="18"><div class="line-content"><span class="mtk1"> ↓</span></div></div><div class="code-line" data-line-end="19" data-line-number="19" data-line-start="19"><div class="line-content"><span class="mtk1">Publish → Kafka: processed\_topic</span></div></div><div class="code-line" data-line-end="20" data-line-number="20" data-line-start="20"><div class="line-content"><span class="mtk1"> ↓ (error path)</span></div></div><div class="code-line" data-line-end="21" data-line-number="21" data-line-start="21"><div class="line-content"><span class="mtk1">inboundErrorSequence → Log lỗi</span></div></div></div></div></div></div>---

## 1.7. Phụ Lục: Cấu Hình Build (

<div id="bkmrk--24">  
</div>## <span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">pom.xml</span></span>)

<div class="my-4" id="bkmrk-tham-s%E1%BB%91-gi%C3%A1-tr%E1%BB%8B-m%C3%B4-t-1"><table class="min-w-full border-collapse"><thead><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Tham số</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Mô tả</th></tr></thead><tbody><tr><td class="px-6 py-2">`artifactId`</td><td class="px-6 py-2">`Loadbalance_kafka`</td><td class="px-6 py-2">Tên artifact Maven.</td></tr><tr><td class="px-6 py-2">`groupId`</td><td class="px-6 py-2">`com.microintegrator.projects`</td><td class="px-6 py-2">Group package.</td></tr><tr><td class="px-6 py-2">`version`</td><td class="px-6 py-2">`1.0.0`</td><td class="px-6 py-2">Phiên bản hiện tại.</td></tr><tr><td class="px-6 py-2">`project.runtime.version`</td><td class="px-6 py-2">`4.4.0`</td><td class="px-6 py-2">Phiên bản WSO2 MI.</td></tr><tr><td class="px-6 py-2">`dockerfile.base.image`</td><td class="px-6 py-2">`wso2/wso2mi:4.4.0`</td><td class="px-6 py-2">Docker base image.</td></tr><tr><td class="px-6 py-2">`dockerfile.name`</td><td class="px-6 py-2">`loadbalance_kafka:1.0.0`</td><td class="px-6 py-2">Tên Docker image output.</td></tr><tr><td class="px-6 py-2">`mi-inbound-kafka`</td><td class="px-6 py-2">`2.0.6`</td><td class="px-6 py-2">Phiên bản connector nhận message từ Kafka.</td></tr><tr><td class="px-6 py-2">`mi-connector-kafka`</td><td class="px-6 py-2">`3.3.10`</td><td class="px-6 py-2">Phiên bản connector gửi message lên Kafka.</td></tr><tr><td class="px-6 py-2">`mi-connector-http`</td><td class="px-6 py-2">`0.1.14`</td><td class="px-6 py-2">Phiên bản connector gọi HTTP.</td></tr></tbody></table>

</div>

# 1.Cấu trúc dự án

## 1.1. Mục tiêu chương

Chương này quy định cấu trúc dự án chuẩn và phạm vi quản lý artifact giữa **Core Team** và các đơn vị **Partner**, nhằm:

<div id="bkmrk-%C4%90%E1%BA%A3m-b%E1%BA%A3o-t%C3%ADnh-%E1%BB%95n-%C4%91%E1%BB%8Bnh">- Đảm bảo tính ổn định của hệ thống tích hợp
- Tránh xung đột cấu hình và artifact giữa các đơn vị phát triển
- Chuẩn hóa cách tổ chức integration artifact trong toàn dự án
- Tăng khả năng bảo trì, mở rộng và triển khai hệ thống

---

</div>## 1.2. Khái niệm và phạm vi áp dụng

Tài liệu này được áp dụng cho:

<div id="bkmrk-t%E1%BA%A5t-c%E1%BA%A3-c%C3%A1c-d%E1%BB%B1-%C3%A1n-s%E1%BB%AD-">- Tất cả các dự án sử dụng nền tảng **<span class="hover:entity-accent entity-underline inline cursor-pointer align-baseline"><span class="whitespace-normal">WSO2 Micro Integrator</span></span>**
- **Core Team**: Đội phát triển lõi, chịu trách nhiệm quản lý hạ tầng, cấu hình hệ thống và các thành phần dùng chung
- **Partner Team**: Các đơn vị đối tác, chịu trách nhiệm phát triển các luồng nghiệp vụ cụ thể

</div>## 1.3. Cấu trúc dự án chuẩn

Cấu trúc dự án được tổ chức theo tiêu chuẩn nhằm phân tách rõ ràng giữa cấu hình hệ thống, tài nguyên tích hợp và logic nghiệp vụ, đảm bảo tính nhất quán và khả năng mở rộng trong quá trình phát triển.

[![image.png](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/axeimage.png)](https://docs.lifetex.vn/uploads/images/gallery/2026-02/axeimage.png)

<div id="bkmrk-%23%23%23-%C3%9D-ngh%C4%A9a-t%E1%BB%ABng-khu">  
Ý Nghĩa Từng thư mục  
<div>  
</div></div>## \* Root level

### `.mvn`, `mvnw`, `pom.xml`

 Project build bằng Maven

<div id="bkmrk-pom.xml-%E2%86%92-qu%E1%BA%A3n-l%C3%BD-de"><div>- `pom.xml` → quản lý dependency, plugin build WSO2
- `.mvn` → config Maven wrapper

---

</div></div>### `+ deployment/`

 Cấu hình runtime &amp; deploy

<div id="bkmrk-deployment.toml-%E2%86%92-co"><div>- `deployment.toml` → config server (port, datasource, kafka...)
- `docker/` → chạy container (nếu có)
- `libs/` → thêm JAR custom (Kafka client, connector...)

---

</div></div>### `+ docs/`

Tài liệu project (thiết kế, API spec...)

<div id="bkmrk--1"><div>---

</div></div>##  \* Source chính

### `+ src/main/java`

 Nếu có custom Java (hiện tại bạn chưa dùng)

<div id="bkmrk--2"><div>---

</div></div>### + `src/main/wso2mi`

Đây là toàn bộ logic integration

<div id="bkmrk--3"><div>---

</div></div># + artifacts/

<div id="bkmrk--4"><div>---

</div></div>## `+ apis/`

 API nhận request từ client

<div id="bkmrk-kafkaproducerapi.xml"><div>- `KafkaProducerApi.xml`  
    → API nhận request → đẩy vào Kafka (producer)
- `PlanningDirectApi.xml`  
    → API gọi trực tiếp backend (không qua Kafka)

---

</div></div>## `+ inbound-endpoints/`

Kafka Consumer (cực kỳ quan trọng)

<div id="bkmrk-load_balance_example"><div>- `Load_balance_example.xml`  
    → Lắng nghe Kafka topic  
    → Khi có message → trigger sequence xử lý

</div></div>Đây chính là chỗ **load balancing bằng Kafka**

<div id="bkmrk--5"><div>---

</div></div>## `+ sequences/`

 Logic xử lý nghiệp vụ

<div id="bkmrk-load_balance_example-1"><div>- `Load_balance_example-inboundSequence.xml`  
    → Xử lý message lấy từ Kafka  
    → gọi backend
- `Load_balance_example-inboundErrorSequence.xml`  
    → xử lý lỗi khi fail

---

</div></div>## `+ endpoints/`

 Định nghĩa backend service

Ví dụ:

<div id="bkmrk-http-endpoint-rest-a"><div>- HTTP endpoint
- REST API backend

---

</div></div>## `+ local-entries/`

Config dùng chung

<div id="bkmrk-kafkaconnection.xml%E2%86%92"><div>- `KafkaConnection.xml`  
    → cấu hình Kafka broker (host, port, topic...)

---

</div></div>## `+ message-stores/`

 Lưu message (queue trung gian)

 Dùng khi:

<div id="bkmrk-retry-%C4%91%E1%BA%A3m-b%E1%BA%A3o-kh%C3%B4ng-"><div>- retry
- đảm bảo không mất dữ liệu

---

</div></div>## `+ message-processors/`

 Xử lý message từ store

 Ví dụ:

<div id="bkmrk-retry-g%E1%BB%ADi-l%E1%BA%A1i-x%E1%BB%AD-l%C3%BD-"><div>- retry gửi lại
- xử lý batch

---

</div></div>## `+ proxy-services/`

 Proxy service (SOAP/HTTP)

 Thường dùng khi:

<div id="bkmrk-wrap-service-c%C5%A9-expo"><div>- wrap service cũ
- expose service trung gian

---

</div></div>## `+ data-services/`

 Kết nối database

 Tạo API CRUD trực tiếp DB

<div id="bkmrk--6"><div>---

</div></div>## `+ data-sources/`

 Cấu hình kết nối DB

<div id="bkmrk--7"><div>---

</div></div>## `+ tasks/`

 Job chạy định kỳ (cron job)

<div id="bkmrk--8"><div>---

</div></div>## `+ templates/`

 Template tái sử dụng

 Giúp:

<div id="bkmrk-tr%C3%A1nh-l%E1%BA%B7p-code-seque"><div>- tránh lặp code sequence / endpoint

---

</div></div># `+ resources/`

 Tài nguyên bổ trợ

<div id="bkmrk-conf%2F-%E2%86%92-config-runti"><div>- `conf/` → config runtime (properties)
- `connectors/` → Kafka connector JAR
- `api-definitions/` → Swagger/OpenAPI
- `metadata/` → metadata của artifact

  
</div>  
<div>  
</div></div>## 1.4. Quy định chính

### 1.4.1. Phân quyền quản lý artifact

<div id="bkmrk-khu-v%E1%BB%B1c-%C4%90%C6%A1n-v%E1%BB%8B-qu%E1%BA%A3n-"><div><div class="TyagGW_tableContainer"><div class="group TyagGW_tableWrapper flex flex-col-reverse w-fit" tabindex="-1"><table class="w-fit min-w-(--thread-content-width)" data-end="860" data-start="202"><thead data-end="248" data-start="202"><tr data-end="248" data-start="202"><th class="" data-col-size="sm" data-end="212" data-start="202">Khu vực</th><th class="" data-col-size="sm" data-end="229" data-start="212">Đơn vị quản lý</th><th class="" data-col-size="sm" data-end="248" data-start="229">Quyền chỉnh sửa</th></tr></thead><tbody data-end="860" data-start="263"><tr data-end="330" data-start="263"><td data-col-size="sm" data-end="281" data-start="263">`deployment/**`</td><td data-col-size="sm" data-end="293" data-start="281">Core Team</td><td data-col-size="sm" data-end="330" data-start="293">Partner không được phép chỉnh sửa</td></tr><tr data-end="411" data-start="331"><td data-col-size="sm" data-end="362" data-start="331">`artifacts/local-entries/**`</td><td data-col-size="sm" data-end="374" data-start="362">Core Team</td><td data-col-size="sm" data-end="411" data-start="374">Partner không được phép chỉnh sửa</td></tr><tr data-end="483" data-start="412"><td data-col-size="sm" data-end="434" data-start="412">`resources/conf/**`</td><td data-col-size="sm" data-end="446" data-start="434">Core Team</td><td data-col-size="sm" data-end="483" data-start="446">Partner không được phép chỉnh sửa</td></tr><tr data-end="561" data-start="484"><td data-col-size="sm" data-end="512" data-start="484">`resources/connectors/**`</td><td data-col-size="sm" data-end="524" data-start="512">Core Team</td><td data-col-size="sm" data-end="561" data-start="524">Partner không được phép chỉnh sửa</td></tr><tr data-end="630" data-start="562"><td data-col-size="sm" data-end="584" data-start="562">`artifacts/apis/**`</td><td data-col-size="sm" data-end="606" data-start="584">Core Team + Partner</td><td data-col-size="sm" data-end="630" data-start="606">Được phép phát triển</td></tr><tr data-end="704" data-start="631"><td data-col-size="sm" data-end="658" data-start="631">`artifacts/sequences/**`</td><td data-col-size="sm" data-end="680" data-start="658">Core Team + Partner</td><td data-col-size="sm" data-end="704" data-start="680">Được phép phát triển</td></tr><tr data-end="786" data-start="705"><td data-col-size="sm" data-end="740" data-start="705">`artifacts/inbound-endpoints/**`</td><td data-col-size="sm" data-end="762" data-start="740">Core Team + Partner</td><td data-col-size="sm" data-end="786" data-start="762">Được phép phát triển</td></tr><tr data-end="860" data-start="787"><td data-col-size="sm" data-end="814" data-start="787">`artifacts/endpoints/**`</td><td data-col-size="sm" data-end="836" data-start="814">Core Team + Partner</td><td data-col-size="sm" data-end="860" data-start="836">Được phép phát triển</td></tr></tbody></table>

</div></div>---

</div></div>### 1.4.2. Quy định bắt buộc

Partner không được phép chỉnh sửa trực tiếp các khu vực sau:

<div id="bkmrk-deployment%2F%2A%2A-artifa"><div>- `deployment/**`
- `artifacts/local-entries/**`
- `resources/conf/**`
- `resources/connectors/**`

</div></div>Mọi thay đổi liên quan đến các khu vực trên phải tuân theo quy trình kiểm soát của Core Team, bao gồm:

<div id="bkmrk-t%E1%BA%A1o-y%C3%AAu-c%E1%BA%A7u-thay-%C4%91%E1%BB%95i"><div>1. Tạo yêu cầu thay đổi (Core Change Request)
2. Được Core Team xem xét và đánh giá
3. Được Core Team phê duyệt và thực hiện thay đổi

---

</div></div>## 1.5. Cách thực hiện / Quy trình

### 1.5.1. Quy trình thay đổi artifact dùng chung

**Bước 1:** Partner xác định nhu cầu thay đổi đối với các khu vực do Core Team quản lý.

**Bước 2:** Tạo ticket trên hệ thống quản lý công việc (Jira, Redmine hoặc YouTrack) với tiêu đề:

<div id="bkmrk-%5Bcore-change-request"><div><div class="relative w-full my-4"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" dir="ltr" id="bkmrk-%5Bcore-change-request-1"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly">[CORE CHANGE REQUEST] &lt;Mô tả ngắn gọn thay đổi&gt;</div></div></div></div></div></div></div></div><div class=""><div class="">  
</div></div></div></div></div></div></div>**Ví dụ:**  
`[CORE CHANGE REQUEST] Thêm Kafka topic mới vào KafkaConnection local-entry`

<div id="bkmrk--9"><div>---

</div></div>### Luồng xử lý

<div id="bkmrk-partner-t%E1%BA%A1o-ticket-%E2%86%92"><div><div class="relative w-full my-4"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" dir="ltr" id="bkmrk-partner-t%E1%BA%A1o-ticket-%E2%86%92-1"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly">Partner tạo ticket → Core Team review → Cập nhật trạng thái → Core thực hiện → Partner cập nhật code</div></div></div></div></div></div></div></div><div class=""><div class="">  
</div></div></div></div></div>---

</div></div>### Trạng thái ticket

<div id="bkmrk-tr%E1%BA%A1ng-th%C3%A1i-%C3%9D-ngh%C4%A9a-o"><div><div class="TyagGW_tableContainer"><div class="group TyagGW_tableWrapper flex flex-col-reverse w-fit" tabindex="-1"><table class="w-fit min-w-(--thread-content-width)" data-end="2194" data-start="1902"><thead data-end="1926" data-start="1902"><tr data-end="1926" data-start="1902"><th class="" data-col-size="sm" data-end="1915" data-start="1902">Trạng thái</th><th class="" data-col-size="sm" data-end="1926" data-start="1915">Ý nghĩa</th></tr></thead><tbody data-end="2194" data-start="1937"><tr data-end="1969" data-start="1937"><td data-col-size="sm" data-end="1946" data-start="1937">`Open`</td><td data-col-size="sm" data-end="1969" data-start="1946">Ticket được tạo mới</td></tr><tr data-end="2014" data-start="1970"><td data-col-size="sm" data-end="1987" data-start="1970">`Under Review`</td><td data-col-size="sm" data-end="2014" data-start="1987">Core Team đang đánh giá</td></tr><tr data-end="2057" data-start="2015"><td data-col-size="sm" data-end="2028" data-start="2015">`Approved`</td><td data-col-size="sm" data-end="2057" data-start="2028">Được chấp thuận thực hiện</td></tr><tr data-end="2097" data-start="2058"><td data-col-size="sm" data-end="2071" data-start="2058">`Rejected`</td><td data-col-size="sm" data-end="2097" data-start="2071">Bị từ chối (kèm lý do)</td></tr><tr data-end="2150" data-start="2098"><td data-col-size="sm" data-end="2114" data-start="2098">`Implemented`</td><td data-col-size="sm" data-end="2150" data-start="2114">Core Team đã hoàn thành thay đổi</td></tr><tr data-end="2194" data-start="2151"><td data-col-size="sm" data-end="2162" data-start="2151">`Merged`</td><td data-col-size="sm" data-end="2194" data-start="2162">Đã merge vào nhánh `develop`</td></tr></tbody></table>

</div></div>---

</div></div>**Bước 3:** Core Team thực hiện đánh giá dựa trên các tiêu chí:

<div id="bkmrk-ph%E1%BA%A1m-vi-%E1%BA%A3nh-h%C6%B0%E1%BB%9Fng-%C4%91%E1%BA%BF"><div>- Phạm vi ảnh hưởng đến các luồng hiện tại
- Rủi ro liên quan đến cấu hình (ví dụ: Kafka, endpoint, connection)
- Tính cần thiết và mức độ ảnh hưởng đến hệ thống

---

</div></div>**Bước 4:** Nếu thay đổi được chấp thuận:

<div id="bkmrk-core-team-th%E1%BB%B1c-hi%E1%BB%87n-"><div>- Core Team thực hiện cập nhật
- Merge vào nhánh `develop`

---

</div></div>**Bước 5:** Partner cập nhật code mới và tiếp tục phát triển.

<div id="bkmrk--10"><div>---

</div></div>## 1.6. Ví dụ minh họa

### 1.6.1. Trường hợp hợp lệ

**Tình huống:** Bổ sung xử lý lỗi cho inbound sequence Kafka

<div id="bkmrk-artifacts%2Fsequences%2F"><div><div class="relative w-full my-4"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" dir="ltr" id="bkmrk-artifacts%2Fsequences%2F-1"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly">artifacts/sequences/Load_balance_example-inboundSequence.xml  
artifacts/sequences/Load_balance_example-inboundErrorSequence.xml</div></div></div></div></div></div></div></div><div class=""><div class="">  
</div></div></div></div></div></div></div>→ Được phép chỉnh sửa trực tiếp và tạo Pull Request.

<div id="bkmrk--11"><div>---

</div></div>**Tình huống:** Thêm API mới để nhận dữ liệu

<div id="bkmrk-artifacts%2Fapis%2Fplann"><div><div class="relative w-full my-4"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" dir="ltr" id="bkmrk-artifacts%2Fapis%2Fplann-1"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly">artifacts/apis/PlanningDirectApi.xml</div></div></div></div></div></div></div></div><div class=""><div class="">  
</div></div></div></div></div></div></div>→ Được phép tạo mới hoặc chỉnh sửa, sau đó tạo Pull Request để Core Team review.

<div id="bkmrk--12"><div>---

</div></div>### 1.6.2. Trường hợp không hợp lệ

**Tình huống:** Thêm Kafka topic vào cấu hình kết nối

<div id="bkmrk-artifacts%2Flocal-entr"><div><div class="relative w-full my-4"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" dir="ltr" id="bkmrk-artifacts%2Flocal-entr-1"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly">artifacts/local-entries/KafkaConnection.xml</div></div></div></div></div></div></div></div><div class=""><div class="">  
</div></div></div></div></div></div></div>→ Không được phép chỉnh sửa trực tiếp.

Phải thực hiện theo quy trình:

<div id="bkmrk-t%E1%BA%A1o-ticket%3A%5Bcore-cha"><div>1. Tạo ticket:  
    `[CORE CHANGE REQUEST] Thêm topic planning_topic vào KafkaConnection`
2. Chờ Core Team phê duyệt
3. Core Team thực hiện thay đổi và merge

---

</div></div>**Tình huống:** Thay đổi cấu hình Kafka bootstrap server

<div id="bkmrk-deployment%2Fdeploymen"><div><div class="relative w-full my-4"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" dir="ltr" id="bkmrk-deployment%2Fdeploymen-1"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly">deployment/deployment.toml  
resources/conf/config.properties</div></div></div></div></div></div></div></div><div class=""><div class="">  
</div></div></div></div></div></div></div>→ Không được phép chỉnh sửa trực tiếp. Phải tạo Core Change Request.

<div id="bkmrk--13"><div>---

</div></div>## 1.7. Checklist áp dụng

Trước khi commit hoặc tạo Pull Request, cần đảm bảo:

<div id="bkmrk-artifact-n%E1%BA%B1m-trong-c"><div>- [ ] Artifact nằm trong các thư mục được phép chỉnh sửa (`artifacts/apis/`, `artifacts/sequences/`, `artifacts/inbound-endpoints/`, `artifacts/endpoints/`)
- [ ] Không chỉnh sửa `artifacts/local-entries/**`
- [ ] Không chỉnh sửa `deployment/**`
- [ ] Không chỉnh sửa `resources/conf/**`
- [ ] Không chỉnh sửa `resources/connectors/**`
- [ ] Pull Request đã được Core Team review (nếu ảnh hưởng đến luồng chung)
- [ ] Đã kiểm thử luồng Kafka producer/consumer sau khi thay đổi (nếu có)

---

</div></div>*Tài liệu này thuộc phạm vi quản lý của Core Team. Mọi thay đổi nội dung phải được Core Team phê duyệt.*

<div id="bkmrk--14"><div>  
</div></div><div align="left" dir="ltr" id="bkmrk--15"></div>

# 2. API Design & Response Standard

## 2.1. Mục Tiêu Chương

Chương này quy định cấu trúc chuẩn của mỗi integration flow và trách nhiệm của từng artifact layer nhằm:

- Tách biệt rõ ràng các tầng xử lý trong luồng tích hợp
- Giảm phụ thuộc giữa các thành phần artifact
- Dễ bảo trì, mở rộng và kiểm thử từng layer độc lập
- Đảm bảo tính nhất quán giữa các flow trong toàn hệ thống

## 2.2. Khái Niệm / Phạm Vi Áp Dụng

Quy tắc này áp dụng cho:

- Tất cả các integration flow trong thư mục `artifacts/**`
- Cả **Core Team** và **Partner Team**
- Tất cả các luồng tích hợp mới được phát triển

<span style="color: rgb(34, 34, 34); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 2.333em; font-weight: 400;">Cấu Trúc Artifact Chuẩn</span>

Mỗi integration flow gồm các thành phần sau:[![image.png](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/F6Simage.png)](https://docs.lifetex.vn/uploads/images/gallery/2026-02/F6Simage.png)

##### Ý Nghĩa Từng Artifact Layer

<table border="1" id="bkmrk-layer-artifact-vai-t" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.0271%;"></col><col style="width: 25.0271%;"></col><col style="width: 25.0271%;"></col><col style="width: 25.0271%;"></col></colgroup><tbody><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Layer</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Artifact</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Vai trò</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Ví dụ</th></tr><tr><td class="px-6 py-2">**API Layer**</td><td class="px-6 py-2">`apis/*.xml`</td><td class="px-6 py-2">Nhận request từ client và trả response</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">KafkaProducerApi.xml</span></span>, <div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">PlanningDirectApi.xml</span></span></td></tr><tr><td class="px-6 py-2">**Mediation Layer**</td><td class="px-6 py-2">`sequences/*-inboundSequence.xml`</td><td class="px-6 py-2">Xử lý logic, điều phối luồng</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">Load\_balance\_example-inboundSequence.xml</span></span></td></tr><tr><td class="px-6 py-2">**Error Layer**</td><td class="px-6 py-2">`sequences/*-inboundErrorSequence.xml`</td><td class="px-6 py-2">Xử lý và cô lập lỗi</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">Load\_balance\_example-inboundErrorSequence.xml</span></span></td></tr><tr><td class="px-6 py-2">**Consumer Layer**</td><td class="px-6 py-2">`inbound-endpoints/*.xml`</td><td class="px-6 py-2">Nhận message từ Kafka topic</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">Load\_balance\_example.xml</span></span></td></tr><tr><td class="px-6 py-2">**Connection Layer**</td><td class="px-6 py-2">`local-entries/*.xml`</td><td class="px-6 py-2">Cấu hình kết nối dùng chung</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">KafkaConnection.xml</span></span></td></tr><tr><td class="px-6 py-2">**Endpoint Layer**</td><td class="px-6 py-2">`endpoints/*.xml`</td><td class="px-6 py-2">Định nghĩa backend endpoint</td><td class="px-6 py-2">Backend API address</td></tr></tbody></table>

## 2.3. Quy Định Chính

### Trách Nhiệm Từng Layer

#### API Layer (`apis/`)

- Nhận request HTTP từ client (hoặc từ WSO2 APIM Gateway)
- Gọi backend trực tiếp hoặc publish message lên Kafka
- Trả response chuẩn về client
- **Không** chứa logic nghiệp vụ phức tạp

#### Mediation Layer (`sequences/*-inboundSequence.xml`)

- Xử lý message nhận từ Kafka consumer
- Điều phối lời gọi đến backend API
- Phân loại kết quả theo HTTP status code (2xx / 4xx / 5xx)
- Publish kết quả vào `processed_topic` hoặc `error_topic`
- **Không** xử lý lỗi kỹ thuật (dành cho Error Layer)

#### Error Layer (`sequences/*-inboundErrorSequence.xml`)

- Bắt lỗi kỹ thuật khi consume message từ Kafka
- Log đầy đủ thông tin: `ERROR_CODE`, `ERROR_MESSAGE`, `ORIGINAL_PAYLOAD`
- Đóng gói và publish message lỗi vào `error_topic`
- **Không** retry — chỉ ghi nhận và chuyển sang error topic

#### Consumer Layer (`inbound-endpoints/`)

- Kết nối đến Kafka broker và subscribe topic
- Khai báo `sequence` (luồng thành công) và `onError` (luồng lỗi)
- **Không** chứa logic xử lý

#### Connection Layer (`local-entries/`)

- Lưu thông tin kết nối Kafka dùng chung toàn hệ thống
- Chỉ Core Team được phép chỉnh sửa

### Quy Tắc Bắt Buộc

<p class="callout info"> **API Layer không được chứa logic nghiệp vụ phức tạp**</p>

<p class="callout info">**Mọi response trả về client phải theo cấu trúc JSON chuẩn**</p>

<p class="callout info">**Error sequence phải luôn publish message vào `error_topic`**</p>

<p class="callout info">**Mediation sequence phải phân loại HTTP status (2xx / 4xx / 5xx)**</p>

### Cấu Trúc Response Chuẩn

**Thành công:**

```json
{
  "success": true,
  "message": "Hồ sơ đã được gửi thành công"
}

```

**Thành công kèm data:**

```json
{
  "eventType": "transaction",
  "source": "<sequence-name>",
  "data": { ... }
}

```

**Lỗi từ API Layer:**

```json
{
  "success": false,
  "httpStatus": "400",
  "error": { ... }
}

```

**Lỗi kỹ thuật (Error Sequence ghi vào `error_topic`):**

```json
{
  "errorSource": "Load_balance_example-inboundEndpoint",
  "sourceTopic": "test_topic_01",
  "errorType": "KAFKA_CONSUME_ERROR",
  "errorCode": "...",
  "errorMessage": "...",
  "originalPayload": { ... },
  "timestamp": "..."
}

```

## 2.4. Cách Thực Hiện / Quy Trình

### Quy Trình Phát Triển Một Integration Flow Mới

**Bước 1: Khai báo Connection (nếu chưa có)**

Tạo hoặc tái sử dụng <span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true">`local-entries/KafkaConnection.xml`</span>.

<p class="callout warning">Chỉ Core Team thực hiện bước này.</p>

**Bước 2: Tạo Inbound Endpoint**

Tạo file `inbound-endpoints/<flow-name>.xml`:

- Khai báo Kafka broker, topic, group ID
- Trỏ `sequence` đến inbound sequence
- Trỏ `onError` đến error sequence

**Bước 3: Tạo Error Sequence**

Tạo file `sequences/<flow-name>-inboundErrorSequence.xml`:

- Log đầy đủ `ERROR_CODE`, `ERROR_MESSAGE`, `ORIGINAL_PAYLOAD`
- Build payload lỗi chuẩn
- Publish vào `error_topic`

**Bước 4: Tạo Inbound Sequence**

Tạo file `sequences/<flow-name>-inboundSequence.xml`:

- Log message nhận được
- Set header xác thực
- Gọi backend API (`<call>`)
- Phân loại theo HTTP status: 
    - `200 / 201` → publish vào `processed_topic`
    - `4xx` → đóng gói lỗi dữ liệu → publish vào `error_topic`
    - `5xx` → đóng gói lỗi backend → publish vào `error_topic`

**Bước 5: Tạo API (nếu cần điểm vào HTTP)**

Tạo file `apis/<flow-name>Api.xml`:

- Khai báo context path và method
- Set header, gọi backend hoặc publish Kafka
- Trả response chuẩn theo HTTP status

[![ho-so-core-flow (1).jpg](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/ho-so-core-flow-1.jpg)](https://docs.lifetex.vn/uploads/images/gallery/2026-02/ho-so-core-flow-1.jpg)

## 2.5. Ví Dụ Minh Họa

### Ví Dụ Đúng — Inbound Sequence phân loại HTTP Status

```xml
<!-- Thành công: publish vào processed_topic -->
<filter xpath="get-property('HTTP_STATUS') = '200' or get-property('HTTP_STATUS') = '201'">
    <then>
        <log level="custom">
            <property name="STATUS" value="[KafkaConsumer][SUCCESS] Backend xử lý thành công"/>
        </log>
        <payloadFactory media-type="json" template-type="default">
            <format>{
                "eventType": "transaction",
                "source": "Load_balance_example-inboundSequence",
                "data": ${payload}
            }</format>
        </payloadFactory>
        <kafkaTransport.publishMessages configKey="KafkaConnection">
            <topic>processed_topic</topic>
        </kafkaTransport.publishMessages>
    </then>
    <else>
        <!-- Lỗi: publish vào error_topic -->
        <kafkaTransport.publishMessages configKey="KafkaConnection">
            <topic>error_topic</topic>
        </kafkaTransport.publishMessages>
    </else>
</filter>

```

**Đúng vì:**

- Phân loại rõ 2xx (thành công) và lỗi
- Kết quả thành công đi vào `processed_topic`
- Lỗi được tách riêng vào `error_topic`
- Sequence không xử lý lỗi kỹ thuật (để cho Error Sequence)

<span style="color: rgb(34, 34, 34); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 2.333em; font-weight: 400;">Ví Dụ Đúng — Error Sequence đóng gói lỗi chuẩn</span>

```xml
<sequence name="Load_balance_example-inboundErrorSequence">
    <!-- Bước 1: Lưu thông tin lỗi -->
    <property name="ERROR_CODE"      expression="get-property('ERROR_CODE')"    scope="default"/>
    <property name="ERROR_MESSAGE"   expression="get-property('ERROR_MESSAGE')" scope="default"/>
    <property name="ORIGINAL_PAYLOAD" expression="json-eval($)"                 scope="default"/>

    <!-- Bước 2: Log chi tiết -->
    <log level="custom">
        <property name="STATUS"         value="[KafkaConsumer][ERROR] Lỗi khi xử lý message"/>
        <property name="ERROR_CODE"     expression="get-property('ERROR_CODE')"/>
        <property name="ERROR_MESSAGE"  expression="get-property('ERROR_MESSAGE')"/>
    </log>

    <!-- Bước 3: Đóng gói payload lỗi -->
    <payloadFactory media-type="json">
        <format>{
            "errorSource": "Load_balance_example-inboundEndpoint",
            "sourceTopic": "test_topic_01",
            "errorType":   "KAFKA_CONSUME_ERROR",
            "errorCode":   "$1",
            "errorMessage":"$2",
            "originalPayload": $3
        }</format>
    </payloadFactory>

    <!-- Bước 4: Publish vào error_topic -->
    <kafkaTransport.publishMessages configKey="KafkaConnection">
        <topic>error_topic</topic>
    </kafkaTransport.publishMessages>
</sequence>

```

###  Ví Dụ Sai — Không phân loại HTTP Status

```xml
<!-- SAI: Không kiểm tra status, trả về luôn -->
<call>
    <endpoint>
        <address uri="http://192.168.0.133:8080/api/v1/plannings"/>
    </endpoint>
</call>
<respond/>

```

**Sai vì:**

- Không kiểm tra HTTP status code từ backend
- Lỗi 4xx / 5xx không được phân loại, không vào `error_topic`
- Không log trạng thái xử lý

###  Ví Dụ Sai — API trả về raw data không theo chuẩn

```xml
<!-- SAI: Không wrap response theo chuẩn -->
<payloadFactory media-type="json">
    <format>{"data": "ok"}</format>
</payloadFactory>
<respond/>

```

**Sai vì:**

- Response không nhất quán với chuẩn `success / error` toàn hệ thống

## 2.6. Checklist Áp Dụng

Trước khi commit integration flow mới:

- [ ]  Có đủ: `inbound-endpoint`, `inboundSequence`, `inboundErrorSequence`
- [ ]  Inbound Sequence có phân loại HTTP status (2xx / 4xx / 5xx)
- [ ]  Thành công publish vào `processed_topic`
- [ ]  Lỗi publish vào `error_topic`
- [ ]  Error Sequence log đầy đủ: `ERROR_CODE`, `ERROR_MESSAGE`, `ORIGINAL_PAYLOAD`
- [ ]  Error Sequence đóng gói payload lỗi theo cấu trúc chuẩn
- [ ]  API Layer trả response theo cấu trúc JSON chuẩn (`success`, `error`)
- [ ]  Không hardcode thông tin kết nối trong Sequence (dùng `local-entries`)
- [ ]  PR đã được Core Team review

*Tài liệu này thuộc phạm vi quản lý của **Core Team** — mọi thay đổi phải được Core Team phê duyệt.*

<div class="absolute select-none shadow rounded-md" id="bkmrk--1"></div><div align="left" dir="ltr" id="bkmrk--3"></div>

# 3.Cơ chế xác thực (JWT/Bearer)

## 3.1. Mục Tiêu Chương

Chương này quy định chuẩn xác thực và nguyên tắc thiết kế bảo mật nhằm:

- Đảm bảo cơ chế xác thực thống nhất giữa các integration flow và đơn vị phát triển
- Giúp artifact dễ đọc, dễ hiểu và dễ bảo trì
- Giảm lỗi phát sinh do xử lý token không đúng chuẩn
- Tăng hiệu quả review và kiểm soát chất lượng artifact
- Đảm bảo toàn bộ luồng từ Client → APIM → MI → Backend đều có xác thực nhất quán
- Tích hợp đúng với WSO2 Identity Server (IS) làm trung tâm cấp và xác thực token

## 3.2. Khái Niệm / Phạm Vi Áp Dụng

Quy định này áp dụng cho:

- Toàn bộ artifact xử lý hoặc chuyển tiếp request qua xác thực
- **Core Team** và **Partner Team**
- Tất cả các API, Sequence, Inbound Endpoint trong `artifacts/**`
- Cấu hình APIM và IS do Core Team quản lý

### Kiến Trúc Xác Thực Tổng Thể

[![image.png](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/Oz9image.png)](https://docs.lifetex.vn/uploads/images/gallery/2026-02/Oz9image.png)

### Ba Tầng Xác Thực

<table border="1" id="bkmrk-t%E1%BA%A7ng-th%C3%A0nh-ph%E1%BA%A7n-vai-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 33.3694%;"></col><col style="width: 33.3694%;"></col><col style="width: 33.3694%;"></col></colgroup><tbody><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Tầng</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Thành phần</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Vai trò xác thực</th></tr><tr><td class="px-6 py-2">**Cấp token**</td><td class="px-6 py-2">WSO2 Identity Server (IS)</td><td class="px-6 py-2">OAuth2 Authorization Server, cấp JWT/Access Token</td></tr><tr><td class="px-6 py-2">**Validate token**</td><td class="px-6 py-2">WSO2 API Manager (APIM)</td><td class="px-6 py-2">Xác thực token tại Gateway trước khi route vào MI</td></tr><tr><td class="px-6 py-2">**Forward token**</td><td class="px-6 py-2">WSO2 Micro Integrator (MI)</td><td class="px-6 py-2">Set `Authorization: Bearer` header khi gọi backend</td></tr></tbody></table>

## 3.3. Quy Định Chính

### 3.3.1. Quy Tắc Đặt Tên Artifact

Các artifact liên quan xác thực phải đặt tên theo quy ước thống nhất:

<table border="1" id="bkmrk-th%C3%A0nh-ph%E1%BA%A7n-quy-t%E1%BA%AFc-%C4%91" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 33.3694%;"></col><col style="width: 33.3694%;"></col><col style="width: 33.3694%;"></col></colgroup><tbody><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Thành phần</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Quy tắc đặt tên</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Ví dụ</th></tr><tr><td class="px-6 py-2">REST API</td><td class="px-6 py-2">`<Name>Api.xml`</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">KafkaProducerApi.xml</span></span>, <div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">PlanningDirectApi.xml</span></span></td></tr><tr><td class="px-6 py-2">Inbound Sequence</td><td class="px-6 py-2">`<flow>-inboundSequence.xml`</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">Load\_balance\_example-inboundSequence.xml</span></span></td></tr><tr><td class="px-6 py-2">Error Sequence</td><td class="px-6 py-2">`<flow>-inboundErrorSequence.xml`</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">Load\_balance\_example-inboundErrorSequence.xml</span></span></td></tr><tr><td class="px-6 py-2">Inbound Endpoint</td><td class="px-6 py-2">`<flow>.xml`</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">Load\_balance\_example.xml</span></span></td></tr><tr><td class="px-6 py-2">Connection Entry</td><td class="px-6 py-2">`<Name>Connection.xml`</td><td class="px-6 py-2"><div>  
</div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">KafkaConnection.xml</span></span></td></tr><tr><td class="px-6 py-2">Backend Endpoint</td><td class="px-6 py-2">`<Name>Endpoint.xml`</td><td class="px-6 py-2">`PlanningBackendEndpoint.xml`</td></tr></tbody></table>

### 3.3.2. Quy Tắc Đặt Property Trong Sequence

Dùng tên property nhất quán khi xử lý token/header:

```xml
<!-- Đúng: Tên property rõ ràng, đúng chuẩn -->
<property name="PAYLOAD"          expression="json-eval($)"    scope="default"/>
<property name="HTTP_STATUS"      expression="$axis2:HTTP_SC"  scope="default"/>
<property name="ERROR_CODE"       expression="get-property('ERROR_CODE')"    scope="default"/>
<property name="ERROR_MESSAGE"    expression="get-property('ERROR_MESSAGE')" scope="default"/>

<!-- Sai: Tên không mô tả ý nghĩa -->
<property name="x"    expression="json-eval($)"   scope="default"/>
<property name="code" expression="$axis2:HTTP_SC" scope="default"/>

```

### 3.3.3. Quy Định Về Logging Xác Thực

Không được bỏ qua bước log khi xử lý request:

```xml
<!-- Sai: Không log trạng thái -->
<call>
    <endpoint>
        <address uri="http://192.168.0.133:8080/api/v1/plannings"/>
    </endpoint>
</call>

```

Phải log đầy đủ các bước:

```xml
<!-- Đúng: Log đầy đủ theo chuẩn dự án -->
<log level="custom">
    <property name="API_NAME" value="PlanningDirectApi"/>
    <property name="STATUS"   value="Nhận request, gọi thẳng Backend..."/>
    <property name="PAYLOAD"  expression="get-property('PAYLOAD')"/>
</log>

```

### 3.3.4. Quy Định Về Token / Header Xác Thực

Token dùng để gọi backend phải được set từ `local-entries` hoặc <span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">config.properties</span></span> — **không hardcode trong Sequence**:

```xml
<!-- SAI: Hardcode token trong Sequence -->
<header name="Authorization" scope="transport"
    value="Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9..."/>

```

```xml
<!-- ĐÚNG: Lấy token từ property/config -->
<property name="BACKEND_TOKEN"
    expression="get-property('conf:backend.auth.token')"
    scope="default"/>
<header name="Authorization" scope="transport"
    expression="fn:concat('Bearer ', get-property('BACKEND_TOKEN'))"/>

```

## 3.4. Nguyên Lý Tách Biệt Trách Nhiệm (Separation of Concerns)

Tương đương nguyên lý **SOLID** áp dụng trong hệ thống WSO2:

### 3.4.1. S — Mỗi Artifact Một Trách Nhiệm

Mỗi artifact chỉ được thực hiện một nhiệm vụ:

<table border="1" id="bkmrk-artifact-ch%E1%BB%89-%C4%91%C6%B0%E1%BB%A3c-l%C3%A0" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50.0542%;"></col><col style="width: 50.0542%;"></col></colgroup><tbody><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Artifact</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Chỉ được làm</th></tr><tr><td class="px-6 py-2">`apis/`</td><td class="px-6 py-2">Nhận HTTP request, set header auth, gọi backend/Kafka, trả response</td></tr><tr><td class="px-6 py-2">`sequences/*-inboundSequence.xml`</td><td class="px-6 py-2">Điều phối luồng message từ Kafka đến backend</td></tr><tr><td class="px-6 py-2">`sequences/*-inboundErrorSequence.xml`</td><td class="px-6 py-2">Ghi nhận lỗi và publish vào `error_topic`</td></tr><tr><td class="px-6 py-2">`inbound-endpoints/`</td><td class="px-6 py-2">Kết nối Kafka, subscribe topic</td></tr><tr><td class="px-6 py-2">`local-entries/`</td><td class="px-6 py-2">Lưu thông tin kết nối và cấu hình dùng chung</td></tr></tbody></table>

**Sai** — Error logic nằm trong inbound sequence:

```xml
<!-- SAI: Inbound sequence tự xử lý lỗi kỹ thuật -->
<sequence name="myFlow-inboundSequence">
    <call>...</call>
    <property name="ERROR_CODE" expression="get-property('ERROR_CODE')"/>
    <kafkaTransport.publishMessages configKey="KafkaConnection">
        <topic>error_topic</topic>
    </kafkaTransport.publishMessages>
    <!-- Đây là nhiệm vụ của inboundErrorSequence -->
</sequence>

```

**Đúng** — Error sequence riêng biệt:

```xml
<inboundEndpoint
    sequence="myFlow-inboundSequence"
    onError="myFlow-inboundErrorSequence">

```

### 3.4.2. O — Mở Rộng Không Sửa Artifact Cũ

Khi thêm topic/luồng mới, tạo artifact mới — không sửa sequence của luồng khác:

```xml
<!-- Thêm luồng mới: tạo file riêng -->
<!-- planning_topic-inboundSequence.xml -->
<!-- planning_topic-inboundErrorSequence.xml -->
<!-- planning_topic.xml (inbound endpoint) -->

```

### 3.4.3. L — Consumer Tuân Thủ Contract Sequence

Inbound Endpoint phải trỏ đúng sequence và error sequence đã khai báo:

```xml
<!-- Sai: onError trỏ sai sequence -->
<inboundEndpoint name="Load_balance_example"
    sequence="Load_balance_example-inboundSequence"
    onError="someOtherSequence"/>  <!-- không tồn tại -->

```

```xml
<!-- Đúng -->
<inboundEndpoint name="Load_balance_example"
    sequence="Load_balance_example-inboundSequence"
    onError="Load_balance_example-inboundErrorSequence"/>

```

### 3.4.4. I — Tách Riêng Local Entry Theo Chức Năng

Không nhét tất cả cấu hình vào một local entry:

```xml
<!-- Sai: Một local entry chứa tất cả -->
<localEntry key="AllConfig">
    <!-- Kafka config, backend URL, token, ... -->
</localEntry>

<!-- Đúng: Tách riêng -->
<localEntry key="KafkaConnection">...</localEntry>
<localEntry key="BackendConfig">...</localEntry>

```

### 3.4.5. D — Phụ Thuộc Config, Không Hardcode

Sequence phụ thuộc vào `local-entries` và <span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">config.properties</span></span> — không hardcode URL hay token:

```xml
<!-- Sai -->
<address uri="http://192.168.0.133:8080/api/v1/plannings"/>

<!-- Đúng -->
<address uri="{$ctx:backend.planning.url}"/>

```

## 3.5. Luồng Xác Thực Chuẩn Trong Hệ Thống

### 3.5.1. Luồng Chuẩn Từ Client Đến Backend

```html
Client
  → [1] Lấy token từ WSO2 IS (/oauth2/token)
  → [2] Gọi API qua WSO2 APIM với Bearer token
  → [3] APIM validate token với WSO2 IS (introspect / JWT verify)
  → [4] APIM route request vào WSO2 MI
  → [5] MI set header Authorization khi gọi backend
  → [6] Backend (Spring Boot) validate JWT

```

### 3.5.2. Luồng Kafka Consumer Không Đi Qua APIM

```html
Kafka Topic (test_topic_01)
  → [1] MI Inbound Endpoint consume message
  → [2] MI Inbound Sequence xử lý
  → [3] MI set header Authorization: Bearer <token>
  → [4] MI gọi Backend API
  → [5] Backend validate JWT
  → [6] Kết quả: published_topic hoặc error_topic

```

<p class="callout warning">**Luồng Kafka consumer bypass APIM Gateway — token phải được quản lý thủ công trong MI (lấy từ IS hoặc dùng service account token).**</p>

### 3.5.3. Không Được Phép

```xml
<!-- Sai: Gọi backend không có Authorization header -->
<call>
    <endpoint>
        <address uri="http://192.168.0.133:8080/api/v1/plannings"/>
    </endpoint>
</call>

<!-- Sai: API tại APIM không bật Security Scheme -->
<!-- Mọi API publish lên APIM đều phải bật OAuth2 hoặc API Key -->

```

### 3.5.4. Luồng Token Trong Kafka Consumer (Chuẩn)

```xml
<!-- Bước 1: Lấy token từ config -->
<property name="BACKEND_TOKEN"
    expression="get-property('conf:backend.auth.token')"
    scope="default"/>

<!-- Bước 2: Set Authorization header -->
<header name="Authorization" scope="transport"
    expression="fn:concat('Bearer ', get-property('BACKEND_TOKEN'))"/>
<property name="Content-Type" value="application/json" scope="transport"/>

<!-- Bước 3: Gọi backend -->
<call>
    <endpoint>
        <address uri="{$ctx:backend.planning.url}">
            <suspendOnFailure>
                <initialDuration>1000</initialDuration>
                <progressionFactor>2.0</progressionFactor>
                <maximumDuration>60000</maximumDuration>
            </suspendOnFailure>
        </address>
    </endpoint>
</call>

```

## 3.6. Cấu Hình Xác Thực WSO2 APIM &amp; IS

### 3.6.1. WSO2 IS — Key Manager

WSO2 IS đóng vai trò **Key Manager** cho APIM:

<table border="1" id="bkmrk-ch%E1%BB%A9c-n%C4%83ng-endpoint-w" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50.0542%;"></col><col style="width: 50.0542%;"></col></colgroup><tbody><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Chức năng</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Endpoint WSO2 IS</th></tr><tr><td class="px-6 py-2">Lấy Access Token</td><td class="px-6 py-2">`POST /oauth2/token`</td></tr><tr><td class="px-6 py-2">Introspect Token</td><td class="px-6 py-2">`POST /oauth2/introspect`</td></tr><tr><td class="px-6 py-2">Lấy Public Key (JWKS)</td><td class="px-6 py-2">`GET /oauth2/jwks`</td></tr><tr><td class="px-6 py-2">Revoke Token</td><td class="px-6 py-2">`POST /oauth2/revoke`</td></tr></tbody></table>

**Lấy token từ IS (client\_credentials):**

```html
POST https://<IS_HOST>:9443/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(clientId:clientSecret)>

grant_type=client_credentials&scope=<scope>

```

### 3.6.2. WSO2 APIM — API Gateway Security

Mọi API publish lên APIM phải cấu hình:

<table border="1" id="bkmrk-thu%E1%BB%99c-t%C3%ADnh-gi%C3%A1-tr%E1%BB%8B-c" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50.0542%;"></col><col style="width: 50.0542%;"></col></colgroup><tbody><tr><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Thuộc tính</th><th class="bg-gray-500/20 px-6 py-2 text-left font-medium">Giá trị chuẩn</th></tr><tr><td class="px-6 py-2">Security Type</td><td class="px-6 py-2">OAuth2 hoặc API Key</td></tr><tr><td class="px-6 py-2">Token Validation</td><td class="px-6 py-2">IS Introspection / JWT verify</td></tr><tr><td class="px-6 py-2">Throttling</td><td class="px-6 py-2">Theo chính sách dự án</td></tr><tr><td class="px-6 py-2">CORS</td><td class="px-6 py-2">Cấu hình theo môi trường</td></tr></tbody></table>

### 3.6.3. JWT Claims Chuẩn Của Hệ Thống

Token JWT cấp bởi WSO2 IS phải chứa:

```json
{
  "sub": "admin",
  "role": "ADMIN",
  "iat": 1771917707,
  "exp": 1772004107,
  "iss": "https://<IS_HOST>:9443/oauth2/token",
  "aud": "...client_id...",
  "scope": "..."
}

```

## 3.7. Cách Thực Hiện / Quy Trình

### Quy Trình Thêm Xác Thực Cho Luồng Mới

**Bước 1:** Đăng ký OAuth2 Application trong WSO2 IS

- Tạo Service Provider
- Lấy `clientId` và `clientSecret`

**Bước 2:** Cấu hình token trong

```yaml
backend.auth.token=<access_token_từ_IS>
backend.planning.url=http://192.168.0.133:8080/api/v1/plannings

```

**Bước 3:** Trong Sequence, đọc token từ config

```xml
<property name="BACKEND_TOKEN"
    expression="get-property('conf:backend.auth.token')"/>
<header name="Authorization" scope="transport"
    expression="fn:concat('Bearer ', get-property('BACKEND_TOKEN'))"/>

```

**Bước 4:** Publish API lên APIM với OAuth2 Security

- Import OpenAPI spec từ `resources/api-definitions/`
- Enable Security: **OAuth2**
- Set Key Manager: **WSO2 IS**

**Bước 5:** Test luồng xác thực end-to-end

- Lấy token từ IS
- Gọi API qua APIM với Bearer token
- Verify log trong MI: `STATUS`, `HTTP_STATUS`, `PAYLOAD`

[![image.png](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/mCVimage.png)](https://docs.lifetex.vn/uploads/images/gallery/2026-02/mCVimage.png)

## 3.8. Ví Dụ Minh Họa

### Ví Dụ Sai — Sequence Không Có Authorization Header

```xml
<sequence name="myFlow-inboundSequence">
    <call>
        <endpoint>
            <address uri="http://192.168.0.133:8080/api/v1/plannings"/>
        </endpoint>
    </call>
    <respond/>
</sequence>

```

**Sai vì:**

- Không set `Authorization: Bearer` header
- Không log request/response
- Backend sẽ reject với 401 Unauthorized
- Không xử lý HTTP status response

### Ví Dụ Sai — Hardcode Token Trong Sequence

```xml
<header name="Authorization" scope="transport"
    value="Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9..."/>

```

**Sai vì:**

- Token hardcode sẽ hết hạn, phải sửa code mỗi lần
- Lộ thông tin xác thực trong source code
- Không thể thay đổi theo môi trường (dev / staging / prod)

### Ví Dụ Đúng — Sequence Xác Thực Và Gọi Backend Chuẩn

```xml
<sequence name="Load_balance_example-inboundSequence">

    <!-- 1. Lưu payload gốc -->
    <property name="PAYLOAD" expression="json-eval($)" scope="default"/>
    <log level="custom">
        <property name="STATUS"  value="[KafkaConsumer] Message nhận được từ Kafka"/>
        <property name="TOPIC"   value="test_topic_01"/>
        <property name="PAYLOAD" expression="get-property('PAYLOAD')"/>
    </log>

    <!-- 2. Set Authorization header từ config -->
    <property name="BACKEND_TOKEN"
        expression="get-property('conf:backend.auth.token')" scope="default"/>
    <header name="Authorization" scope="transport"
        expression="fn:concat('Bearer ', get-property('BACKEND_TOKEN'))"/>
    <property name="Content-Type" value="application/json" scope="transport"/>

    <!-- 3. Gọi backend -->
    <call>
        <endpoint>
            <address uri="{$ctx:backend.planning.url}">
                <suspendOnFailure>
                    <initialDuration>1000</initialDuration>
                    <progressionFactor>2.0</progressionFactor>
                    <maximumDuration>60000</maximumDuration>
                </suspendOnFailure>
            </address>
        </endpoint>
    </call>

    <!-- 4. Kiểm tra phản hồi -->
    <property name="HTTP_STATUS"   expression="$axis2:HTTP_SC"  scope="default"/>
    <property name="API_RESPONSE"  expression="json-eval($)"    scope="default"/>
    <log level="custom">
        <property name="STATUS"      value="[KafkaConsumer] Phản hồi Backend"/>
        <property name="HTTP_STATUS" expression="get-property('HTTP_STATUS')"/>
    </log>

    <!-- 5. Phân loại kết quả -->
    <filter xpath="get-property('HTTP_STATUS') = '200' or get-property('HTTP_STATUS') = '201'">
        <then>
            <kafkaTransport.publishMessages configKey="KafkaConnection">
                <topic>processed_topic</topic>
            </kafkaTransport.publishMessages>
        </then>
        <else>
            <kafkaTransport.publishMessages configKey="KafkaConnection">
                <topic>error_topic</topic>
            </kafkaTransport.publishMessages>
        </else>
    </filter>

</sequence>

```

> **\[Chỗ này thêm ảnh: WSO2 MI log viewer — hiển thị STATUS, HTTP\_STATUS sau khi gọi backend thành công\]**

### Ví Dụ Đúng — API APIM Với OAuth2 Security

```yaml
# resources/api-definitions/KafkaProducerApi.yaml
openapi: 3.0.0
info:
  title: Kafka Producer API
  version: 1.0.0
paths:
  /kafka-producer:
    post:
      summary: Publish message lên Kafka
      security:
        - OAuth2: []          # Bắt buộc xác thực OAuth2 tại APIM
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Thành công
components:
  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://<IS_HOST>:9443/oauth2/token
          scopes: {}

```

## 3.9. Checklist Áp Dụng

Trước khi commit hoặc tạo PR:

**Naming**

- [ ]  API artifact: `<Name>Api.xml`
- [ ]  Inbound Sequence: `<flow>-inboundSequence.xml`
- [ ]  Error Sequence: `<flow>-inboundErrorSequence.xml`
- [ ]  Connection Entry: `<Name>Connection.xml`

**Token &amp; Xác Thực**

- [ ]  Không hardcode Bearer token trong Sequence
- [ ]  Token lấy từ <div>  
    </div><span class="inline-flex items-center gap-0.5 rounded-md align-middle text-sm font-medium transition-[opacity,background-color] cursor-pointer hover:bg-gray-500/20 select-text translate-y-[-1px]" draggable="true"><span class="inline-flex break-all">config.properties</span></span> hoặc `local-entries`
- [ ]  Mọi lời gọi backend đều có `Authorization: Bearer` header
- [ ]  API publish lên APIM đã bật OAuth2 Security
- [ ]  Key Manager trỏ đúng về WSO2 IS

**Logging**

- [ ]  Sequence log `STATUS`, `PAYLOAD` khi nhận request
- [ ]  Sequence log `HTTP_STATUS`, `API_RESPONSE` sau khi gọi backend
- [ ]  Error Sequence log `ERROR_CODE`, `ERROR_MESSAGE`, `ORIGINAL_PAYLOAD`

**Separation of Concerns**

- [ ]  API Layer không chứa logic điều phối phức tạp
- [ ]  Error handling nằm trong Error Sequence riêng
- [ ]  Kết nối Kafka trong `local-entries`, không khai báo lại trong Sequence
- [ ]  URL backend lấy từ config, không hardcode

**Architecture**

- [ ]  Inbound Endpoint trỏ đúng `sequence` và `onError`
- [ ]  Luồng thành công publish vào `processed_topic`
- [ ]  Luồng lỗi publish vào `error_topic`
- [ ]  Không có luồng nào bỏ qua xác thực với backend

## 3.10. Cấu Trúc Chuẩn Của Một Sequence Xử Lý Nghiệp Vụ

### 3.10.1. Mục Tiêu

Quy định cấu trúc thống nhất của một `inboundSequence` nhằm:

- Dễ đọc và dễ review
- Tách rõ: **nhận dữ liệu → xác thực → gọi backend → phân loại kết quả**
- Tránh thiếu log hoặc thiếu xử lý HTTP status
- Chuẩn hóa cách publish kết quả vào Kafka topic

### 3.10.2. Cấu Trúc Bắt Buộc

Một `inboundSequence` phải theo thứ tự:

```html
1. Lưu payload gốc vào property
2. Log thông tin message nhận được
3. Set Authorization header (Bearer token)
4. Gọi backend API (<call>)
5. Lưu HTTP status và response
6. Log phản hồi backend
7. Phân loại theo HTTP status (filter)
   - 2xx → publish vào processed_topic
   - 4xx → đóng gói lỗi dữ liệu → publish vào error_topic
   - 5xx → đóng gói lỗi backend → publish vào error_topic

```

### 3.10.3. Quy Định Chi Tiết Từng Bước

**1. Lưu payload:**

```xml
<property name="PAYLOAD" expression="json-eval($)" scope="default"/>
```

**2. Log input:**

```xml
<log level="custom">
    <property name="STATUS"  value="[KafkaConsumer] Message nhận được từ Kafka"/>
    <property name="PAYLOAD" expression="get-property('PAYLOAD')"/>
</log>
```

**3. Set header xác thực (từ config, không hardcode):**

```xml
<property name="BACKEND_TOKEN"
    expression="get-property('conf:backend.auth.token')"/>
<header name="Authorization" scope="transport"
    expression="fn:concat('Bearer ', get-property('BACKEND_TOKEN'))"/>
```

**4. Gọi backend:**

```xml
<call>
    <endpoint>
        <address uri="{$ctx:backend.planning.url}">
            <suspendOnFailure>
                <initialDuration>1000</initialDuration>
                <progressionFactor>2.0</progressionFactor>
                <maximumDuration>60000</maximumDuration>
            </suspendOnFailure>
        </address>
    </endpoint>
</call>
```

**5–6. Lưu và log phản hồi:**

```xml
<property name="HTTP_STATUS"  expression="$axis2:HTTP_SC"  scope="default"/>
<property name="API_RESPONSE" expression="json-eval($)"    scope="default"/>
<log level="custom">
    <property name="HTTP_STATUS"    expression="get-property('HTTP_STATUS')"/>
    <property name="RESPONSE_BODY"  expression="get-property('API_RESPONSE')"/>
</log>
```

**7. Phân loại kết quả:**

```xml
<filter xpath="get-property('HTTP_STATUS') = '200' or get-property('HTTP_STATUS') = '201'">
    <then>
        <!-- publish processed_topic -->
    </then>
    <else>
        <filter xpath="get-property('HTTP_STATUS') >= '400' and get-property('HTTP_STATUS') &lt; '500'">
            <then>
                <!-- 4xx: lỗi dữ liệu → error_topic -->
            </then>
            <else>
                <!-- 5xx: lỗi backend → error_topic -->
            </else>
        </filter>
    </else>
</filter>
```

### 3.10.4. Ví Dụ Sequence Sai

**Sai vì:** Không có Bearer token, không log, không phân loại HTTP status, không publish kết quả.

### 3.10.5. Checklist Review Sequence

Khi review một `inboundSequence`:

- [ ]  Có lưu `PAYLOAD` vào property trước khi xử lý
- [ ]  Có log thông tin nhận message
- [ ]  Có set `Authorization: Bearer` header (từ config, không hardcode)
- [ ]  Có log HTTP status sau khi gọi backend
- [ ]  Có phân loại HTTP status: 2xx / 4xx / 5xx
- [ ]  2xx → `processed_topic`
- [ ]  4xx và 5xx → `error_topic`
- [ ]  Không xử lý lỗi kỹ thuật trong sequence này (để cho Error Sequence)

*Tài liệu này thuộc phạm vi quản lý của **Core Team** — mọi thay đổi phải được Core Team phê duyệt.*

### <button class="flex items-center font-medium justify-center p-1 rounded bg-primary text-primary-foreground hover:bg-primary-hover duration-100 transition-colors absolute z-10" data-tooltip-id="689024a2-4aad-4468-848d-b7aafee79647" title="Comment on this line"><svg aria-hidden="true" class="lucide lucide-message-square-plus h-3 w-3 stroke-1" fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"></path><path d="M12 8v6"></path><path d="M9 11h6"></path></svg></button>  

# 4. Logging & Audit trong Sequence

### 4.1. Mục tiêu chương

<div id="bkmrk-">  
</div>Chương này quy định cách kiểm soát các thay đổi liên quan đến cấu trúc dữ liệu nhằm:

- Tránh mất dữ liệu hoặc lỗi hệ thống
- Đảm bảo tương thích giữa các module
- Ngăn xung đột schema khi nhiều đơn vị cùng phát triển
- Bảo vệ các field và bảng dữ liệu chuẩn của hệ thống

### 4.2. Khái niệm / phạm vi áp dụng

<div id="bkmrk--1">  
</div>Quy định này áp dụng cho:

- Tất cả các entity trong hệ thống
- Tất cả các bảng database
- Core Team và Partner Team
- Mọi thay đổi liên quan đến:

#### Các loại thay đổi cần kiểm soát

- Thêm field mới
- Xóa field
- Sửa field
- Đổi kiểu dữ liệu
- Thay đổi quan hệ entity
- Thêm/xóa bảng database

### 4.3. Quy định chính

<div id="bkmrk--2">  
</div>#### 4.3.1. Không được tự ý thay đổi các field chuẩn

Các field chuẩn hệ thống:

- id
- created\_at
- created\_by
- updated\_at
- updated\_by
- deleted / status
- tenant\_id (nếu có)

##### Quy định bắt buộc

Không được:

- Xóa field chuẩn
- Đổi kiểu dữ liệu
- Đổi tên field
- Thay đổi ý nghĩa nghiệp vụ của field

Các field này được coi là system fields và được dùng chung cho:

- Audit
- Logging
- Multi-tenant
- Phân quyền
- Tích hợp hệ thống

---

#### 4.3.2. Thêm field mới trong module

Partner chỉ được phép thêm field khi:

- Phục vụ nghiệp vụ của module
- Không trùng với dữ liệu core
- Không phá vỡ cấu trúc hiện tại
- Có migration script
- Có mô tả trong Pull Request
- Được Core Team review

---

#### 4.3.3. Các thay đổi bắt buộc xin phép Core Team

Các thay đổi sau không được tự ý thực hiện:

- Đổi kiểu dữ liệu field
- Xóa field
- Đổi tên field
- Thay đổi quan hệ entity
- Thêm field liên quan:
- user
- role
- permission
- tenant
- audit


Tất cả các trường hợp trên phải qua:

Data Model Change Request

### 4.4. Cách thực hiện / quy trình

<div id="bkmrk--5">  
</div>#### Trường hợp 1: Thêm field trong module

Bước 1: Cập nhật entity trong:

modules/&lt;module&gt;/entity/\*\*

Bước 2: Tạo migration script:

V&lt;timestamp&gt;\_\_add\_&lt;field&gt;\_to\_&lt;table&gt;.sql

Bước 3: Tạo Pull Request, trong PR phải có:

- Mô tả field mới
- Lý do nghiệp vụ
- Migration script

Bước 4: Core Team review:

- Không trùng core data
- Không phá vỡ hệ thống

Bước 5: Merge vào develop

---

#### Trường hợp 2: Thay đổi field hoặc quan hệ entity

Bước 1: Tạo ticket: các phần mềm quản lý công việc liên quan (Jira / Redmine / YouTrack) hoặc nội bộ

\[DATA MODEL CHANGE REQUEST\] &lt;mô tả thay đổi&gt;

![](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/embedded-image-3augpwwi-png.png)

Bước 2: Cung cấp thông tin:

- Entity bị ảnh hưởng
- Field thay đổi
- Kiểu dữ liệu cũ và mới
- Migration script
- Lý do thay đổi

Bước 3: Core Team review:

- Đánh giá ảnh hưởng hệ thống
- Kiểm tra backward compatibility

Bước 4: Nếu được duyệt:

- Core Team thực hiện thay đổi
- Merge vào develop

### 4.5. Ví dụ minh họa

<div id="bkmrk--12">  
</div>#### Trường hợp hợp lệ

Partner thêm field vào entity module:

@Column(name = "PRIORITY")

private Integer priority;

Và có migration:

ALTER TABLE PLANNING ADD PRIORITY INT;

→ Được phép.

---

#### Trường hợp không hợp lệ

Partner sửa:

@Column(name = "CREATED\_AT")

private String createdAt;

→ Đổi kiểu dữ liệu từ timestamp sang string.

→ Không được phép.

Phải tạo:

\[DATA MODEL CHANGE REQUEST\]

---

#### Trường hợp cần xin phép

Partner muốn:

- Xóa field status
- Đổi tên description → detail
- Thêm quan hệ với bảng user\_account

### 4.6. Checklist áp dụng

<div id="bkmrk--24">  
</div>Trước khi commit hoặc tạo PR:

- Có thay đổi field chuẩn không?
- Nếu có → phải tạo Data Model Change Request

- Có đổi kiểu dữ liệu field không?
- Nếu có → phải xin phép Core Team

- Có migration script chưa?
- Field mới có trùng dữ liệu core không?
- PR đã mô tả rõ thay đổi chưa?

# 5.Git Workflow & Pull Request

### 5.1. Mục tiêu chương

<div id="bkmrk-">  
</div>Chương này quy định chuẩn commit và quy trình merge code nhằm:

- Đảm bảo lịch sử thay đổi rõ ràng, dễ truy vết
- Giảm xung đột giữa các nhóm phát triển
- Đảm bảo chất lượng code trước khi đưa vào nhánh chính
- Chuẩn hóa quy trình phát triển giữa Core Team và Partner

### 5.2. Khái niệm / phạm vi áp dụng

<div id="bkmrk--1">  
</div>Quy định này áp dụng cho:

- Tất cả repository của dự án
- Core Team và Partner Team
- Mọi commit và Pull Request (PR)

Quy trình phát triển tuân theo:

- Chuẩn format commit
- Chiến lược branch

**Điều kiện merge bắt buộc**

### 5.3. Quy định chính

#### 5.3.1. Format commit

Tất cả commit phải theo format:

&lt;type&gt;: &lt;description&gt;

Trong đó:

- &lt;type&gt;: loại thay đổi
- &lt;description&gt;: mô tả ngắn gọn nội dung thay đổi

#### Ví dụ commit hợp lệ

feat: add planning API

fix: validate file upload

refactor: optimize planning service

docs: update API guideline

test: add unit test for planning service

chore: update dependency versions

---

<span style="color: rgb(34, 34, 34); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 1.666em; font-weight: 400;">Type hợp lệ</span>

<div align="left" dir="ltr" id="bkmrk-type-%C3%9D-ngh%C4%A9a-feat-th"><table><colgroup><col width="65"></col><col width="256"></col></colgroup><tbody><tr><td>Type

</td><td>Ý nghĩa

</td></tr><tr><td>feat

</td><td>Thêm chức năng mới

</td></tr><tr><td>fix

</td><td>Sửa lỗi

</td></tr><tr><td>refactor

</td><td>Tối ưu, không đổi logic

</td></tr><tr><td>docs

</td><td>Cập nhật tài liệu

</td></tr><tr><td>test

</td><td>Thêm hoặc sửa test

</td></tr><tr><td>chore

</td><td>Thay đổi cấu hình, build, dependency

</td></tr></tbody></table>

</div>---

#### 5.3.2. Branch strategy

Hệ thống sử dụng mô hình branch chuẩn:

<div align="left" dir="ltr" id="bkmrk-branch-m%E1%BB%A5c-%C4%91%C3%ADch-main"><table><colgroup><col width="72"></col><col width="244"></col></colgroup><tbody><tr><td>Branch

</td><td>Mục đích

</td></tr><tr><td>main

</td><td>Bản stable, dùng cho production

</td></tr><tr><td>develop

</td><td>Bản tích hợp, nơi merge các feature

</td></tr><tr><td>feature/\*

</td><td>Phát triển chức năng mới

</td></tr><tr><td>fix/\*

</td><td>Sửa lỗi

</td></tr></tbody></table>

</div>---

#### Quy tắc làm việc với branch

- Không commit trực tiếp vào main
- Không commit trực tiếp vào develop
- Mọi thay đổi phải qua Pull Request
- Branch phải được đặt tên đúng quy tắc

---

<span style="color: rgb(34, 34, 34); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 1.666em; font-weight: 400;">Ví dụ tên branch hợp lệ</span>

feature/planning-create-api

feature/document-upload

fix/login-null-pointer

fix/planning-date-validation

---

<span style="color: rgb(34, 34, 34); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 2.333em; font-weight: 400;">5.3.3. Điều kiện merge Pull Request</span>

Pull Request chỉ được merge khi đáp ứng đầy đủ các điều kiện:

1. Build thành công

Không sửa:  
  
common/\*\*

config/\*\*

2. Không sửa shared entity

**Có review từ Core Team**

### 5.4. Cách thực hiện / quy trình

<div id="bkmrk--12">  
</div>#### Quy trình phát triển tính năng

Bước 1: Tạo branch từ develop

git checkout develop

git pull

git checkout -b feature/planning-create-api

Bước 2: Thực hiện commit theo chuẩn

git commit -m "feat: add planning create API"

Bước 3: Push branch lên remote

git push origin feature/planning-create-api

Bước 4: Tạo Pull Request vào develop

Bước 5: Core Team review

Bước 6: Nếu đạt yêu cầu → merge vào develop

---

#### Quy trình sửa lỗi

Bước 1: Tạo branch fix

git checkout develop

git checkout -b fix/planning-null-error

Bước 2: Commit

git commit -m "fix: handle null planning name"

Bước 3: Tạo PR và chờ review

### 5.5. Ví dụ minh họa

#### Trường hợp commit sai

![](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/embedded-image-slvhkcbl-png.png)

Sai vì:

- Không theo format chuẩn
- Không có type

---

<span style="color: rgb(34, 34, 34); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 1.666em; font-weight: 400;">Trường hợp commit đúng</span>

fix: handle null planning name

![](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/embedded-image-xikz8bws-png.png)

---

#### Trường hợp merge sai

Partner:

- Commit trực tiếp vào develop
- Không tạo PR
- Không có review

→ Vi phạm quy trình.

---

#### Trường hợp merge đúng

1. Tạo branch feature/planning-api
2. Commit theo chuẩn
3. Tạo PR vào develop
4. Core Team review
5. Build pass
6. Merge

**![](https://docs.lifetex.vn/uploads/images/gallery/2026-02/scaled-1680-/embedded-image-g3tqv2lw-png.png)**

### 5.6. Checklist áp dụng

<div id="bkmrk--37">  
</div>Trước khi tạo Pull Request:

- Commit đúng format &lt;type&gt;: &lt;description&gt;
- Branch đúng quy tắc feature/\* hoặc fix/\*
- Không commit trực tiếp vào main hoặc develop
- Build thành công
- Không sửa:
- common/\*\*
- config/\*\*
- shared entity

- PR đã được Core Team review

# 6.Checklist kiểm tra trước khi merge

### 6.1. Mục tiêu chương

<div id="bkmrk-">  
</div>Chương này quy định checklist bắt buộc trước khi merge code nhằm:

- Đảm bảo chất lượng code trước khi tích hợp
- Tránh vi phạm các quy định về kiến trúc và dữ liệu
- Giảm rủi ro lỗi khi build hoặc triển khai
- Chuẩn hóa quy trình review giữa Core Team và Partner

### 6.2. Khái niệm / phạm vi áp dụng

<div id="bkmrk--1">  
</div>Checklist này áp dụng cho:

- Tất cả Pull Request (PR)
- Core Team và Partner Team
- Mọi module trong hệ thống

Checklist được dùng trong:

- Code review
- Kiểm tra trước khi merge vào develop

**Kiểm tra trước khi release**

### 6.3. Quy định chính

<div id="bkmrk--2">  
</div>Pull Request chỉ được phép merge khi đáp ứng đầy đủ các điều kiện sau:

Code nằm trong khu vực được phép:  
  
modules/\*\*

1. Không sửa các khu vực bị cấm:

  
common/\*\*

config/\*\*

2. Không sửa shared entity trong:

  
common/entity/\*\*

3. Nếu có thay đổi database:
- Phải có migration script

5. API phải đúng chuẩn:

/api/v1/&lt;module-name&gt;

6. Response phải dùng:

ApiResponse&lt;T&gt;

 7.Build phải thành công

### 6.4. Cách thực hiện / quy trình

#### Quy trình kiểm tra trước khi merge

Bước 1: Dev hoàn thành feature hoặc fix bug  
Bước 2: Tạo Pull Request vào develop  
Bước 3: Dev tự kiểm tra checklist  
Bước 4: Core Team review theo checklist  
Bước 5: Nếu đạt yêu cầu → merge

Nếu không đạt:

- PR bị reject
- Dev phải sửa lại theo checklist

### 6.5. Ví dụ minh họa

#### Trường hợp hợp lệ

PR:

Chỉ sửa code trong:  
  
modules/planning/\*\*

- Có migration: V20260210\_\_add\_priority\_to\_planning.sql
- API: /api/v1/planning
- Build thành công

→ Được merge.

---

#### Trường hợp không hợp lệ

PR:

Sửa file:  
  
common/entity/UserEntity.java

- 

→ Vi phạm quy định shared entity.

→ PR bị từ chối.

---

#### Trường hợp bị từ chối do thiếu migration

PR:

Thêm field trong entity:  
  
private Integer priority;

- 
- Không có migration SQL

→ PR bị reject.

### 6.6. Checklist áp dụng

<div id="bkmrk--11">  
</div>Dev phải tick đủ các mục sau trước khi merge:

\[ \] Code nằm trong modules/\*\*

\[ \] Không sửa common/\*\*

\[ \] Không sửa config/\*\*

\[ \] Không sửa shared entity

\[ \] Có migration nếu thay đổi DB

\[ \] API đúng chuẩn /api/v1/

\[ \] Response dùng ApiResponse

\[ \] Build thành công

Core Team chỉ merge khi checklist đạt đầy đủ.

# 7.Quy trình phối hợp & quản lý thay đổi các nhóm

### 7.1. Mục tiêu chương

<div id="bkmrk-">  
</div>Chương này quy định cách phối hợp giữa nhiều đơn vị phát triển nhằm:

- Tránh xung đột giữa FE, BE và Database
- Đảm bảo tích hợp ổn định
- Giữ tính tương thích giữa các module
- Kiểm soát các thay đổi ảnh hưởng hệ thống

### 7.2. Khái niệm / phạm vi áp dụng

<div id="bkmrk--1">  
</div>Áp dụng khi:

- Có nhiều đơn vị cùng phát triển
- FE và BE tách source
- Nhiều FE dùng chung BE
- BE tách thành nhiều service

Mọi thay đổi liên quan:

- API contract
- Entity/Database
- Auth
- Domain/CORS

đều phải thông qua Core Team.

### 7.3. Quy định chính

#### Nguyên tắc chung

- Dùng contract-first (OpenAPI là nguồn chuẩn)
- Mọi thay đổi API hoặc DB phải được kiểm soát
- Mọi thay đổi breaking phải qua Core Team

---

#### Trường hợp FE và BE khác source, dùng chung DB

Rủi ro:

- Sai contract API
- Response làm FE vỡ
- Migration xung đột

Quy định:

- BE thay đổi API/DB phải update OpenAPI
- Mọi thay đổi DB phải có migration
- Staging dùng DB chung

Quy trình:

1. FE tạo ticket: API CONTRACT ISSUE
2. BE/Core xác nhận
3. Fix backward compatible hoặc tạo API v2

---

#### Trường hợp FE và BE dùng DB riêng khi dev

Quy định:

- Có DB baseline chung
- Staging dùng DB chung

Quy trình:

1. Nếu staging lỗi → so version migration
2. Không cho merge nếu thiếu migration

---

#### Trường hợp nhiều FE dùng chung một BE

Quy định:

- Dùng IdP chung (WSO2/Keycloak)
- Mỗi FE là một OAuth client riêng
- Gọi API bằng JWT

Quy trình:

1. FE đăng ký client\_id
2. Core cấu hình redirectUri, scope
3. Core cấp quyền

---

#### Trường hợp FE chung source, BE nhiều service

Quy định:

- FE cấu hình baseURL theo service
- Auth dùng token chung
- CORS do Core Team quản lý

Quy trình:

1. Thêm service → tạo Integration Checklist
2. Core cấu hình CORS và token audience

---

#### Trường hợp thay đổi response làm FE vỡ

Quy định:

- Chỉ được thêm field (non-breaking)
- Không đổi kiểu dữ liệu
- Không rename field
- Breaking change phải tạo /api/v2

Quy trình:

1. Tạo BREAKING CHANGE REQUEST
2. Core duyệt
3. Thông báo FE

---

#### Trường hợp xung đột entity/model

Quy định:

- Entity dùng chung → coi là shared model
- Phải qua Data Model Change Request

Quy trình:

1. Partner đề xuất field + migration
2. Core review
3. Approve hoặc yêu cầu sửa

---

#### Trường hợp migration xung đột

Quy định:

- Không sửa migration đã merge
- Tạo migration mới để fix forward

Quy trình:

1. Rebase
2. Tạo migration mới
3. Test staging

---

#### Trường hợp seed/master data bị sửa

Quy định:

- Seed core do Core Team quản lý
- Partner chỉ được đề xuất

Quy trình:

1. Tạo MASTER DATA REQUEST
2. Core duyệt và triển khai

### 7.4. Quy trình triển khai tính năng nhiều tầng

<div id="bkmrk--11">  
</div>1. Tạo ticket Feature
2. Chốt API contract (OpenAPI)
3. Chốt entity/migration
4. BE implement
5. FE integrate
6. Test staging
7. Release

### 7.5.Ví dụ minh họa

<div id="bkmrk--12">  
</div>**1. bảng mô tả các môi trường hệ thống:**

<table class="w-fit min-w-(--thread-content-width)" data-end="1244" data-start="953" id="bkmrk-env-fe-url-be-url-db"><thead data-end="990" data-start="953"><tr data-end="990" data-start="953"><th class="" data-col-size="sm" data-end="959" data-start="953">Env</th><th class="" data-col-size="sm" data-end="968" data-start="959">FE URL</th><th class="" data-col-size="sm" data-end="977" data-start="968">BE URL</th><th class="" data-col-size="sm" data-end="982" data-start="977">DB</th><th class="" data-col-size="sm" data-end="990" data-start="982">Auth</th></tr></thead><tbody data-end="1244" data-start="1029"><tr data-end="1099" data-start="1029"><td data-col-size="sm" data-end="1035" data-start="1029">dev</td><td data-col-size="sm" data-end="1059" data-start="1035"><a class="decorated-link cursor-pointer" data-end="1058" data-start="1037" rel="noopener" target="_new">http://localhost:4200<span aria-hidden="true" class="ms-0.5 inline-block align-middle leading-none"><svg aria-hidden="true" class="block h-[0.75em] w-[0.75em] stroke-current stroke-[0.75]" data-rtl-flip="" height="20" width="20" xmlns="http://www.w3.org/2000/svg"></svg></span></a></td><td data-col-size="sm" data-end="1083" data-start="1059"><a class="decorated-link cursor-pointer" data-end="1082" data-start="1061" rel="noopener" target="_new">http://localhost:8080<span aria-hidden="true" class="ms-0.5 inline-block align-middle leading-none"><svg aria-hidden="true" class="block h-[0.75em] w-[0.75em] stroke-current stroke-[0.75]" data-rtl-flip="" height="20" width="20" xmlns="http://www.w3.org/2000/svg"></svg></span></a></td><td data-col-size="sm" data-end="1091" data-start="1083">local</td><td data-col-size="sm" data-end="1099" data-start="1091">mock</td></tr><tr data-end="1182" data-start="1100"><td data-col-size="sm" data-end="1110" data-start="1100">staging</td><td data-col-size="sm" data-end="1135" data-start="1110"><a class="decorated-link cursor-pointer" data-end="1134" data-start="1112" rel="noopener" target="_new">https://staging.app.vn<span aria-hidden="true" class="ms-0.5 inline-block align-middle leading-none"><svg aria-hidden="true" class="block h-[0.75em] w-[0.75em] stroke-current stroke-[0.75]" data-rtl-flip="" height="20" width="20" xmlns="http://www.w3.org/2000/svg"></svg></span></a></td><td data-col-size="sm" data-end="1160" data-start="1135"><a class="decorated-link cursor-pointer" data-end="1159" data-start="1137" rel="noopener" target="_new">https://staging.api.vn<span aria-hidden="true" class="ms-0.5 inline-block align-middle leading-none"><svg aria-hidden="true" class="block h-[0.75em] w-[0.75em] stroke-current stroke-[0.75]" data-rtl-flip="" height="20" width="20" xmlns="http://www.w3.org/2000/svg"></svg></span></a></td><td data-col-size="sm" data-end="1169" data-start="1160">shared</td><td data-col-size="sm" data-end="1182" data-start="1169">WSO2 thật</td></tr><tr data-end="1244" data-start="1183"><td data-col-size="sm" data-end="1190" data-start="1183">prod</td><td data-col-size="sm" data-end="1207" data-start="1190">[https://app.vn<span aria-hidden="true" class="ms-0.5 inline-block align-middle leading-none"><svg aria-hidden="true" class="block h-[0.75em] w-[0.75em] stroke-current stroke-[0.75]" data-rtl-flip="" height="20" width="20" xmlns="http://www.w3.org/2000/svg"></svg></span>](https://app.vn/)</td><td data-col-size="sm" data-end="1224" data-start="1207"><a class="decorated-link cursor-pointer" data-end="1223" data-start="1209" rel="noopener" target="_new">https://api.vn<span aria-hidden="true" class="ms-0.5 inline-block align-middle leading-none"><svg aria-hidden="true" class="block h-[0.75em] w-[0.75em] stroke-current stroke-[0.75]" data-rtl-flip="" height="20" width="20" xmlns="http://www.w3.org/2000/svg"></svg></span></a></td><td data-col-size="sm" data-end="1231" data-start="1224">prod</td><td data-col-size="sm" data-end="1244" data-start="1231">WSO2 thật</td></tr></tbody></table>

# 8. Prompt Template cho AI Agent tạo API trên LifeESB

## 8.1. Vai trò và Nhiệm vụ (System Prompt)

**Copy đoạn văn bản sau đưa cho AI:**

> **Vai trò:** Bạn là một Chuyên gia Kiến trúc Tích hợp (Integration Architect) hệ thống WSO2 Micro Integrator (MI) 4.4.0. Nhiệm vụ của bạn là đọc các bản đặc tả API (Use Cases) từ người dùng và tự động sinh ra mã nguồn XML chuẩn xác, có thể chạy được ngay của WSO2 MI.
> 
> **Tiêu chuẩn Thiết kế (Bắt buộc tuân thủ 100%):**
> 
> 1. **Kiến trúc REST:** Mọi API phải được bọc trong thẻ `<api context="/... " name="...">`. Các Use Case bên trong được chia thành các thẻ `<resource methods="..." uri-template="...">`.
> 2. **Phân tách Endpoint:** Cấm hardcode URL trực tiếp vào `<call>`. Mọi URL gọi xuống backend phải được định dạng trong một file `<endpoint>` độc lập.
> 3. **Cấu hình Môi trường:** Trong file `<endpoint>`, thuộc tính `uri-template` của `<http>` BẮT BUỘC phải dùng định dạng System Properties: `$SYSTEM:TEN_BIEN_CUA_DICH_VU`.
> 4. **Xử lý Dữ liệu mặc định là XML:** Payload đi qua hệ thống mặc định là XML. Trừ khi được yêu cầu đổi sang JSON, còn lại không tự ý dùng DataMapper hay `<property name="messageType" value="application/json"/>`.
> 5. **Trích xuất thông tin:** Trong `inSequence` của API, phải có các property sử dụng **XPath** để lấy các trường quan trọng từ payload XML đầu vào (như `requestId`, `procedureCode`, `registrationId`...).
> 6. **Ghi Log (Observability):** Ngay sau khi vào `inSequence` và trích xuất property, phải có thẻ `<log level="custom">` để in ra Console thông tin request (bao gồm Use Case ID đang chạy và các biến vừa lấy).
> 7. **Xử lý Ngoại lệ:** Mọi `<resource>` bắt buộc phải có `<faultSequence>` để bẫy lỗi và trả response.
> 8. **Đầu ra (Output Mode):** Chỉ in ra kết quả là các khối mã XML rõ ràng kèm tên file (comment ở dòng đầu), không giải thích dài dòng trừ khi người dùng hỏi.

## 8.2. Kiến thức Nền tảng (Few-Shot Examples)

**Cung cấp cho AI đoạn mã mẫu sau làm chuẩn mực (Golden Template):**

> **Dưới đây là Mẫu Code Tiêu Chuẩn bạn phải học theo:**
> 
> **Mẫu 1: File API định tuyến (ReceivingAPI.xml)**
> 
> ```xml
> <?xml version="1.0" encoding="UTF-8"?>
> <api context="/api/v1/receiving/registrations" name="ReceivingAPI" xmlns="http://ws.apache.org/ns/synapse">
>     <resource methods="POST" uri-template="/">
>         <inSequence>
>             <log level="custom">
>                 <property name="Status" value="[UC-22] Received New Registration Request"/>
>                 <property name="requestId" expression="//requestId/text()"/>
>                 <property name="procedureCode" expression="//procedureCode/text()"/>
>            </log>
>             <call>
>                 <endpoint key="BackendReceivingCreateEndpoint"/>
>             </call>
>             <respond/>
>         </inSequence>
>         <outSequence/>
>         <faultSequence>
>             <log level="custom"><property name="Error" value="[UC-22] Failed"/></log>
>             <respond/>
>         </faultSequence>
>     </resource>
> </api>
> ```
> 
> **Mẫu 2: File Endpoint độc lập (BackendReceivingCreateEndpoint.xml)**
> 
> ```xml
> <?xml version="1.0" encoding="UTF-8"?>
> <endpoint name="BackendReceivingCreateEndpoint" xmlns="http://ws.apache.org/ns/synapse">
>     <http method="post" uri-template="$SYSTEM:URL_BACKEND_RECEIVING_CREATE">
>         <suspendOnFailure>
>             <initialDuration>2000</initialDuration>
>             <progressionFactor>1.0</progressionFactor>
>             <maximumDuration>3000</maximumDuration>
>         </suspendOnFailure>
>     </http>
> </endpoint>
> ```

## 8.3. Cách thức làm việc (User Workflow)

Sau khi lưu 2 đoạn trên làm cấu hình cho Bot (System Instructions), bạn chỉ cần làm việc với nó qua các bước đơn giản mỗi lệnh:

**Ví dụ một đoạn Chat bạn nhập vào:**

> **Prompt: "Đây là tài liệu Use Case UC-26: \[dán phần text tài liệu UC-26 Định tuyến thông điệp vào đây\]. Yêu cầu: Generates file RoutingAPI.xml với context '/api/v1/routing' và các file Endpoints tương ứng. Chú ý sử dụng Switch mediator để rẽ nhánh procedureCode."**

**Kết quả hoàn hảo bot trả về:** Bot sẽ lập tức sinh ra 1 file API có Switch mediator dùng XPath `//procedureCode/text()` cực chuẩn xác và 2-3 file cấu hình Endpoint `$SYSTEM:` cho nó.

<div class="relative w-full h-full" id="bkmrk-commentctrl%2Balt%2Bm"><div class="w-full flex justify-center h-full relative"><div class="relative pl-4 pr-4 py-1 min-w-0 grow h-fit max-w-[40rem]"><div class="absolute select-none shadow rounded-md"><button class="flex items-center gap-1 font-medium justify-center px-2 py-2 bg-primary text-primary-foreground hover:bg-primary-hover duration-100 transition-colors"><span class="text-xs">Comment</span><span class="text-xs opacity-50">Ctrl+Alt+M</span></button></div></div></div></div>

# 9. Prompt Template cho AI Agent tạo Test Case API LifeESB

Bản Prompt này được tối ưu để hướng dẫn AI sinh ra các kịch bản kiểm thử (Test Cases), cURL commands, và Postman Collection dựa trên tài liệu đặc tả API và file mã nguồn WSO2 MI.

## 9.1. Vai trò và Nhiệm vụ (System Prompt)

**Copy đoạn văn bản sau đưa cho AI:**

> **Vai trò:** Bạn là một QA / API Testing Engineer cấp cao chuyên về nền tảng tích hợp WSO2 Micro Integrator (MI). Nhiệm vụ của bạn là đọc các bản đặc tả Use Case và đoạn mã XML API của WSO2, từ đó sinh ra bộ Test Case hoàn chỉnh để kiểm thử API hoạt động đúng nghiệp vụ.
> 
> **Tiêu chuẩn Thiết kế Test Case (Bắt buộc tuân thủ 100%):**
> 
> 1. **Dữ liệu Mặc định là XML:** Mặc dù đặc tả có thể mô tả bằng JSON, nhưng Client giao tiếp với MI qua định dạng XML. Tất cả các Test Case payload (`Body`) BẮT BUỘC phải viết dưới dạng chuỗi XML hợp lệ.
> 2. **Cấu trúc Test Case:** Mỗi Use Case phải có ít nhất 2 nhóm Test Case gốc: 
>     - **Happy Path (Thành công):** Dữ liệu chuẩn, mô phỏng luồng đi trót lọt (`HTTP 200/202`).
>     - **Negative Path (Báo lỗi):** Dữ liệu cố tình làm sai (VD: thiếu dòng/sai XML, logic sai như thiếu chữ ký...) để kiểm thử khối `<faultSequence>` hoặc logic Validate của backend.
> 3. **Cú pháp Đầu ra (Output Format):** Mỗi Test Case phải trình bày rõ ràng 4 phần: 
>     - **Tên Test Case:** (VD: TC01 - Tiếp nhận đăng ký thành công)
>     - **Endpoint &amp; Method:** (VD: `POST http://localhost:8290/api/v1/receiving/registrations`)
>     - **Headers:** Định nghĩa rõ `Content-Type: application/xml` (hoặc `application/json` nếu có yêu cầu dị biệt).
>     - **Request Body (XML):** Cấu trúc XML chuẩn xác theo nghiệp vụ.
> 4. **Postman/cURL Script:** Cung cấp câu lệnh `cURL` tương ứng cho nhóm Happy Path để người dùng dễ dàng dán vào Terminal hoặc Import vào Postman test nhanh.
> 5. **Tập trung vào Giao diện (Integration Edge):** Bạn chỉ test lớp Integration (Gateway/MI). Hãy chú ý truyền đúng các Param/Path Variable/Body mà WSO2 `inSequence` đang cần dùng (như `requestId`, `procedureCode`).

## 9.2. Kiến thức Nền tảng (Few-Shot Examples)

**Cung cấp cho AI ví dụ Test Case chuẩn mực sau:**

> **Dưới đây là Mẫu Test Case Tiêu Chuẩn bạn phải học theo:**
> 
> **Use Case Mẫu:** UC-22 Tiếp nhận hồ sơ mới (Nhận XML Ingestion)
> 
> ### TC01: Happy Path - Đăng ký hồ sơ thành công (Dữ liệu XML chuẩn mực)
> 
> - **Method:** `POST`
> - **URL:** `http://localhost:8290/api/v1/receiving/registrations`
> - **Headers:** `Content-Type: application/xml` `Accept: application/xml`
> - **Body Raw (XML):**<div class="font-sans text-sm text-ide-text-color opacity-60">  
>     </div>
> 
> ```xml
> 
> <request>
>     <requestId>REQ_20250228001</requestId>
>     <procedureCode>HQ01</procedureCode>
>     <enterpriseId>DN001</enterpriseId>
>     <documents>
>         <document>
>             <documentType>INV</documentType>
>             <content>Base-64-Encoded-Content</content>
>         </document>
>     </documents>
>     <signature>
>         <signedData>...</signedData>
>     </signature>
> </request>
> ```
> 
> - **Expected Result:** Nhận về HTTP Status `200 OK` (Hoặc 202 tuỳ backend) với form Output Payload do Backend nhả ra qua WSO2 MI.
> 
> **cURL để Test nhanh TC01:**
> 
> ```bash
> bash
> curl -X POST -H 'Content-Type: application/xml' -H 'Accept: application/xml' -d '<request><requestId>REQ_20250228001</requestId><procedureCode>HQ01</procedureCode><enterpriseId>DN001</enterpriseId><documents><document><documentType>INV</documentType><content>Base-64-Encoded-Content</content></document></documents><signature><signedData>...</signedData></signature></request>' http://localhost:8290/api/v1/receiving/registrations
> ```
> 
> ### TC02: Negative Path - Payload XML bị khuyết cấu trúc nghiêm trọng (Lỗi Missing Mandatory Field)
> 
> - Mô tả: Gửi cố tình xoá cụm `<procedureCode>` để xem khối WSO2 Fault Sequence hoặc Backend có bẫy được không.
> - **Body Raw (XML):**<div class="font-sans text-sm text-ide-text-color opacity-60">  
>     </div>
> 
> ```xml
> <request>
>     <requestId>REQ_20250228_ERR02</requestId>
>     <!-- Cố tình thiếu procedureCode và enterpriseId -->
> </request>
> 
> ```
> 
> - **Expected Result:** Nhận về chuỗi XML mô tả lỗi, HTTP Code dạng `400 Bad Request` hoặc `500`.

## 9.3. Cách thức làm việc (User Workflow)

Sau khi nhập 2 khối trên vào System Context, quy trình bạn ra lệnh sẽ như sau:

**Ví dụ một đoạn Chat bạn nhập vào:**

> **Prompt: "Tôi vừa viết xong WSO2 API cho UC-23 (Sửa hồ sơ `/{registrationId}/amend`). Đây là nguyên văn tài liệu: \[Dán tài liệu UC-23\]. Yêu cầu sinh Test Case cho API này theo định dạng XML. Context API gốc chày trên máy tôi là port 8290."**

**Kết quả hoàn hảo bot trả về: Bot sẽ sinh ra các bộ câu hỏi kiểm thử: HTTP Body (XML) sửa hồ sơ, Path params `id` kèm cURL Script. Bạn chỉ việc Copy ném vào Postman là bắn test luôn không phải typing mỏi tay định dạng từng dấu ngoặc.**