# Tổng quan kiến trúc hệ thống

# Nội dung chi tiết

### 1. Tổng quan kiến trúc hệ thống

#### 1.1. Mục tiêu kiến trúc

Kiến trúc backend được thiết kế nhằm:

- Đảm bảo khả năng mở rộng và bảo trì lâu dài
- Tách biệt rõ trách nhiệm giữa các layer
- Chuẩn hóa cách tổ chức code giữa các module
- Hỗ trợ phát triển song song nhiều developer
- Dễ dàng tích hợp các hệ thống ngoài (SSO, Cache, MQ…)

Tất cả các service backend phải tuân thủ kiến trúc chuẩn được định nghĩa trong tài liệu này.

---

#### 1.2. Mô hình kiến trúc

Hệ thống backend sử dụng mô hình:

**Modular Monolith – Layered Architecture**

Đặc điểm:

- Một service Spring Boot deploy độc lập
- Bên trong chia module theo domain
- Mỗi module có đầy đủ layer Controller → Service → Repository
- Có thể tách thành microservice khi cần

Sơ đồ tổng thể:

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

Tích hợp ngoài:

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

---

### 1.3. Quy tắc phân lớp (Layered Architecture)

Mỗi module phải tuân thủ cấu trúc phân lớp sau:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-controller-service-r"><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-attribute">controller</span>servicerepositoryentitydtomapperexception`</div></div>#### 1.3.1. Controller Layer (API Layer)

Chức năng:

- Nhận request HTTP
- Validate input DTO
- Gọi service xử lý
- Trả response chuẩn

Quy định:

- Không chứa business logic
- Không truy cập DB trực tiếp
- Không xử lý transaction
- Không map entity

Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-%40restcontroller-%40req"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-meta">@RestController</span><span class="hljs-meta">@RequestMapping("/api/users")</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserController</span> {    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> UserService userService;    <span class="hljs-meta">@PostMapping</span>    <span class="hljs-keyword">public</span> ApiResponse<UserResponse> <span class="hljs-title function_">create</span><span class="hljs-params">(<span class="hljs-meta">@Valid</span></span> <span class="hljs-meta">@RequestBody</span> UserRequest req) {        <span class="hljs-keyword">return</span> ApiResponse.success(userService.create(req));    }}`</div></div>---

#### 1.3.2. Service Layer (Business Layer)

Chức năng:

- Chứa toàn bộ business logic
- Điều phối repository
- Xử lý transaction
- Mapping entity ↔ DTO

Quy định:

- Không nhận HttpServletRequest
- Không trả Entity ra ngoài
- Phải qua DTO/Response
- Transaction đặt tại Service

Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-%40service-public-clas"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-meta">@Service</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserService</span> {    <span class="hljs-meta">@Transactional</span>    <span class="hljs-keyword">public</span> UserResponse <span class="hljs-title function_">create</span><span class="hljs-params">(UserRequest req)</span> {        <span class="hljs-type">User</span> <span class="hljs-variable">entity</span> <span class="hljs-operator">=</span> mapper.toEntity(req);        repository.save(entity);        <span class="hljs-keyword">return</span> mapper.toResponse(entity);    }}`</div></div>---

#### 1.3.3. Repository Layer (Persistence Layer)

Chức năng:

- Truy cập DB
- Query dữ liệu
- Mapping ORM

Quy định:

- Không chứa business logic
- Không gọi service
- Chỉ thao tác entity

Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-public-interface-use"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">UserRepository</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">JpaRepository</span><User, Long> {}`</div></div>---

#### 1.3.4. Entity Layer

Chức năng:

- Mapping bảng DB
- Quan hệ ORM

Quy định:

- Không chứa logic nghiệp vụ
- Không trả trực tiếp ra API
- Không dùng cho request/response

---

#### 1.3.5. DTO Layer

Gồm:

- Request DTO
- Response DTO

Chức năng:

- Trao đổi dữ liệu với client
- Tách biệt entity và API

Quy định:

- Controller chỉ nhận/trả DTO
- Không expose entity

---

#### 1.3.6. Mapper Layer

Chức năng:

- Chuyển đổi DTO ↔ Entity

Có thể dùng:

- MapStruct
- Manual mapping

Quy định:

- Không đặt trong controller
- Không đặt trong repository

---

### 1.4. Quy tắc phụ thuộc (Dependency Direction)

