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ý API Backend (API Processing Flow)

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


Mô tả luồng xử lý chuẩn của một API backend từ khi nhận request đến khi deploy production nhằm:

Thống nhất cách xây dựng API giữa các team

Đảm bảo tuân thủ đầy đủ các quy định kiến trúc

Dễ trace lỗi và audit

Liên kết các chuẩn đã quy định ở các chương trước

1.2. Phạm vi áp dụng:

Áp dụng cho:

Tất cả REST API trong modules/**

Core Team và Partner Team

Tất cả service Spring Boot

1.3. Tổng quan luồng xử lý API


Luồng chuẩn:

image.png


1.4. Các bước xử lý chi tiết


Bước 1. Client gọi API


Ví dụ:

POST /api/v1/users
Payload:

{
  "username": "test",
  "password": "123456"
}
Quy định liên quan:

👉 Chương 7 — API Design & Response Standard

Bước 2. Security / Authentication


Hệ thống kiểm tra:

JWT / SSO token

Permission

Role

Nếu fail:

401 Unauthorized
403 Forbidden
Quy định liên quan:

👉 Chương 8 — Cơ chế xác thực & SSO (WSO2)

Bước 3. Controller nhận request

@PostMapping
public ApiResponse<UserResponse> create(
        @Valid @RequestBody UserCreateRequest request) {
    return ApiResponse.ok(userService.create(request));
}
Quy định:

Name Controller

Không chứa business logic

👉 Chương 3 — Coding Convention
👉 Chương 7 — API Design

Bước 4. DTO Validation


DTO:

public class UserCreateRequest {

    @NotBlank
    @Size(max = 100)
    private String username;

    @NotBlank
    private String password;
}
Spring tự validate trước khi vào service.

Nếu lỗi:

400 Bad Request
Response chuẩn:


{
  "code": "VALIDATION_ERROR",
  "message": "username is required"
}
Quy định:

👉 Chương 3 — Coding Convention (Validation)
👉 Chương 7 — Response Standard

Bước 5. Service xử lý logic

public UserResponse create(UserCreateRequest req) {
    log.info("Create user {}", req.getUsername());

    UserEntity e = mapper.toEntity(req);
    repo.save(e);

    return mapper.toResponse(e);
}
Cấu trúc chuẩn 1 hàm service:

image.png


Quy định:

👉 Chương 3 — Coding Convention
👉 Chương 4 — Entity & Database

Bước 6. Repiository & Database

userRepository.save(entity);
Nếu thay đổi schema:

update entity

migration script

Quy định:

👉 Chương 4 — Thay đổi Entity & DB
👉 Chương 5 — Schema & Migration

Bước 7. Mapping Response

return UserResponse.builder()
        .id(e.getId())
        .username(e.getUsername())
        .build();
Response chuẩn:


{
  "code": "SUCCESS",
  "data": {
    "id": 1,
    "username": "test"
  }
}
Quy định:

👉 Chương 7 — Response Standard

Bước 8. Logging & Audit


Trong service:


log.info("User created id={}", e.getId());
Nếu nghiệp vụ quan trọng:

ghi audit_log

Quy định:

👉 Chương 3 — Logging
👉 Chương 6 — Audit Log

Bước 9. Exception Handling


Ví dụ:


if (exists) {
    throw new BusinessException("USER_EXISTS");
}
Global handler:


@ExceptionHandler(BusinessException.class)
Response:


{
  "code": "USER_EXISTS",
  "message": "User already exists"
}
Quy định:

👉 Chương 7 — Response & Error

1.5. Luồng phát triển & release API


Sau khi code xong:


Dev → Commit → PR → Review → Merge → CI Build → Deploy


Bước 10. Commit & PR


Quy định:

👉 Chương 9 — Git Workflow & Pull Request

Bước 11. Checklist trước merge


Quy định:

👉 Chương 10 — Checklist merge

Bước 12. Deploy & Update DB


Nếu có migration:

chạy script

deploy service

Quy định:

👉 Chương 5 — Migration
👉 Chương 12 — Connect DB

1.6. Sơ đồ tổng thể API lifecycle


https://docs.lifetex.vn/link/333#bkmrk-request-%E2%86%93-auth-%28ch8%29

Request
  ↓
Auth (Ch8)
  ↓
Controller (Ch3,7)
  ↓
DTO Validate (Ch3)
  ↓
Service Logic (Ch3)
  ↓
Entity/DB (Ch4,5)
  ↓
Mapping (Ch7)
  ↓
Logging/Audit (Ch3,6)
  ↓
Response (Ch7)
  ↓
Git/PR (Ch9,10)
  ↓
Deploy (Ch5,12)

1.7. Transaction trong xử lý API

1.7.1. Nguyên tắc transaction

Mọi thao tác thay đổi dữ liệu nghiệp vụ trong API phải được thực hiện trong transaction nhằm đảm bảo:

Transaction phải được đặt tại Service layer.


1.7.2. Quy định transaction

Transaction:

Không được đặt transaction tại:


1.7.3. Ví dụ transaction chuẩn


@Transactional public UserResponse create(UserCreateRequest req) { if (repo.existsByUsername(req.getUsername())) { throw new BusinessException("USER_EXISTS"); } UserEntity e = mapper.toEntity(req); repo.save(e); auditService.logCreate("USER", e.getId()); return mapper.toResponse(e); }

1.7.4. Quy tắc rollback

Transaction phải rollback khi:

Không được:


1.8. Security Context trong Service

1.8.1. Khái niệm

Security Context là thông tin người dùng hiện tại sau khi xác thực (JWT / SSO).

Thông tin thường có:


1.8.2. Sử dụng trong Service

Service được phép truy cập Security Context để:

Ví dụ:


Long userId = SecurityUtils.getCurrentUserId(); String username = SecurityUtils.getCurrentUsername();

1.8.3. Quy định sử dụng

Không được:

Permission check phải tại Service layer.


1.8.4. Ví dụ kiểm tra quyền


public void updateUser(Long id, UserUpdateRequest req) { Long currentUser = SecurityUtils.getCurrentUserId(); if (!permissionService.canUpdateUser(currentUser, id)) { throw new AccessDeniedException("NO_PERMISSION"); } ... }

1.9. Xử lý API danh sách (List API)

1.9.1. Nguyên tắc List API

API trả danh sách phải hỗ trợ:

Mục tiêu:


1.9.2. Chuẩn request List API

Ví dụ:


GET /api/v1/users?page=0&size=20&sort=createdAt,desc

Tham số chuẩn:


1.9.3. Chuẩn response List API


{ "code": "SUCCESS", "data": { "content": [], "page": 0, "size": 20, "totalElements": 100, "totalPages": 5 } }

1.9.4. Quy định bắt buộc


1.10. Gọi hệ thống ngoài trong API

1.10.1. Phạm vi

API có thể cần gọi:


1.10.2. Kiến trúc gọi ngoài

Luồng chuẩn:

Service → Integration Client → External System

Ví dụ:


UserInfo info = ssoClient.getUser(token);

1.10.3. Quy định

Không được gọi external tại:

Phải:


1.10.4. Xử lý lỗi external

Nếu external fail:


1.11. Monitoring & Metrics trong API

1.11.1. Mục tiêu

Ngoài Logging & Audit, API production cần hỗ trợ:


1.11.2. Metrics cần có


1.11.3. Trace request

Mỗi request phải có:

TraceId phải liên kết:


1.12. Versioning API

1.12.1. Chuẩn version

API phải có version trong URL:


/api/v1/...

1.12.2. Nguyên tắc version


1.12.3. Ví dụ


/api/v1/users /api/v2/users

1.13. Testing trước merge

1.13.1. Mục tiêu

Đảm bảo API hoạt động đúng trước khi merge và deploy.


1.13.2. Loại test bắt buộc


1.13.3. Quy định

API mới hoặc thay đổi logic phải có test:


1.14. Hiệu năng & Database

1.14.1. Nguyên tắc thiết kế DB trong API

API phải đảm bảo:


1.14.2. Các lỗi cần tránh


1.14.3. Kiểm tra khi review

Khi review API cần kiểm tra:

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ý mã nguồn giữa Core Team và các đơn vị Partner nhằm:

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

Tài liệu áp dụng cho:

Cấu trúc dự án chuẩn:

com.example.demo

 ├── common/   (thành phần dùng chung)

 ├── config/   (cấu hình hệ thống)

 ├── modules/  (các module nghiệp vụ)

 └── DemoApplication.java


Ý nghĩa từng khu vực

Thư mục

Vai trò

Phạm vi sử dụng

common

Thành phần dùng chung toàn hệ thống

Core quản lý

config

Cấu hình hệ thống, security, hạ tầng

Core quản lý

modules

Các module nghiệp vụ

Core + Partner

1.3. Quy định chính

Phân quyền quản lý code

Khu vực

Đơn vị quản lý

Quyền chỉnh sửa

common/**

Core Team

Partner không được sửa

config/**

Core Team

Partner không được sửa

modules/**

Core + Partner

Được phép phát triển

Quy định bắt buộc

Đối tác KHÔNG được phép sửa trực tiếp:

common/**

config/**

Mọi thay đổi trong các khu vực này phải:

  1. Tạo yêu cầu thay đổi (Core Change Request)

  2. Được Core Team xem xét

  3. Core Team phê duyệt và thực hiện

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

Quy trình thay đổi code dùng chung

Bước 1: Partner phát hiện nhu cầu thay đổi
Bước 2: Tạo ticket với tiêu đề: các phần mềm quản lý công việc liên quan (Jira / Redmine / YouTrack) hoặc nội bộ

[CORE CHANGE REQUEST] Thêm field phone vào user_account

Luồng xử lý: 

  1. Partner tạo ticket

  2. Core Team review

  3. Chuyển trạng thái:

Trạng thái

Ý nghĩa

Open

Ticket mới

Under Review

Core đang đánh giá

Approved

Đồng ý thay đổi

Rejected

Từ chối

Implemented

Core đã code

Merged

Đã merge vào develop


Bước 3: Core Team đánh giá:

Bước 4: Nếu hợp lệ:

Bước 5: Partner pull code mới về và tiếp tục phát triển

1.5. Ví dụ minh họa

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

Partner cần thêm logic trong module planning:

modules/planning/service/PlanningService.java

→ Được phép sửa trực tiếp và tạo PR.

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

Partner muốn sửa:

common/security/JwtAuthenticationFilter.java

→ Không được sửa trực tiếp.

Phải thực hiện:

  1. Tạo Core Change Request

  2. Chờ Core Team phê duyệt

  3. Core Team thực hiện thay đổi

1.6. Checklist áp dụng

Trước khi commit hoặc tạo PR, cần kiểm tra:

2.Quy chuẩn phát triển module

2.1. Mục tiêu chương

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

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

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

Cấu trúc chuẩn mỗi module

modules/<module-name>/

  controller/ 

  service/

  repository/

  entity/

  dto/

Ý nghĩa từng thành phần

Layer

Vai trò

ví dụ 

controller

Nhận request từ client và trả  response

@PostMapping

public ApiResponse<PlanningResponse> create(@Valid @RequestBody PlanningCreateRequest req) {

   return ApiResponse.ok(service.create(req));

}


service

Xử lý logic nghiệp vụ

public PlanningResponse get(Long id) {

   PlanningEntity e = repo.findById(id).orElseThrow(() ->

           new AppException(ErrorCode.NOT_FOUND, HttpStatus.NOT_FOUND, "Planning not found: " + id)

   );

   return toResponse(e);

}

repository

Truy cập và thao tác dữ liệu

public interface PlanQueryRepository {

   long count(PlanListFilter filter);

   List<PlanListItem> list(PlanListFilter filter);

}


entity

Mapping bảng database

public class PlanningEntity {

   @Id

   @GeneratedValue(strategy = GenerationType.IDENTITY)

   private Long id;

}

dto

Model request và response

public class PlanListFilter {

   private String keyword;

   private Integer page = 1; // 1-based

   private Integer size = 20;

   private String sortBy = "created_at";

   private String sortDir = "desc";


   public String getKeyword() { return keyword; }

   public void setKeyword(String keyword) { this.keyword = keyword; }

   public Integer getPage() { return page; }

   public void setPage(Integer page) { this.page = page; }

   public Integer getSize() { return size; }

   public void setSize(Integer size) { this.size = size; }

   public String getSortBy() { return sortBy; }

   public void setSortBy(String sortBy) { this.sortBy = sortBy; }

   public String getSortDir() { return sortDir; }

   public void setSortDir(String sortDir) { this.sortDir = sortDir; }

}

2.3. Quy định chính

Trách nhiệm từng layer

Controller
Service
Repository
Entity
DTO

Quy tắc bắt buộc

  1. Controller không được chứa logic nghiệp vụ

  2. Không trả trực tiếp Entity ra API

  3. Luôn dùng DTO cho request/response

  4. Response chuẩn toàn hệ thống: ApiResponse<T>

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

Quy trình phát triển một chức năng mới

Bước 1: Tạo DTO

Ví dụ:

UserCreateRequest

UserResponse

Bước 2: Tạo Entity

Mapping với bảng database:

UserEntity

Bước 3: Tạo Repository

UserRepository

Bước 4: Tạo Service

UserService

Xử lý logic:

Bước 5: Tạo Controller

UserController

image.png

2.5. Ví dụ minh họa

Ví dụ sai (vi phạm quy tắc)

Controller chứa logic:

@GetMapping("/users")

public List<UserEntity> getAll() {

    return userRepository.findAll();

}

Sai vì:


Ví dụ đúng

@GetMapping("/users")

public ApiResponse<List<UserResponse>> getAll() {

    return ApiResponse.ok(userService.getAll());

}

Service:

public List<UserResponse> getAll() {

    return userRepository.findAll()

        .stream()

        .map(this::toResponse)

        .toList();

}

2.6. Checklist áp dụng

Trước khi commit module mới:

3.Coding convention

3.1. Mục tiêu chương

Chương này quy định chuẩn coding và nguyên tắc thiết kế nhằm:


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

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


3.3. Quy định chính

3.3.1. Quy tắc đặt tên

Các thành phần phải đặt tên theo quy ước thống nhất:

Thành phần Quy tắc đặt tên Ví dụ
Controller <Name>Controller UserController
Service <Name>Service UserService
Repository <Name>Repository UserRepository
Entity <Name>Entity UserEntity
DTO Request <Name>CreateRequest, <Name>UpdateRequest UserCreateRequest
DTO Response <Name>Response UserResponse
Mapper <Name>Mapper UserMapper
Enum <Name>Enum UserStatusEnum

3.3.2. Quy tắc chung Java

Dùng camelCase cho:

Dùng PascalCase cho:

Ví dụ:


private String fullName; // đúng private String FullName; // sai public class UserService { } // đúng public class userService { } // sai

3.3.3. Quy định về logging

Không được dùng:


System.out.println("Error");

Phải dùng logging chuẩn:


private static final Logger log = LoggerFactory.getLogger(UserService.class); log.info("User created"); log.error("Create user failed", ex);

3.3.4. Validation dữ liệu đầu vào

Tất cả dữ liệu đầu vào phải được validate bằng annotation.

Annotation chuẩn:

Ví dụ:


public class UserCreateRequest { @NotBlank @Size(max = 100) private String username; @NotBlank private String password; }

3.4. Nguyên lý thiết kế SOLID áp dụng trong dự án

3.4.1. S — Single Responsibility

Một class chỉ có một trách nhiệm.

Quy định dự án:

Sai:


public class UserController { @Autowired UserRepository repo; // sai tầng }

Đúng:


public class UserController { @Autowired UserService userService; }

3.4.2. O — Open/Closed

Code phải mở rộng được nhưng không sửa code cũ.

Áp dụng:

Ví dụ:


public interface NotificationService { void send(Message msg); }

Triển khai:


public class EmailNotificationService implements NotificationService { } public class SmsNotificationService implements NotificationService { }

3.4.3. L — Liskov Substitution

Class con có thể thay thế class cha.

Quy định:

Sai:


public class UserServiceImpl implements UserService { public User get(String id) { throw new UnsupportedOperationException(); } }

3.4.4. I — Interface Segregation

Không tạo interface quá lớn.

Sai:


public interface UserService { create(); update(); delete(); exportExcel(); sendEmail(); }

Đúng:


public interface UserService { } public interface UserExportService { } public interface UserNotificationService { }

3.4.5. D — Dependency Inversion

Phụ thuộc abstraction, không phụ thuộc implementation.

Quy định:

Sai:


UserService service = new UserServiceImpl();

Đúng:


@Autowired UserService userService;

3.5. Quy tắc kiến trúc Spring Boot trong dự án

3.5.1. Luồng chuẩn tầng


Controller → Service → Repository → Database

Không được:


Controller → Repository Controller → EntityManager

3.5.2. Controller không chứa nghiệp vụ

Sai:


@PostMapping public User create(...) { user.setCreatedDate(LocalDateTime.now()); // sai }

Đúng:


@PostMapping public UserResponse create(...) { return userService.create(request); }

3.5.3. Service không chứa logic HTTP

Sai:


throw new ResponseStatusException(HttpStatus.BAD_REQUEST);

Đúng:


throw new BusinessException("USER_EXISTS");

3.5.4. Repository chỉ truy vấn dữ liệu

Không chứa:


3.6. Design pattern sử dụng trong hệ thống

3.6.1. Service pattern

Service là tầng nghiệp vụ trung tâm.


Controller → Service → Repository

3.6.2. DTO pattern

Tách DTO khỏi Entity.

Không trả Entity ra API.

Sai:


public UserEntity create(...) { }

Đúng:


public UserResponse create(...) { }

3.6.3. Mapper pattern

Convert DTO ↔ Entity.


UserEntity entity = mapper.toEntity(request);

3.6.4. Exception pattern

Dùng BusinessException thống nhất.


throw new BusinessException(ErrorCode.USER_NOT_FOUND);

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

Quy trình tạo DTO đúng chuẩn

Bước 1: Tạo class DTO theo quy tắc đặt tên

Ví dụ:

Bước 2: Thêm validation annotation

Bước 3: Dùng DTO trong controller


@PostMapping public ApiResponse<UserResponse> create( @Valid @RequestBody UserCreateRequest request) { return ApiResponse.ok(userService.create(request)); }

Quy trình logging chuẩn

Bước 1: Khai báo logger trong class


private static final Logger log = LoggerFactory.getLogger(UserService.class);

Bước 2: Dùng logger thay cho System.out.println


3.8. Ví dụ minh họa

Trường hợp sai


public class usercontroller { public void CreateUser() { System.out.println("Create user"); } }

Sai vì:


Trường hợp đúng


@RestController public class UserController { private static final Logger log = LoggerFactory.getLogger(UserController.class); private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @PostMapping public ApiResponse<UserResponse> create( @Valid @RequestBody UserCreateRequest request) { log.info("Create user {}", request.getUsername()); return ApiResponse.ok(userService.create(request)); } }

Ví dụ DTO sai


public class userDTO { public String name; }

Sai vì:


Ví dụ DTO đúng


public class UserCreateRequest { @NotBlank @Size(max = 255) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }

3.9. Checklist áp dụng

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

Naming


Coding


Validation


SOLID


Architecture

3.10. Cấu trúc chuẩn của một hàm 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 hàm xử lý nghiệp vụ nhằm:


3.10.2. Cấu trúc chuẩn bắt buộc

Một hàm service/controller phải theo thứ tự:


1. Validate input 2. Log input 3. Business logic 4. Catch exception (nếu cần) 5. Trả response / throw BusinessException

3.10.3. Quy định chi tiết từng phần

1. Validate

Ví dụ:


if (repo.existsByUsername(request.getUsername())) { throw new BusinessException(ErrorCode.USER_EXISTS); }

2. Log

Phải log:


log.info("Create user {}", request.getUsername());

3. Business logic

Chỉ chứa xử lý nghiệp vụ:


UserEntity entity = mapper.toEntity(request); repo.save(entity);

4. Exception handling

Không catch Exception chung chung nếu không xử lý.

Sai:


try { repo.save(entity); } catch (Exception e) { }

Đúng:


try { repo.save(entity); } catch (DataIntegrityViolationException ex) { log.error("DB error", ex); throw new BusinessException(ErrorCode.DB_ERROR); }

5. Response

Service:

Controller:


return UserResponse.from(entity);

Controller:


return ApiResponse.ok(userService.create(request));

3.10.4. Ví dụ hàm sai


public User create(UserCreateRequest req) { UserEntity e = new UserEntity(); e.setUsername(req.getUsername()); repo.save(e); return e; }

Sai vì:


3.10.5. Ví dụ hàm đúng chuẩn dự án


public UserResponse create(UserCreateRequest request) { // 1. Validate if (repo.existsByUsername(request.getUsername())) { throw new BusinessException(ErrorCode.USER_EXISTS); } // 2. Log log.info("Create user {}", request.getUsername()); // 3. Business logic UserEntity entity = mapper.toEntity(request); repo.save(entity); // 4. Response return mapper.toResponse(entity); }

3.10.6. Checklist

Khi review hàm service/controller:

4. Quy định thay đổi cấu trúc dữ liệu (Entity & Database)

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


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:

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


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

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

4.3. Quy định chính


4.3.1. Không được tự ý thay đổi các field chuẩn

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

Quy định bắt buộc

Không được:

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


4.3.2. Thêm field mới trong module

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


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:

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


Trường hợp 1: Thêm field trong module

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

modules/<module>/entity/**


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

V<timestamp>__add_<field>_to_<table>.sql


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

Bước 4: Core Team review:

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] <mô tả thay đổi>

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

Bước 3: Core Team review:

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

4.5. Ví dụ minh họa


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:

4.6. Checklist áp dụng


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

5.Quy định về Database, Schema và Migration

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


Chương này quy định cách quản lý cấu trúc database và các thay đổi schema nhằm:

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


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

Hệ thống phân loại bảng dữ liệu thành hai nhóm:


1. Core tables (dùng chung toàn hệ thống)

Bao gồm các bảng:

Đây là các bảng dữ liệu lõi, được dùng chung cho:


2. Module tables

Bao gồm:

Ví dụ:

5.3. Quy định chính

5.3.1. Quy định với Core tables

Partner không được phép:

Mọi thay đổi liên quan đến core tables phải thông qua:

DB Core Change Request -> sử dụng jira/… hoặc các phần mềm quản lý công việc nội bộ

Và do Core Team thực hiện.


5.3.2. Quy định với Module tables

Partner được phép:

Nhưng phải tuân thủ:


5.3.3. Migration bắt buộc

Mọi thay đổi database phải tuân thủ các nguyên tắc sau:

Công cụ sử dụng:


Format migration

Tên file migration:

V<timestamp>__<description>.sql


Ví dụ:

V20260209__create_planning_table.sql

V20260210__add_priority_to_planning.sql

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


Trường hợp 1: Thêm hoặc sửa bảng trong module

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

modules/<module>/entity/**


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

V<timestamp>__<description>.sql


Bước 3: Commit:

Bước 4: Tạo Pull Request

Bước 5: Core Team review:

Bước 6: Merge vào develop


Trường hợp 2: Thay đổi core tables

Bước 1: Tạo ticket:

[DB CORE CHANGE REQUEST] <mô tả thay đổi>

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

Bước 3: Core Team review

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

Merge vào develop

5.5. Ví dụ minh họa


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

Partner thêm bảng mới:

CREATE TABLE PLANNING (

    ID BIGINT PRIMARY KEY,

    NAME VARCHAR(255),

    DESCRIPTION VARCHAR(1000)

);


→ Thuộc module table → được phép.


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

Partner chạy trực tiếp trên production:

ALTER TABLE USER_ACCOUNT ADD PHONE VARCHAR(20);


→ Sai quy định vì:


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

Partner muốn:

ALTER TABLE ROLE ADD DESCRIPTION VARCHAR(255);


→ Đây là core table.

Phải tạo: trên phần mềm quản lý công việc nội bộ hoặc phần mềm nghiệp vụ tương đương

[DB CORE CHANGE REQUEST]

5.6. Checklist áp dụng


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

5.7. Quy định thay đổi cấu trúc dữ liệu (Entity & Database)


5.7.1. Mục tiêu chương


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:

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


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

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

5.7.3. Quy định chính


5.7.3.1. Không được tự ý thay đổi các field chuẩn

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

Quy định bắt buộc

Không được:

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


5.7.3.2. Thêm field mới trong module

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


5.7.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:

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

Data Model Change Request

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


Trường hợp 1: Thêm field trong module

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

modules/<module>/entity/**


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

V<timestamp>__add_<field>_to_<table>.sql


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

Bước 4: Core Team review:

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] <mô tả thay đổi>

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

Bước 3: Core Team review:

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

5.7.5. Ví dụ minh họa


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:

5.7.6. Checklist áp dụng


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

6.Audit Log & System Metadata

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


Chương này quy định cách quản lý dữ liệu audit nhằm:

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


Audit log là dữ liệu ghi nhận:

Các bảng audit:

Các bảng này được coi là dữ liệu core của hệ thống.

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

Mọi thay đổi liên quan đến audit

6.3. Quy định chính


Partner không được phép:

Các bảng audit được coi là:

Core system data


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

phải được:

Core Team phê duyệt

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


Trường hợp 1: Module cần ghi thêm audit

Bước 1: Sử dụng cơ chế audit có sẵn của hệ thống


Ví dụ:

Bước 2: Không được tự tạo bảng audit riêng

Bước 3: Nếu cần mở rộng dữ liệu audit:

Tạo ticket:

[AUDIT CHANGE REQUEST] <mô tả thay đổi>

Bước 4: Core Team review:


Trường hợp 2: Cần thay đổi cấu trúc bảng audit

Bước 1: Tạo ticket:

[DB CORE CHANGE REQUEST] Audit table change

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

Bước 3: Core Team review và quyết định

6.5. Ví dụ minh họa

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

Module planning muốn ghi log khi tạo kế hoạch:

auditService.logCreate("PLANNING", planningId, userId);


→ Sử dụng cơ chế audit có sẵn → hợp lệ.


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

Partner tự tạo bảng:

CREATE TABLE planning_audit (

    id BIGINT,

    action VARCHAR(50)

);

→ Sai quy định vì:


Trường hợp bị cấm

Partner chạy trực tiếp:

DELETE FROM audit_log WHERE created_at < '2024-01-01';


→ Không được phép xóa dữ liệu audit.

6.6. Checklist áp dụng


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

7.API Design & Response Standard

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


Chương này quy định chuẩn API và định dạng response nhằm:

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


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

Mọi API phải tuân theo:

7.3. Quy định chính


7.3.1. Chuẩn Base Path

Tất cả API phải có dạng:

/api/v1/<module-name>

vd: http://localhost:8080/api/v1/auth/config

Trong đó:


Ví dụ base path hợp lệ

Module

Base path

auth

/api/v1/auth

planning

/api/v1/planning

document

/api/v1/document

user

/api/v1/user


7.3.2. Chuẩn Response

Tất cả API phải trả về:

ApiResponse<T>

image.png

Không được trả:

trực tiếp ra API.


Cấu trúc chuẩn của ApiResponse

Ví dụ:

{

  "success": true,

  "message": "OK",

  "data": {

    "id": 1,

    "name": "Test"

  }

}


Hoặc với danh sách:

{

  "success": true,

  "message": "OK",

  "data": [

    { "id": 1, "name": "A" },

    { "id": 2, "name": "B" }

  ]

}

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

Bước 1: Tạo controller theo base path

Ví dụ:

@RestController

@RequestMapping("/api/v1/planning")

public class PlanningController {

}



Bước 2: Service trả DTO

public PlanningResponse getById(Long id) {

    // xử lý nghiệp vụ

}



Bước 3: Controller trả ApiResponse

@GetMapping("/{id}")

public ApiResponse<PlanningResponse> getById(@PathVariable Long id) {

    return ApiResponse.ok(planningService.getById(id));

}

7.5. Ví dụ minh họa

Trường hợp sai

@GetMapping("/planning")

public List<PlanningEntity> getAll() {

    return planningRepository.findAll();

}


Sai vì:


Trường hợp đúng

@GetMapping

public ApiResponse<List<PlanningResponse>> getAll() {

    return ApiResponse.ok(planningService.getAll());

}



Trường hợp sai về base path

@RequestMapping("/planning")


→ Sai vì thiếu:

/api/v1/

7.6. Checklist áp dụng


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

8. Cơ chế xác thực & tích hợp SSO (WSO2)

8.1. Mục tiêu chương


Quy định cơ chế xác thực linh hoạt giữa:

Nhằm:

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

Hai chế độ xác thực:

Chế độ

Mô tả

Local

Login nội bộ

WSO2

Login qua SSO OIDC

8.3. Quy định chính


Nguyên tắc chung

Logic xác thực nằm trong:

modules/auth

common/security


Cấu hình chế độ đăng nhập

auth.type = local

auth.type = wso2



Local Login


WSO2 SSO flow

  1. FE redirect sang WSO2

  2. User login

  3. WSO2 trả access_token

FE gọi BE với:

Authorization: Bearer <token>

  1. BE validate token


Quy tắc FE

Không được hardcode.


Quy tắc BE

Không hardcode user

8.4. Quy trình thêm FE mới vào SSO


  1. Partner cung cấp:

  2. Core tạo OAuth client

  3. Cấp client_id, scope

8.5. Checklist tích hợp xác thực


9.Git Workflow & Pull Request

Chú ý: tham chiếu sang tài liệu để theo dõi rõ ràng đầy đủ hơn:  https://docs.lifetex.vn/books/quy-dinh-git-workflow-va-quy-uoc-code c
9.1. Mục tiêu chương


Chương này quy định chuẩn commit và quy trình merge code nhằm:

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


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

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

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

9.3. Quy định chính

9.3.1. Format commit

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

<type>: <description>


Trong đó:

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


Type hợp lệ

Type

Ý nghĩa

feat

Thêm chức năng mới

fix

Sửa lỗi

refactor

Tối ưu, không đổi logic

docs

Cập nhật tài liệu

test

Thêm hoặc sửa test

chore

Thay đổi cấu hình, build, dependency


9.3.2. Branch strategy

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

Branch

Mục đích

main

Bản stable, dùng cho production

develop

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

feature/*

Phát triển chức năng mới

fix/*

Sửa lỗi


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


Ví dụ tên branch hợp lệ

feature/planning-create-api

feature/document-upload

fix/login-null-pointer

fix/planning-date-validation


9.3.3. Điều kiện merge Pull Request

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/**

  1. Không sửa shared entity

Có review từ Core Team

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


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

9.5. Ví dụ minh họa

Trường hợp commit sai

Sai vì:


Trường hợp commit đúng

fix: handle null planning name


Trường hợp merge sai

Partner:

→ 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

9.6. Checklist áp dụng


Trước khi tạo Pull Request:

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

10.1. Mục tiêu chương


Chương này quy định checklist bắt buộc trước khi merge code nhằm:

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


Checklist này áp dụng cho:

Checklist được dùng trong:

Kiểm tra trước khi release

10.3. Quy định chính


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/**

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


common/entity/**

  1. Nếu có thay đổi database:

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

/api/v1/<module-name>


  1. Response phải dùng:

ApiResponse<T>


10.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:

10.5. Ví dụ minh họa

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

PR:

Chỉ sửa code trong:

modules/planning/**

→ Đượ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;

→ PR bị reject.

10.6. Checklist áp dụng


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 đủ.

11.Quy trình phối hợp & quản lý thay đổi liên nhóm

11.1. Mục tiêu chương


Chương này quy định cách phối hợp giữa nhiều đơn vị phát triển nhằm:

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


Áp dụng khi:

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

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

11.3. Quy định chính

Nguyên tắc chung


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

Rủi ro:

Quy định:

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:

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:

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:

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:

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:

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:

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:

Quy trình:

  1. Tạo MASTER DATA REQUEST

  2. Core duyệt và triển khai

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


  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

11.5.Ví dụ minh họa


1. bảng mô tả các môi trường hệ thống:

Env FE URL BE URL DB Auth
dev http://localhost:4200 http://localhost:8080 local mock
staging https://staging.app.vn https://staging.api.vn shared WSO2 thật
prod https://app.vn https://api.vn prod WSO2 thật

12. Connect Database & mẫu CRUD & switch Oracle/MSSQL

12.Quy định kết nối Database và Message Queue

12.1. Mục tiêu chương


Chuẩn hóa kết nối DB và Message Queue nhằm:

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


Áp dụng cho:

12.3. Quy định chính

Kết nối Database

Cấu hình datasource tại:

config/**

application-*.yml


Message Queue

Cấu hình tại:

config/**

common/messaging/**

Partner không được:


Chuẩn đặt tên queue/topic

<system>.<module>.<purpose>

Ví dụ:

core.audit.log

planning.project.created

auth.user.synced

12.4. Quy trình tạo queue/topic


Tạo ticket:

[QUEUE REQUEST] <module> - <purpose>


  1. Cung cấp:

Core tạo queue và cấp quyền

12.5. Checklist DB & Queue


Database:

Queue:

13.1. Mục tiêu chương


Thiết kế kiến trúc đa database nhằm:

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


Áp dụng cho:

13.3. Quy định chính


Nguyên tắc thiết kế

db.vendor = oracle | mssql



Cấu trúc khuyến nghị

modules/<module>/

  controller/

  service/

  repository/

  query/

    QueryProvider.java

    OracleQueryProvider.java

    SqlServerQueryProvider.java

  entity/

  dto/



Quy định đa DB

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

Khi thay đổi nghiệp vụ

  1. Sửa Service

  2. Cập nhật query tương ứng trong provider


Khi khác DB

  1. Chỉ sửa query trong:

  2. Không sửa Service


Khi tạo Pull Request

PR phải:

13.5. Ví dụ minh họa

Cấu hình

db.vendor = mssql


Hệ thống tự inject:

SqlServerQueryProvider


image.png


Ví dụ sai

if (dbType.equals("oracle")) {

   // SQL

} else {

   // SQL

}


Sai vì logic DB nằm trong Service.


Ví dụ đúng

Service:

queryProvider.getFindAllPlanningSql();


QueryProvider quyết định SQL theo DB.

13.6. Checklist áp dụng


13.Swagger API Documentation & JWT Testing

13.1. Mục tiêu chương

Chương này quy định cách:

Mục tiêu:


13.2. Truy cập Swagger UI

Sau khi chạy service:


http://localhost:8080/swagger-ui/index.html

Hoặc:


http://localhost:8080/swagger-ui.html

Swagger hiển thị danh sách API theo controller.


13.3. Cấu hình Swagger trong Spring Boot

Dependency Maven:


<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.7.0</version> </dependency>

13.3.1. Cấu hình OpenAPI + JWT

File:


config/OpenApiConfig.java

@Configuration public class OpenApiConfig { @Bean public OpenAPI api() { return new OpenAPI() .info(new Info() .title("Company Backend API") .version("v1.0") .description("API documentation")) .components(new Components() .addSecuritySchemes("bearerAuth", new SecurityScheme() .type(SecurityScheme.Type.HTTP) .scheme("bearer") .bearerFormat("JWT") ) ) .addSecurityItem(new SecurityRequirement().addList("bearerAuth")); } }

13.4. Đánh dấu API cần JWT trong Controller

Controller cần auth:


@RestController @RequestMapping("/api/v1/users") @SecurityRequirement(name = "bearerAuth") public class UserController {

Swagger sẽ hiển thị icon:


🔒 GET /api/v1/users

13.5. Cấu hình Spring Security cho Swagger

Swagger UI phải được phép truy cập không cần auth:


.requestMatchers( "/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**" ).permitAll()

API business:


.anyRequest().authenticated()

13.6. Test API có JWT trên Swagger (Step-by-Step)

Bước 1. Login lấy JWT

Gọi API login:


POST /api/v1/auth/login

Response:


{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }

Bước 2. Mở Swagger


http://localhost:8080/swagger-ui/index.html

Bước 3. Click Authorize

Góc phải Swagger:


Authorize 🔒

Click vào.


Bước 4. Nhập JWT

Nhập:


Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Lưu ý:

Ví dụ đúng:


Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Bước 5. Authorize

Click:


Authorize

Swagger sẽ lưu token cho toàn bộ API.


Bước 6. Test API

Chọn API có 🔒:


GET /api/v1/theme-config

Click:


Execute

Swagger gửi request:


Authorization: Bearer eyJhbGciOiJIUzI1Ni...

13.7. Kiểm tra Swagger đã gửi token chưa

Trong Swagger phần Curl phải có:


-H "Authorization: Bearer eyJhbGciOiJIUzI1Ni..."

Nếu không có → chưa Authorize.


13.8. Lỗi thường gặp

403 Forbidden

Nguyên nhân:


Không thấy nút Authorize

Nguyên nhân:

Khắc phục:


Ctrl + F5

Swagger call được API không cần token

Nguyên nhân:

Security config permitAll API.

Ví dụ:


.requestMatchers("/api/v1/**").permitAll()

Swagger không cần JWT.


13.9. Quy định chuẩn sử dụng Swagger trong dự án

Controller cần auth:


@SecurityRequirement(name = "bearerAuth")

Controller public:

không cần annotation.


API test phải:


13.10. Quy định commit & PR

API mới phải:

PR bị reject nếu:


13.11. Checklist Swagger cho Dev

Trước khi merge:


13.12. Best Practice

Không permitAll API production.

Swagger chỉ permit:


/swagger-ui/** /v3/api-docs/**

JWT luôn test qua Authorize.