Architecture
Quick Reference — 4 layer, 1 quy tắc bất biến: engine pure, không import từ services.
1. Sơ đồ tầng
mermaid
flowchart TD
UI[UI Layer<br/>pages + components] --> Hooks[Hooks Layer<br/>React Query]
Hooks --> Svc[Services Layer<br/>I/O · Supabase · xlsx]
Svc --> Eng[Engine Layer<br/>PURE compute]
Eng --> Dom[Domain Layer<br/>types · zod schemas]
Svc --> Dom
Hooks --> Dom
UI --> Dom
Svc --> SB[(Supabase<br/>Postgres + Auth + RLS)]
style Eng fill:transparent,stroke:#0aa
style Dom fill:transparent,stroke:#0aaText mô tả: UI gọi hooks → hooks gọi services → services gọi engine (pure) và Supabase. Tất cả layer cùng dùng chung domain/ types. Engine và Domain là pure — không có I/O, không có side-effect.
2. Trách nhiệm từng layer
| Layer | Folder | Quy tắc cứng |
|---|---|---|
| Domain | src/domain/ | Pure types, Zod schemas. KHÔNG import từ layer khác. |
| Engine | src/engine/ | Pure compute (forecast, WLU). KHÔNG import từ services/, không any. |
| Services | src/services/ | Mọi I/O (Supabase, xlsx, audit). Trả về typed result. |
| Hooks | src/hooks/ | React Query wrapper. Không chứa business logic. |
| UI | src/pages/, src/components/ | Dumb. Gọi hooks, render. |
| Lib | src/lib/ | Auth, scope, env, utils. |
3. Module boundaries
Mỗi task agent owner 1 set file (không trùng task khác). Xem architecture/module-boundaries.md. Quy tắc:
Luật bất biến (vi phạm = block PR)
- KHÔNG task nào chạm file thuộc task khác
- KHÔNG skip TDD: test trước, code sau
- KHÔNG hardcode hyperparameter — phải đi qua
configService - KHÔNG import từ
services/vàoengine/ - KHÔNG
anytrongsrc/engine/hoặcsrc/autotune/
4. Forecast engine contract
ts
// engine/forecast/generator.ts
interface GenerateInput {
baseline: BaselineResolution; // từ historical-aggregator
monthly_multiplier: number; // ≥ 0
event_multiplier: number; // ≥ 0
}
interface GenerateOutput {
forecast_qty: number;
raw_qty: number;
baseline_avg: number;
resolved_event_type: EventType;
fallback_used: boolean;
p85: number | null;
sample_size: number;
}Tham chiếu: generator.ts
5. WLU engine contract
ts
// engine/wlu/calculator.ts
WLU_per_order_minutes = 60 / UPH_eff
UPH_eff = harmonic_mean( UPH[group, pk_type], weight = product_mix )
total_workload_minutes = WLU × forecast_qty
headcount = ceil( total_workload_minutes / shift_minutes )Tham chiếu: calculator.ts
6. Auth & RLS
- Auth: Supabase Auth (email/password). User metadata chứa
role. - Roles (xem
src/lib/scope.tsx):KAM,AM,PLANNER,OPS,ADMIN,AUDITOR. - RLS policies: mọi bảng đều bật RLS. Helper functions
is_planner(),is_admin()ở migrationM0_init.sql. - Audit log: REVOKE UPDATE, DELETE → chỉ INSERT.
7. State management
| State | Tool | Phạm vi |
|---|---|---|
| Server state | TanStack Query | mọi hook useForecast, useAdjustments, … |
| UI state | Zustand | scope (warehouse + role hiện hành) |
| Form state | React Hook Form + Zod | Submit adjustment, Config edit |
8. Build & test pipeline
bash
npm run typecheck # tsc -b --noEmit
npm test # vitest run (unit, engine)
npm run build # tsc -b && vite build
npm run test:e2e # playwrightGate trước khi merge: typecheck && test && build PHẢI pass.
9. ADR (Architecture Decisions) ngắn
| # | Decision | Lý do |
|---|---|---|
| ADR-1 | React 18 + Vite (không Next) | App nội bộ, không cần SSR; build nhanh, dev DX tốt |
| ADR-2 | Supabase (DB + Auth + RLS) | Đủ cho MVP, ship nhanh, có RLS row-level |
| ADR-3 | Engine PURE | Test dễ, không phụ thuộc môi trường, dùng lại được trong worker |
| ADR-4 | Zod ở cả domain + form | 1 nguồn truth cho types & validation |
| ADR-5 | Audit log insert-only | Yêu cầu compliance, RLS revoke UPDATE/DELETE |
| ADR-6 | Cloudflare Pages cho prototype HTML | Demo nhanh trước MVP React |
Tiếp: Database · Data Flow · Deployment