Skip to content

Architecture

1. Bối cảnh hệ thống (C4 L1)

2. Góc nhìn Container (C4 L2)

Một deployable duy nhất chạy dưới role api, worker, hoặc cả hai, chọn bằng APP_ENV_APPLICATION_ROLES.

3. Góc nhìn Component (C4 L3) — Phân lớp nội bộ

LớpTrách nhiệm
ControllersAuth + assertMerchantAccess + ánh xạ DTO
Service Queue/Snapshot/ConfigLogic vòng đời, idempotency, enqueue
Worker serviceĐiều phối pipeline (fetch → render → encrypt → upload)
Fetchers / generators / crypto / metalinkCác pha pipeline đơn trách nhiệm
RepositoriesTruy vấn Drizzle, bộ chỉnh job-status nguyên tử, soft-delete
ComponentsKafka producer/consumer, recovery sweep, WS emitter

4. Chỉ mục máy trạng thái

EntityTrạng tháiSơ đồ
LedgerDRAFT, FINALIZED, ARCHIVED, SUBMITTED*→ nhảy tới
LedgerJobPENDING, PROCESSING, COMPLETED, REJECTED (+DRAFT/PARTIAL dành chỗ)→ nhảy tới

* SUBMITTED đã dành chỗ — logic chuyển trạng thái chưa xây.

Ledger (vòng đời do người dùng điều khiển)

Sở hữu độc quyền bởi LedgerSnapshotService. Worker không bao giờ thay đổi Ledger.status.

TừSự kiệnĐếnĐiều kiện
DRAFTfinalizeFINALIZEDsnapshot không có hasUnrecordedChange
FINALIZEDrevisehàng DRAFT mớicần note.default; đặt version+1, previousVersionId

LedgerJob (trạng thái tạo)

Sở hữu bởi LedgerJobService + LedgerWorkerService. Độc lập với Ledger.status.

TừSự kiệnĐếnĐiều kiện
PENDINGsetProcessingPROCESSINGUPDATE … WHERE status=PENDING nguyên tử; trả null nếu đã bị claim
PROCESSINGsetCompletedIfProcessingCOMPLETEDWHERE status=PROCESSING nguyên tử; false nếu bị giành trước
PROCESSINGlỗi/timeoutREJECTEDghi failureReason (errorCode + i18n)
REJECTEDhandleRetryPENDINGre-enqueue; không reset attemptCount
PROCESSINGkẹt (processStartAt < cutoff)PENDINGquét RecoveryComponent, rồi re-enqueue

5. Kịch bản runtime

5.1 Enqueue → generate → complete

BướcChi tiết
3Idempotent trên (merchantId, type, period); sổ FINALIZED từ chối re-generate
8Không có job PENDING → return (đã claim/done) trừ khi APP_ENV_FORCE_GENERATE
11Parse thất bại → REJECTED (FETCH_DATA_ERROR), commit, không replay
14setCompletedIfProcessing=false → bị worker đồng thời giành trước; bỏ qua finalize
16Commit chỉ xảy ra sau upload + finalize; lỗi rethrow và message vẫn được commit (không auto-replay)

5.2 Phục hồi job kẹt

BướcChi tiết
2Chỉ job PROCESSINGprocessStartAt cũ hơn APP_ENV_STALL_THRESHOLD_MS (mặc định 180s)
4RecoveryComponent đăng ký trước KafkaConsumerComponent nên message re-enqueue tồn tại trước khi consumer poll

5.3 Finalize rồi revise

6. Mối quan tâm xuyên suốt

Mối quan tâmCách service này xử lý
AuthNJWT (issuer = identity), JWKS xác minh mỗi request (VerifierApplication)
AuthZCasbin qua PolicyDefinition; permission cache trong Redis; mọi endpoint gọi assertMerchantAccess(merchantId)
i18njsonb { en, vi } (ledger note, tax-level name/description, failure-reason)
Loggingkey-value có cấu trúc (key: %s); ghi log các pha pipeline (FETCH/GENERATE/UPLOAD/COMPLETED)
IdempotencyEnqueue khoá trên (merchantId, type, period); job claim qua UPDATE có điều kiện nguyên tử
EncryptionAES-256-GCM trên mọi file upload (APP_ENV_LEDGER_ENCRYPTION_KEY)
Soft-deleteSoftDeletableRepository (deletedAt); unique index là partial (WHERE deleted_at IS NULL)
IDsSnowflake qua IdGenerator, worker 6

7. Trang liên quan

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.