Skip to content

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:#0aa

Text 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

LayerFolderQuy tắc cứng
Domainsrc/domain/Pure types, Zod schemas. KHÔNG import từ layer khác.
Enginesrc/engine/Pure compute (forecast, WLU). KHÔNG import từ services/, không any.
Servicessrc/services/Mọi I/O (Supabase, xlsx, audit). Trả về typed result.
Hookssrc/hooks/React Query wrapper. Không chứa business logic.
UIsrc/pages/, src/components/Dumb. Gọi hooks, render.
Libsrc/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)

  1. KHÔNG task nào chạm file thuộc task khác
  2. KHÔNG skip TDD: test trước, code sau
  3. KHÔNG hardcode hyperparameter — phải đi qua configService
  4. KHÔNG import từ services/ vào engine/
  5. KHÔNG any trong src/engine/ hoặc src/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() ở migration M0_init.sql.
  • Audit log: REVOKE UPDATE, DELETE → chỉ INSERT.

7. State management

StateToolPhạm vi
Server stateTanStack Querymọi hook useForecast, useAdjustments, …
UI stateZustandscope (warehouse + role hiện hành)
Form stateReact Hook Form + ZodSubmit 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    # playwright

Gate trước khi merge: typecheck && test && build PHẢI pass.

9. ADR (Architecture Decisions) ngắn

#DecisionLý do
ADR-1React 18 + Vite (không Next)App nội bộ, không cần SSR; build nhanh, dev DX tốt
ADR-2Supabase (DB + Auth + RLS)Đủ cho MVP, ship nhanh, có RLS row-level
ADR-3Engine PURETest dễ, không phụ thuộc môi trường, dùng lại được trong worker
ADR-4Zod ở cả domain + form1 nguồn truth cho types & validation
ADR-5Audit log insert-onlyYêu cầu compliance, RLS revoke UPDATE/DELETE
ADR-6Cloudflare Pages cho prototype HTMLDemo nhanh trước MVP React

Tiếp: Database · Data Flow · Deployment