Nhảy đến nội dung chính

Nội dung chi tiết

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

1.1. MụcGiới 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

Thiệu

Hệ thống backendđược xây dựng theo mô hình Enterprise Service Bus (ESB) kết hợp API Management (APIM), sử dụng nền tảng LifeESB làm trung gian điều phối giữa Client và các Backend Service. Tích hợp thêm Apache Kafka để xử lý tải lớn theo hình:hình bất đồng bộ.

ModularTên Monolithhệ thống: LayeredLifeESB ArchitectureAPIM: API Manager Message Queue: Apache Kafka (Broker: hdp-master:9092)

Đặc

điểm:


    1.2.
  • 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

đồĐồ tổngKiến thể:

Trúc Tổng Thể

Client (Web / Mobile / External System) ↓ Controller (API Layer) ↓ Service (Business Layer) ↓ Repository (Persistence Layer) ↓ DB

Tích hợp ngoài:service-architecture-layered.jpg

 


Client → API → Service → ↘ DB ↘ Redis Cache ↘ Message Queue ↘ SSO / Identity Server ↘ External API


1.3. Các Thành Phần Chính

1.3.1. QuyAPIM tắc phânAPI lớp (Layered Architecture)Manager

MỗiCổng modulevào phảiduy tuân thủ cấu trúc phân lớp sau:

controller service repository entity dto mapper exception

1.3.1. Controller Layernhất (APISingle Layer)

Entry

ChứcPoint) 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ụ:


@RestController @RequestMapping("/api/users") public class UserController { private final UserService userService; @PostMapping public ApiResponse<UserResponse> create(@Valid @RequestBody UserRequest req) { return ApiResponse.success(userService.create(req)); } }

1.3.2. Service Layer (Business Layer)

Chức năng:

  • Chứacho 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ụ:


@Service public class UserService { @Transactional public UserResponse create(UserRequest req) { User entity = mapper.toEntity(req); repository.save(entity); return mapper.toResponse(entity); } }

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ụ:


public interface UserRepository extends JpaRepository<User, Long> { }

1.3.4. Entity Layer

Chức năng:

  • Mapping bảng DB

  • Quan hệ ORMthống.

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:


Controller → Service → Repository → DB

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:


com.company.project ├── common │ ├── config │ ├── exception │ ├── util │ ├── user │ ├── controller │ ├── service │ ├── repository │ ├── entity │ ├── dto │ ├── mapper │ ├── document │ ├── controller │ ├── service │ ├── repository │ ├── entity │ ├── dto │ ├── mapper

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ụ:


integration ├── sso │ ├── SsoClient │ ├── redis │ ├── RedisService │ ├── external │ ├── ExternalApiClient

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ụ:


user module → user-service document module → document-service auth module → auth-service

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ụ:


@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<ApiError> handleNotFound(ResourceNotFoundException ex) { return build(HttpStatus.NOT_FOUND, ex.getCode(), ex.getMessage()); } @ExceptionHandler(AccessDeniedException.class) public ResponseEntity<ApiError> handleForbidden(AccessDeniedException ex) { return build(HttpStatus.FORBIDDEN, "ACCESS_DENIED", "Access denied"); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ApiError> handleValidation(MethodArgumentNotValidException ex) { return build(HttpStatus.BAD_REQUEST, "VALIDATION_ERROR", "Invalid request"); } @ExceptionHandler(Exception.class) public ResponseEntity<ApiError> handleSystem(Exception ex) { return build(HttpStatus.INTERNAL_SERVER_ERROR, "SYSTEM_ERROR", "Internal server error"); } private ResponseEntity<ApiError> build(HttpStatus status, String code, String message) { ApiError error = ApiError.of(code, message); return ResponseEntity.status(status).body(error); } }

1.9.3. Mapping Exception → HTTP Status

Chuẩn mapping bắt buộc:

Chức
Exception HTTP Status
ValidationException400 Bad Request
MethodArgumentNotValid400 Bad Request
IllegalArgumentException400 Bad Request
AccessDeniedException403 Forbidden
AuthenticationException401 Unauthorized
ResourceNotFoundException404 Not Found
BusinessException422 Unprocessable
SystemException500 Internal Server Error
Exception (default)500 Internal Server Error

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:


{ "timestamp": "2026-02-23T10:15:30Z", "code": "USER_NOT_FOUND", "message": "User not found", "traceId": "abc123" }

Ý nghĩa:

Fieldnăng Mô tả
timestampXác thực ThờiKiểm điểmtra lỗiJWT / OAuth2 / API Key trước khi vào LifeESB
codeRate Limiting Giới lỗihạn hệsố thốngrequest/giây theo từng API hoặc User
messageVersioning ThôngQuản báo lỗinhiều version API (/v1/v2) song song
traceIdAnalytics IDTheo truydõi vếtlưu loglượng, latency, error rate
PortalDeveloper Portal để publish và subscribe API

1.3.2. LifeESB

Lớp tích hợp trung gian, chịu trách nhiệm điều phối luồng dữ liệu.