Dependency chỉ được phép đi theo một chiều:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-controller-%E2%86%92-service"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-attribute">Controller</span> → Service → Repository → DB`</div></div>Không được phép:

- Repository gọi Service
- Service gọi Controller
- Controller gọi Repository trực tiếp
- DTO gọi Repository

Nguyên tắc:

**Layer trên được gọi layer dưới  
Layer dưới không được gọi layer trên**

---

### 1.5. Tổ chức module theo domain

Project được tổ chức theo domain thay vì technical layer toàn cục.

Cấu trúc chuẩn:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-com.company.project-"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`com.company.project ├── common │    ├── config │    ├── <span class="hljs-keyword">exception</span> │    ├── util │ ├── <span class="hljs-keyword">user</span> │    ├── controller │    ├── service │    ├── repository │    ├── entity │    ├── dto │    ├── mapper │ ├── document │    ├── controller │    ├── service │    ├── repository │    ├── entity │    ├── dto │    ├── mapper`</div></div>---

### 1.6. Tích hợp hệ thống ngoài

Backend có thể tích hợp các thành phần sau:

- SSO / Identity Server
- Cache (Redis)
- Message Queue
- File Storage
- External API

Quy tắc tích hợp:

- Gọi qua Service
- Không gọi trực tiếp từ Controller
- Tách client class riêng

Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-integration-%E2%94%9C%E2%94%80%E2%94%80-sso-"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`integration ├── sso │    ├── SsoClient │ ├── redis │    ├── RedisService │ ├── <span class="hljs-keyword">external</span> │    ├── ExternalApiClient`</div></div>---

### 1.7. Nguyên tắc kiến trúc bắt buộc

Tất cả backend service phải tuân thủ:

- Không expose entity ra API
- Không viết business logic trong controller
- Không truy cập DB ngoài repository
- Không phụ thuộc ngược layer
- Transaction đặt tại service
- Mapping qua DTO

Vi phạm kiến trúc được xem là lỗi nghiêm trọng trong review code.

---

### 1.8. Khả năng mở rộng Microservice

Kiến trúc hiện tại cho phép tách module thành microservice khi cần:

Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-user-module-%E2%86%92-user-s"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`user <span class="hljs-variable language_">module</span> → user-service<span class="hljs-variable language_">document</span> <span class="hljs-variable language_">module</span> → <span class="hljs-variable language_">document</span>-serviceauth <span class="hljs-variable language_">module</span> → auth-service`</div></div>### 1.9. Xử lý ngoại lệ toàn cục (Exception Handling Strategy)

#### 1.9.1. Mục tiêu

Chuẩn hóa cơ chế xử lý lỗi toàn hệ thống nhằm:

- Đảm bảo response lỗi thống nhất giữa các API
- Ẩn thông tin nội bộ hệ thống
- Dễ dàng log và truy vết lỗi
- Hỗ trợ frontend xử lý lỗi chính xác
- Tránh trả lỗi không kiểm soát (stacktrace, SQL…)

Tất cả API backend phải sử dụng cơ chế xử lý ngoại lệ toàn cục.

---

#### 1.9.2. Global Exception Handler

Hệ thống sử dụng Global Exception Handler thông qua `@ControllerAdvice`.

Chức năng:

- Bắt tất cả exception phát sinh từ Controller / Service
- Mapping exception → HTTP status
- Trả response lỗi chuẩn
- Ghi log lỗi hệ thống

Quy định:

- Không xử lý lỗi trực tiếp trong Controller
- Không trả stacktrace ra client
- Không trả message DB/SQL
- Không throw Exception chung chung

Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-%40restcontrolleradvic"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-meta">@RestControllerAdvice</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">GlobalExceptionHandler</span> {    <span class="hljs-meta">@ExceptionHandler(ResourceNotFoundException.class)</span>    <span class="hljs-keyword">public</span> ResponseEntity<ApiError> <span class="hljs-title function_">handleNotFound</span><span class="hljs-params">(ResourceNotFoundException ex)</span> {        <span class="hljs-keyword">return</span> build(HttpStatus.NOT_FOUND, ex.getCode(), ex.getMessage());    }    <span class="hljs-meta">@ExceptionHandler(AccessDeniedException.class)</span>    <span class="hljs-keyword">public</span> ResponseEntity<ApiError> <span class="hljs-title function_">handleForbidden</span><span class="hljs-params">(AccessDeniedException ex)</span> {        <span class="hljs-keyword">return</span> build(HttpStatus.FORBIDDEN, <span class="hljs-string">"ACCESS_DENIED"</span>, <span class="hljs-string">"Access denied"</span>);    }    <span class="hljs-meta">@ExceptionHandler(MethodArgumentNotValidException.class)</span>    <span class="hljs-keyword">public</span> ResponseEntity<ApiError> <span class="hljs-title function_">handleValidation</span><span class="hljs-params">(MethodArgumentNotValidException ex)</span> {        <span class="hljs-keyword">return</span> build(HttpStatus.BAD_REQUEST, <span class="hljs-string">"VALIDATION_ERROR"</span>, <span class="hljs-string">"Invalid request"</span>);    }    <span class="hljs-meta">@ExceptionHandler(Exception.class)</span>    <span class="hljs-keyword">public</span> ResponseEntity<ApiError> <span class="hljs-title function_">handleSystem</span><span class="hljs-params">(Exception ex)</span> {        <span class="hljs-keyword">return</span> build(HttpStatus.INTERNAL_SERVER_ERROR, <span class="hljs-string">"SYSTEM_ERROR"</span>, <span class="hljs-string">"Internal server error"</span>);    }    <span class="hljs-keyword">private</span> ResponseEntity<ApiError> <span class="hljs-title function_">build</span><span class="hljs-params">(HttpStatus status, String code, String message)</span> {        <span class="hljs-type">ApiError</span> <span class="hljs-variable">error</span> <span class="hljs-operator">=</span> ApiError.of(code, message);        <span class="hljs-keyword">return</span> ResponseEntity.status(status).body(error);    }}`</div></div>---

