# Kiến trúc hệ thống ## Project Overview - Đây là dự án tạo ra Starter Kit Golang Backend cho Team ULFlow để có thể khởi tạo các dự án trong thời gian ngắn, nhanh chóng mà vẫn đảm bảo các yếu tố cơ bản - Sử dụng Mô hình DDD (Domain Driven Development) - Customize - `Resource`: Các Aggregate DDD - `Transaction`: Các Saga điều phối luồng nghiệp vụ phức tạp - `Adapter`: Xử lý giao tiếp với các hệ thống bên ngoài - `Helper`: Các thư viện, tiện ích dùng chung - `Transport`: Lớp giao diện người dùng - Thành phần kiến trúc chi tiết (U-Hierarchy) - `ubit`: Đơn vị logic nhỏ nhất (hàm, type, hằng số) - `ubrick`: Tập hợp các `ubit` liên quan - `ublock`: Thành phần hoạt động độc lập tương đối - `ubundle`: Tính năng hoàn chỉnh cho người dùng ## Nguyên tắc thiết kế - **User-Centric**: Ưu tiên trải nghiệm người dùng, giảm thiểu sự phức tạp - **Data-Oriented Programming (DOP)**: Thiết kế xoay quanh luồng dữ liệu và các biến đổi dữ liệu - **Domain-Driven Design (DDD)**: Áp dụng các khái niệm cốt lõi (Bounded Context, Aggregates, Domain Events) ## Áp dụng DDD trong Dự án ### Resource - Tuân theo nguyên tắc Aggregate trong DDD - Mỗi Resource đại diện cho một thực thể nghiệp vụ - Bao gồm các thuộc tính và hành vi liên quan - Đảm bảo tính nhất quán và logic nghiệp vụ ### Transaction - Triển khai mẫu Saga để điều phối các hoạt động phức tạp - Đảm bảo tính toàn vẹn dữ liệu xuyên suốt các Resource - Xử lý các trường hợp lỗi và rollback khi cần thiết ### Adapter - Cung cấp giao diện giao tiếp với hệ thống bên ngoài - Triển khai mẫu Adapter để đảm bảo tính linh hoạt - Dễ dàng thay thế các thành phần khi cần thiết ### Helper - Cung cấp các tiện ích dùng chung - Triển khai các công cụ hỗ trợ phát triển - Tối ưu hóa mã lệnh và tăng khả năng tái sử dụng ### UIUX - Lớp giao diện người dùng (API, Web Interface) - Triển khai theo nguyên tắc thiết kế hướng người dùng - Tách biệt với logic nghiệp vụ ## U-Hierarchy Hệ thống phân cấp U là một cách tiếp cận duy nhất cho tổ chức mã: ### ubit - Đơn vị nhỏ nhất của mã - Ví dụ: một hàm, một constant, một type - Mục đích đơn lẻ, dễ test ### ubrick - Tập hợp các ubit liên quan đến nhau - Cung cấp một chức năng cụ thể - Ví dụ: một package nhỏ, một nhóm hàm liên quan ### ublock - Thành phần có thể hoạt động độc lập - Bao gồm nhiều ubrick làm việc cùng nhau - Ví dụ: một module hoặc service ### ubundle - Một tính năng hoàn chỉnh cho người dùng - Kết hợp nhiều ublock để tạo ra trải nghiệm người dùng - Ví dụ: một tính năng end-to-end ### Directory Structure starter-kit/ ├── .gitea/ │ ├── workflows/ │ │ ├── ci.yml │ │ └── docker.yml │ ├── .hooks/ │ │ ├──pre-commit │ │ └──prepare-commit-msg │ └── commit-template.txt ├── cmd/ │ └── app/ # Hoặc 'server/', 'api/' - Entrypoint chính của ứng dụng │ └── main.go ├── internal/ │ ├── resource/ # Lớp chứa các Aggregates/Entities (DDD) │ │ └── example/ # Một module ví dụ rất cơ bản │ │ ├── example_aggregate.go │ │ ├── example_types.go │ │ └── example_repository_interface.go # Interface cho repository │ └── transaction/ # Lớp chứa các Use Cases/Application Services/Sagas (DDD) │ │ └── example/ # Một module ví dụ rất cơ bản │ │ └── example_transaction.go │ ├── adapter/ # Lớp giao tiếp với các hệ thống bên ngoài hoặc hạ tầng │ │ ├── persistence/ # Hoặc 'repository/', 'storage/' - Triển khai repositories │ │ │ └── postgres/ # Ví dụ với PostgreSQL │ │ │ ├── connection.go # Thiết lập kết nối DB │ │ │ ├── example_postgres_repository.go # Triển khai repository cho example_aggregate │ │ │ └── models.go # (Tùy chọn) GORM models hoặc struct cho DB mapping │ │ | │ │ └── externalapi/ # Giao tiếp với các dịch vụ bên ngoài │ │ └── # example_service_client.go │ ├── helper/ # Các thư viện, tiện ích dùng chung, không có business logic │ │ ├── config/ # Load cấu hình (ví dụ: Gin) │ │ │ └── load.go │ │ ├── logger/ # Thiết lập logger (ví dụ: Logrus) │ │ │ └── log.go │ │ ├── validation/ # Tiện ích validation chung │ │ │ └── common.go │ │ ├── security/ # Tiện ích bảo mật (JWT, hashing) │ │ │ ├── jwt_helper.go │ │ │ └── password_helper.go │ │ └── # ... (ví dụ: datetime, string_utils) │ └── transport/ # Lớp giao tiếp với thế giới bên ngoài (ví dụ: HTTP) │ └── http/ # Cụ thể cho HTTP transport │ ├── handler/ # HTTP request handlers │ │ ├── example_handler.go │ │ ├── health_handler.go # Endpoint kiểm tra sức khỏe ứng dụng │ │ └── middleware/ # HTTP middlewares (auth, logging, cors, recovery) │ │ ├── auth_middleware.go │ │ ├── request_logger_middleware.go │ │ └── error_handling_middleware.go │ ├── router.go # Định nghĩa các routes (ví dụ: sử dụng Gin, Chi) │ └── dto/ # Data Transfer Objects cho request/response │ ├── example_dto.go │ └── common_dto.go ├── docs/ # Tài liệu của starter-kit ├── templates/ # Chứa các file cấu hình mẫu │ ├── config.example.yaml # Đổi tên để rõ là file mẫu │ └── .env.example # Các biến môi trường mẫu ├── migrations/ # Chứa các file SQL migration │ └── 000001_init_schema.example.sql # Migration mẫu ├── api/ # (Tùy chọn) Định nghĩa API (ví dụ: OpenAPI/Swagger specs) │ └── openapi.yaml ├── scripts/ # Các shell script tiện ích (nếu Makefile không đủ) │ ├── # setup_env.sh │ └── # run_checks.sh ├── Makefile # Các lệnh tiện ích (build, test, lint, run, docker, ...) ├── go.mod # Tên module nên là tên của starter-kit, ├── .gitignore └── README.md # Hướng dẫn nhanh và tổng quan về starter-kit ### Checklist cơ bản của vòng đời App I. Thiết lập Cơ bản và Cấu trúc (Basic Setup & Structure) [ ] Tách biệt Logic Khởi tạo: Yêu cầu: main.go chỉ chứa hàm main() và các lệnh gọi ở mức cao nhất. Logic chi tiết cho việc khởi tạo từng thành phần (config, logger, server, DB) phải nằm trong các package/hàm riêng biệt (ví dụ: internal/common/config, internal/common/logger, internal/transport/http/server, internal/infrastructure/db). Mục đích: Giữ main.go ngắn gọn, dễ đọc, dễ hiểu vai trò điều phối của nó. [ ] Hàm main() rõ ràng: Yêu cầu: Hàm main() nên thực hiện các bước khởi tạo một cách tuần tự, logic. Mục đích: Dễ dàng theo dõi luồng khởi động của ứng dụng. [ ] Xử lý lỗi khởi tạo nghiêm trọng: Yêu cầu: Nếu một bước khởi tạo thiết yếu (ví dụ: load config, kết nối DB) thất bại, ứng dụng nên log lỗi rõ ràng và thoát (ví dụ: log.Fatalf hoặc logger.Fatal). Mục đích: Tránh việc ứng dụng chạy trong trạng thái không ổn định hoặc không đầy đủ. II. Quản lý Cấu hình (Configuration Management) [ ] Load Cấu hình: Yêu cầu: Gọi một hàm/package chuyên biệt để đọc cấu hình từ các nguồn (file YAML/JSON/.env, biến môi trường). Ví dụ: cfg, err := config.Load("configs/", ".env.example") Mục đích: Tập trung logic load config, dễ dàng thay đổi nguồn hoặc định dạng config. [ ] Validate Cấu hình Cơ bản (Đề xuất): Yêu cầu (Đề xuất): Sau khi load, có một bước kiểm tra sơ bộ các giá trị cấu hình thiết yếu (ví dụ: port server có hợp lệ, thông tin kết nối DB có đủ). Mục đích: Phát hiện lỗi cấu hình sớm. III. Logging [ ] Structured Logging với Logrus: - Sử dụng Logrus cho structured logging với JSON format - Hỗ trợ các log level: debug, info, warn, error - Tích hợp request ID tracking cho HTTP requests - Cấu hình logging được quản lý tập trung trong section `logger` [ ] Middleware Logging: - Tự động log các HTTP request với thông tin chi tiết: - Request ID - Method - Path - Status code - Latency - Client IP - User agent [ ] Error Handling và Context: - Tích hợp error context vào logs - Structured fields cho phép phân tích và tìm kiếm hiệu quả - Theo dõi chuỗi lỗi với error wrapping IV. Khởi tạo Dependencies Cơ sở hạ tầng (Infrastructure Dependencies) [ ] Khởi tạo Kết nối Database (nếu starter kit có sẵn): Yêu cầu: Gọi một hàm/package để thiết lập kết nối tới DB (ví dụ: PostgreSQL) và quản lý connection pool. Ví dụ: dbConn, err := database.New(cfg.DBConfig, appLogger) Mục đích: Chuẩn bị sẵn sàng cho việc tương tác với DB. [ ] Đóng Kết nối Database khi Shutdown: Yêu cầu: Đảm bảo kết nối DB được đóng một cách an toàn khi ứng dụng tắt (sử dụng defer hoặc trong quá trình graceful shutdown). Mục đích: Tránh rò rỉ tài nguyên. V. Thiết lập Server (ví dụ: HTTP Server) [ ] Khởi tạo Router (từ package riêng): Yêu cầu: Logic định nghĩa routes và gắn handlers phải nằm trong một package riêng (ví dụ: internal/transport/http/router). main.go chỉ khởi tạo nó. Ví dụ: httpRouter := router.New(appLogger /*, dbConn, otherServices */) Mục đích: Tách biệt rõ ràng logic routing và business. [ ] Khởi tạo HTTP Server: Yêu cầu: Tạo instance http.Server với các cấu hình cơ bản (địa chỉ, port, handler là router đã khởi tạo, có thể cả timeouts cơ bản). [ ] Đăng ký Middleware Phi tính năng Cơ bản (thường trong package router hoặc server setup): Yêu cầu: Đảm bảo các middleware thiết yếu như request logging, panic recovery, CORS (nếu cần) được áp dụng. Mục đích: Tăng cường độ tin cậy và khả năng giám sát cho API. [ ] Đăng ký Health Check Endpoint (trong router): Yêu cầu: Cung cấp một endpoint (/healthz, /status) để kiểm tra tình trạng hoạt động của ứng dụng. Mục đích: Hỗ trợ giám sát và tự động hóa vận hành. VI. Quản lý Vòng đời Ứng dụng (Application Lifecycle Management) [ ] Chạy Server trong Goroutine riêng: Yêu cầu: httpServer.ListenAndServe() (hoặc tương tự) được chạy trong một goroutine để không block hàm main(). [ ] Xử lý Graceful Shutdown: Yêu cầu: Lắng nghe các tín hiệu OS (SIGINT, SIGTERM) để thực hiện shutdown một cách an toàn. Bao gồm: Tạo channel để nhận tín hiệu: quit := make(chan os.Signal, 1) Đăng ký tín hiệu: signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) Block cho đến khi nhận tín hiệu: <-quit Gọi httpServer.Shutdown(ctx) với một context có timeout. Log các bước shutdown. Mục đích: Đảm bảo ứng dụng hoàn thành các request đang xử lý, đóng kết nối an toàn, tránh mất dữ liệu. [ ] Xử lý Error và Log: Yêu cầu: Tất cả các error từ các thành phần (DB, HTTP, middleware) nên được log rõ ràng, bao gồm thông tin context (ví dụ: request ID, method, URL). Mục đích: Hỗ trợ debug và giám sát vấn đề khi xảy ra. VII. Quản lý tài nguyên (Resource Management) [ ] Đóng tài nguyên khi không cần thiết: Yêu cầu: Đảm bảo tất cả các tài nguyên (connection, file, mutex, channel) được đóng hoặc giải phóng khi không còn sử dụng. Mục đích: Tránh rò rỉ tài nguyên và đảm bảo hiệu quả sử dụng bộ nhớ.