định:

  • message

    thểuser

    Không

    trảSQL/exceptionmessagephảiclass
    Thành phầnFileChức năng
    Sync API

    QuyPlanningDirectApi.xml

    Gọi
      thẳng
    • Backend,

      codeClient phảinhận ổnkết định,quả dùngngay

    Async API

    KafkaProducerApi.xml
    Đẩy vào Kafka, trả phản hồi ngay cho frontend

    Client
    Inbound Endpoint hiển

    thịLoad_balance_example.xml
    Lắng nghe
  • Kafka,

    Khôngkích trảhoạt stacktrace

    Sequence
  • xử
  • Sequence nội

    bộ

    Load_balance_example-inboundSequence.xml
    Logic xử

    1.9.5.message: Quygọi tắcBackend định+ nghĩa Exception

    Mỗiphân loại lỗi

    Error riêng:Sequence

    Load_balance_example-inboundErrorSequence.xml
    Xử lý lỗi Kafka, đẩy vào error_topic
    Local Entry

    KafkaConnection.xml
    Cấu hình kết nối Kafka tái sử dụng

    1.3.3. Apache Kafka

    Message Broker xử lý tải lớn và đảm bảo không mất dữ liệu.

    TopicMục đích
    test_topic_01Nhận message từ API Producer (đầu vào)
    processed_topicLưu message đã xử lý thành công
    error_topicLưu message lỗi để review thủ công

    1.3.4. Backend Service

    REST API xử lý nghiệp vụ thực tế.

    Thông tinGiá trị
    Base URLhttp://192.168.0.133:8080
    Endpoint chínhPOST /api/v1/plannings
    Xác thựcJWT Bearer Token
    FrameworkSpring Boot

    1.4. Hai Mô Hình Dịch Vụ

    1.4.1. Đồng Bộ — Synchronous (Direct API)

    Client gọi và chờ kết quả từ Backend.



    exceptionClient → APIM → LifeESB (PlanningDirectApi)
    ├──
    ResourceNotFoundException │
    ├──
    ValidationException ▼
    ├──
    BusinessException Backend API [chờ response]
    ├──
    SystemException │
     ▼
     LifeESB → Client [trả kết quả thực]

    Đặc dụ:điểm:

    • Client biết ngay kết quả thành công hay thất bại
    • Backend bị quá tải → Client bị block, timeout
    • Phù hợp: nghiệp vụ cần xác nhận ngay, lượng request vừa phải

    Endpoint: POST /plannings-direct

    1.4.2. Bất Đồng Bộ — Asynchronous (Kafka)

    Client gửi và nhận phản hồi ngay (không chờ Backend xử lý xong).



    public class ResourceNotFoundException extends RuntimeException { private final String code; public ResourceNotFoundException(String code, String message) { super(message); this.code = code; } public String getCode() { return code; } }

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

    Service layer phải throw exception domain:


    ho-so-core-flow (1).jpg
    public User getUser(Long id) { return repository.findById(id) .orElseThrow(() -> new ResourceNotFoundException( "USER_NOT_FOUND", "User not found" )); }

    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ụ:


    Đặc điểm:

    • Client không bị block dù Backend chậm hoặc quá tải
    • Kafka buffer message, đảm bảo không mất dữ liệu
    • Phù hợp: tải lớn, xử lý nền, không cần kết quả ngay

    Endpoint: POST /kafka-producer


    1.5. So Sánh Hai Mô Hình

    classStringtimestamp;privateStringcode;Stringpublic String {ApiErrore = new ApiError(); e.timestamp = Instant.now().toString(); e.code = code; e.message = message; e.traceId = MDC.get("traceId"); return e; } }
    Tiêu chíĐồng bộ (public/plannings-direct) Bất ApiErrorđồng {bộ private(/kafka-producer)
    Client privatechờ message;Chờ privatekết Stringquả traceId;thực staticNhận ApiErrorsuccess ofngay lập tức
    Khả năng chịu tảiThấp hơn (StringBackend code,block) Cao (Kafka buffer)
    Độ trễPhụ thuộc BackendGần như bằng 0 (phía Client)
    Đảm bảo dữ liệuGhi ngay hoặc lỗi ngayKafka lưu, retry nếu fail
    Xử lý lỗifaultSequence → trả ngayerror_topic → review sau
    Phù hợpTải vừa, cần confirmTải lớn, xử lý nền
    Timeout15 giâyKhông (Kafka giữ message)

    1.9.8.6. Nguyên tắcTắc Kiến Trúc

    1. Single Entry Point: Mọi request đi qua APIM, không gọi thẳng LifeESB từ ngoài.
    2. Stateless Mediator: LifeESB không lưu trạng thái — toàn bộ state trong Kafka hoặc Backend DB.
    3. Error Isolation: Lỗi phải được bắt buộctrong

      TấtSequence, cảkhông backend service phải tuân thủ:

      • Mọiđể lỗi phảiraw quathoát GlobalExceptionHandler

        ra ngoài.
      • Dead

        KhôngLetter trảPattern: exceptionMessage rawlỗi raluôn client

        được chuyển vào error_topic, không bao giờ bị drop.
      • Observability:

        KhôngMọi bước xử lý lỗi trong controller

      • Exception phảiđều có codelog level="custom" để trace theo luồng.


    1.7. Môi Trường Triển Khai

    Thành phầnHost / PortGhi chú
    LifeESB192.168.0.167:8290HTTP endpoint
    LifeESB Management192.168.0.167:9201Deploy CAR file
    Kafka Brokerhdp-master:9092PLAINTEXT
    Backend API192.168.0.133:8080Spring Boot

    📸 [Ảnh minh họa] — LifeESB Dashboard sau khi deploy

  • Response lỗi phải đúng format chuẩn

    image.png

  • Mapping📸 đúng[Ảnh HTTPminh statushọa] — Kafka UI / Kafka Manager hiển thị các topic

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