#### 1.9.3. Mapping Exception → HTTP Status

Chuẩn mapping bắt buộc:

<div class="TyagGW_tableContainer" id="bkmrk-exception-http-statu"><div class="group TyagGW_tableWrapper flex flex-col-reverse w-fit" tabindex="-1"><table class="w-fit min-w-(--thread-content-width)" data-end="2826" data-start="2379"><thead data-end="2406" data-start="2379"><tr data-end="2406" data-start="2379"><th class="" data-col-size="sm" data-end="2391" data-start="2379">Exception</th><th class="" data-col-size="sm" data-end="2406" data-start="2391">HTTP Status</th></tr></thead><tbody data-end="2826" data-start="2434"><tr data-end="2473" data-start="2434"><td data-col-size="sm" data-end="2454" data-start="2434">ValidationException</td><td data-col-size="sm" data-end="2473" data-start="2454">400 Bad Request</td></tr><tr data-end="2516" data-start="2474"><td data-col-size="sm" data-end="2497" data-start="2474">MethodArgumentNotValid</td><td data-col-size="sm" data-end="2516" data-start="2497">400 Bad Request</td></tr><tr data-end="2561" data-start="2517"><td data-col-size="sm" data-end="2542" data-start="2517">IllegalArgumentException</td><td data-col-size="sm" data-end="2561" data-start="2542">400 Bad Request</td></tr><tr data-end="2601" data-start="2562"><td data-col-size="sm" data-end="2584" data-start="2562">AccessDeniedException</td><td data-col-size="sm" data-end="2601" data-start="2584">403 Forbidden</td></tr><tr data-end="2646" data-start="2602"><td data-col-size="sm" data-end="2626" data-start="2602">AuthenticationException</td><td data-col-size="sm" data-end="2646" data-start="2626">401 Unauthorized</td></tr><tr data-end="2690" data-start="2647"><td data-col-size="sm" data-end="2673" data-start="2647">ResourceNotFoundException</td><td data-col-size="sm" data-end="2690" data-start="2673">404 Not Found</td></tr><tr data-end="2730" data-start="2691"><td data-col-size="sm" data-end="2709" data-start="2691">BusinessException</td><td data-col-size="sm" data-end="2730" data-start="2709">422 Unprocessable</td></tr><tr data-end="2776" data-start="2731"><td data-col-size="sm" data-end="2747" data-start="2731">SystemException</td><td data-col-size="sm" data-end="2776" data-start="2747">500 Internal Server Error</td></tr><tr data-end="2826" data-start="2777"><td data-col-size="sm" data-end="2797" data-start="2777">Exception (default)</td><td data-col-size="sm" data-end="2826" data-start="2797">500 Internal Server Error</td></tr></tbody></table>

</div></div>Không được trả:

- 200 với lỗi business
- 500 với lỗi validation
- 500 với not found

---

#### 1.9.4. Chuẩn format response lỗi

Tất cả API khi lỗi phải trả format thống nhất:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-%7B-%22timestamp%22%3A-%222026"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-punctuation">{</span>  <span class="hljs-attr">"timestamp"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2026-02-23T10:15:30Z"</span><span class="hljs-punctuation">,</span>  <span class="hljs-attr">"code"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"USER_NOT_FOUND"</span><span class="hljs-punctuation">,</span>  <span class="hljs-attr">"message"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"User not found"</span><span class="hljs-punctuation">,</span>  <span class="hljs-attr">"traceId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"abc123"</span><span class="hljs-punctuation">}</span>`</div></div>Ý nghĩa:

<div class="TyagGW_tableContainer" id="bkmrk-field-m%C3%B4-t%E1%BA%A3-timestam"><div class="group TyagGW_tableWrapper flex flex-col-reverse w-fit" tabindex="-1"><table class="w-fit min-w-(--thread-content-width)" data-end="3296" data-start="3156"><thead data-end="3173" data-start="3156"><tr data-end="3173" data-start="3156"><th class="" data-col-size="sm" data-end="3164" data-start="3156">Field</th><th class="" data-col-size="sm" data-end="3173" data-start="3164">Mô tả</th></tr></thead><tbody data-end="3296" data-start="3190"><tr data-end="3217" data-start="3190"><td data-col-size="sm" data-end="3200" data-start="3190">timestamp</td><td data-col-size="sm" data-end="3217" data-start="3200">Thời điểm lỗi</td></tr><tr data-end="3242" data-start="3218"><td data-col-size="sm" data-end="3223" data-start="3218">code</td><td data-col-size="sm" data-end="3242" data-start="3223">Mã lỗi hệ thống</td></tr><tr data-end="3268" data-start="3243"><td data-col-size="sm" data-end="3251" data-start="3243">message</td><td data-col-size="sm" data-end="3268" data-start="3251">Thông báo lỗi</td></tr><tr data-end="3296" data-start="3269"><td data-col-size="sm" data-end="3277" data-start="3269">traceId</td><td data-col-size="sm" data-end="3296" data-start="3277">ID truy vết log</td></tr></tbody></table>

</div></div>Quy định:

- `code` phải ổn định, dùng cho frontend
- `message` có thể hiển thị user
- Không trả stacktrace
- Không trả SQL/exception message nội bộ

---

#### 1.9.5. Quy tắc định nghĩa Exception

Mỗi loại lỗi phải có class riêng:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-exception-%E2%94%9C%E2%94%80%E2%94%80-resour"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-variable">exception</span> ├── <span class="hljs-variable">ResourceNotFoundException</span> ├── <span class="hljs-variable">ValidationException</span> ├── <span class="hljs-variable">BusinessException</span> ├── <span class="hljs-built_in">SystemException</span>`</div></div>Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-public-class-resourc"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ResourceNotFoundException</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">RuntimeException</span> {    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String code;    <span class="hljs-keyword">public</span> <span class="hljs-title function_">ResourceNotFoundException</span><span class="hljs-params">(String code, String message)</span> {        <span class="hljs-built_in">super</span>(message);        <span class="hljs-built_in">this</span>.code = code;    }    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getCode</span><span class="hljs-params">()</span> {        <span class="hljs-keyword">return</span> code;    }}`</div></div>---

#### 1.9.6. Quy tắc sử dụng Exception

Service layer phải throw exception domain:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-public-user-getuser%28"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-keyword">public</span> User <span class="hljs-title function_">getUser</span><span class="hljs-params">(Long id)</span> {    <span class="hljs-keyword">return</span> repository.findById(id)        .orElseThrow(() -> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ResourceNotFoundException</span>(            <span class="hljs-string">"USER_NOT_FOUND"</span>,            <span class="hljs-string">"User not found"</span>        ));}`</div></div>Không được:

- throw new Exception()
- throw RuntimeException chung chung
- trả null thay vì lỗi
- trả Optional ra controller

---

#### 1.9.7. TraceId và logging

Mỗi request phải có traceId để truy vết lỗi:

- Sinh tại filter/interceptor
- Ghi vào MDC log
- Trả về response lỗi

Ví dụ:

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk-public-class-apierro"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ApiError</span> {    <span class="hljs-keyword">private</span> String timestamp;    <span class="hljs-keyword">private</span> String code;    <span class="hljs-keyword">private</span> String message;    <span class="hljs-keyword">private</span> String traceId;    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ApiError <span class="hljs-title function_">of</span><span class="hljs-params">(String code, String message)</span> {        <span class="hljs-type">ApiError</span> <span class="hljs-variable">e</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ApiError</span>();        e.timestamp = Instant.now().toString();        e.code = code;        e.message = message;        e.traceId = MDC.get(<span class="hljs-string">"traceId"</span>);        <span class="hljs-keyword">return</span> e;    }}`</div></div>---

#### 1.9.8. Nguyên tắc bắt buộc

Tất cả backend service phải tuân thủ:

- Mọi lỗi phải qua GlobalExceptionHandler
- Không trả exception raw ra client
- Không xử lý lỗi trong controller
- Exception phải có code
- Response lỗi phải đúng format chuẩn
- Mapping đúng HTTP status

Vi phạm quy tắc xử lý lỗi được xem là lỗi nghiêm trọng trong code review