# Architecture — SOLUTION_ERP > Kiến trúc tổng thể + trách nhiệm từng layer + diagram luồng dữ liệu. ## 1. Layered overview ``` ┌──────────────────────────────────────────────────────────────┐ │ CLIENT TIER │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ fe-admin :8082 │ │ fe-user :8080 │ │ │ │ React 19 + Vite │ │ React 19 + Vite │ │ │ │ Tailwind 4 │ │ Tailwind 4 │ │ │ │ TanStack Query │ │ TanStack Query │ │ │ └────────┬─────────┘ └────────┬─────────┘ │ └───────────┼──────────────────────────┼───────────────────────┘ │ Vite dev proxy /api │ │ IIS URL Rewrite prod │ ▼ ▼ ┌──────────────────────────────────────────────────────────────┐ │ API LAYER (:5443) │ │ SolutionErp.Api — ASP.NET Core 10 Web API │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Controllers: Auth, Menus, Roles, Permissions, │ │ │ │ Suppliers, Projects, Departments, │ │ │ │ Forms, Contracts, Reports │ │ │ │ Middleware: GlobalException, Serilog, CORS, JWT │ │ │ │ Authorization: MenuPermissionHandler (policy-based) │ │ │ │ Services: CurrentUserService, WebHostEnvLocator │ │ │ │ wwwroot/templates/ (5 .docx/.xlsx) │ │ │ └────────────────────┬───────────────────────────────────┘ │ └───────────────────────┼──────────────────────────────────────┘ │ MediatR ISender.Send(cmd) ▼ ┌──────────────────────────────────────────────────────────────┐ │ APPLICATION LAYER │ │ SolutionErp.Application │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Auth │ │ Master │ │ Permissions │ │ │ │ (Login/Me) │ │ (CRUD 3 │ │ (Menu tree + │ │ │ │ │ │ entity) │ │ matrix) │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ Forms │ │ Contracts │ ┌──────────────┐ │ │ │ (Render │ │ (Workflow 9 │ │ Reports │ │ │ │ engine) │ │ phase, Inbox) │ │ (Dashboard + │ │ │ └──────────────┘ └──────────────────┘ │ Excel exp) │ │ │ └──────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Common: Exceptions, Behaviors (ValidationPipeline), │ │ │ │ Interfaces (IDbContext, ICurrentUser, ...), │ │ │ │ Models (PagedResult) │ │ │ └──────────────────────────────────────────────────────┘ │ └───────┬──────────────────────────────────────────┬───────────┘ │ depends on interface │ runs on ▼ ▼ ┌──────────────────────┐ ┌───────────────────────────┐ │ DOMAIN LAYER │ │ INFRASTRUCTURE LAYER │ │ SolutionErp.Domain │ │ SolutionErp.Infrastructure│ │ │ │ │ │ Common/BaseEntity │ │ Persistence/ApplicationD │ │ Contracts/ │ │ bContext + Migrations │ │ Forms/ │ │ Identity/JwtTokenService │ │ Identity/ │ │ Forms/Docx+XlsxRenderer │ │ Master/ │ │ Services/ContractWorkflow│ │ │ │ + ContractCodeGenerator│ │ Enum + value object │ │ Reports/ExcelExporter │ └──────────────────────┘ │ Services/DateTimeService │ ↑ └────────────┬──────────────┘ │ references │ └───────────────────────────────────────┘ │ ▼ ┌───────────────────────┐ │ DATA TIER │ │ SQL Server 2022 │ │ (dbo schema, 19 bảng)│ └───────────────────────┘ ``` ## 2. Request lifecycle (1 POST/api/contracts) ``` 1. Browser → POST /api/contracts { type, supplierId, ... } 2. Vite → proxy tới :5443 3. JwtBearerMiddleware → validate token, set ClaimsPrincipal 4. Routing → ContractsController.Create(cmd) 5. MediatR.Send(CreateContractCommand) 6. ValidationBehavior (pipeline) → FluentValidation run 7. CreateContractCommandHandler ├─ check Supplier/Project exists (IApplicationDbContext) ├─ new Contract entity + set DrafterUserId (ICurrentUser) ├─ set SlaDeadline (IDateTime + IContractWorkflowService.GetPhaseSla) ├─ db.Contracts.Add(...) └─ SaveChangesAsync ├─ AuditingInterceptor sets CreatedAt/CreatedBy └─ EF Core INSERT → SQL Server 8. Return Guid id 9. Controller → 201 Created + Location header 10. Axios interceptor (FE) → TanStack Query invalidate + UI update ``` ## 3. Workflow state machine (Phase 3) Xem full ở [`workflow-contract.md`](workflow-contract.md). ``` DangChon → DangSoanThao → DangGopY → DangDamPhan → DangInKy → → DangKiemTraCCM → DangTrinhKy → DangDongDau → DaPhatHanh Alternates: → TuChoi (từ DangSoanThao) → DangSoanThao (revise từ bất kỳ phase duyệt) Bypass CĐT (BypassProcurementAndCCM=true): DangInKy → DangTrinhKy (skip CCM) ``` **Code generator trigger:** khi `targetPhase = DangDongDau` + `MaHopDong IS NULL` → gen format RG-001 với transaction SERIALIZABLE. ## 4. Permission model ``` User ──(AspNetUserRoles)── Role ──(Permissions)── MenuItem │ ├── CanRead ├── CanCreate ├── CanUpdate └── CanDelete ``` **Resolution:** - Login → JWT chứa claims (sub, email, roles) - `/api/menus/me` → query Permissions theo roleIds, **union OR** CRUD flags, filter tree theo CanRead - FE cache menu trong `AuthContext` + localStorage - Mỗi API action: `[Authorize(Policy = "{MenuKey}.{Action}")]` → `MenuPermissionHandler` check **Admin bypass:** role `Admin` luôn pass mọi policy (seed default full CRUD). ## 5. Data flow — "tạo HĐ và chạy hết workflow" ```mermaid sequenceDiagram actor D as Drafter actor M as Manager (PD/PM) actor C as CCM actor B as BOD actor H as HRA participant FE as fe-user participant API participant WF as WorkflowService participant CG as CodeGenerator participant DB D->>FE: POST /contracts/new FE->>API: POST /api/contracts API->>DB: INSERT Contracts (Phase=DangSoanThao, SLA=+7d) API-->>FE: 201 {id} D->>FE: Click "Submit → góp ý" FE->>API: POST /contracts/{id}/transitions {target:3} API->>WF: Transition(contract, 3, roles=[Drafter]) WF->>WF: Check adjacency + role WF->>DB: INSERT ContractApproval + UPDATE Contract Phase=3 API-->>FE: 204 M->>FE: Inbox → click HĐ → góp ý + "Chuyển tiếp" FE->>API: POST /transitions {target:4} API->>WF: Transition → Phase 4 Note over WF: Chạy tương tự qua 5,6,7 B->>FE: Duyệt → target:8 DangDongDau FE->>API: POST /transitions {target:8} API->>WF: Transition WF->>CG: GenerateAsync(contract, project, supplier) CG->>DB: BEGIN TRAN SERIALIZABLE CG->>DB: SELECT/UPDATE ContractCodeSequences CG->>DB: COMMIT CG-->>WF: "FLOCK 01/HĐGK/SOL&PVL/03" WF->>DB: UPDATE Contract SET MaHopDong, Phase=8 API-->>FE: 204 H->>FE: Click đóng dấu → target:9 FE->>API: POST /transitions {target:9} API->>WF: Transition (role HrAdmin) WF->>DB: UPDATE Phase=9 (DaPhatHanh) API-->>FE: 204 ``` ## 6. Deployment architecture (Phase 5 — planned) ``` ┌─────────────────────────────┐ │ Internet / Corp LAN │ └──────────────┬──────────────┘ │ ┌──────────▼──────────┐ │ IIS (Win Server) │ │ URL Rewrite / ARR │ └──────────┬──────────┘ ┌─────────────┼─────────────┐ │ │ │ ┌──────▼──────┐ ┌───▼────┐ ┌──────▼──────┐ │ fe-admin │ │ Api │ │ fe-user │ │ static files│ │ Kestrel│ │ static files│ │ (dist/) │ │ :5443 │ │ (dist/) │ └─────────────┘ └───┬────┘ └─────────────┘ │ ┌────────▼────────┐ │ SQL Server │ │ (same host OR │ │ separate VM) │ └─────────────────┘ ``` - IIS app pool riêng cho Api, Integrated Managed Pipeline, .NET CLR disabled (hosting .NET 10 OOP) - Static files 2 FE deploy vào `C:\inetpub\wwwroot\solution-erp-admin\` + `...user\` - HTTPS: Let's Encrypt qua win-acme (hoặc cert mua) - Backup SQL: daily full + 15min log → D:\Backups ## 7. Skill library (AI agent support) `.claude/skills/` có 3 skill chuyên biệt: | Skill | Dùng khi | |---|---| | [`contract-workflow`](../.claude/skills/contract-workflow/SKILL.md) | Debug chuyển phase, 403, mã HĐ, bypass CĐT | | [`form-engine`](../.claude/skills/form-engine/SKILL.md) | Render template, upload, placeholder không replace | | [`permission-matrix`](../.claude/skills/permission-matrix/SKILL.md) | Access denied, menu không hiện, gán role | Claude auto-invoke theo description matching. ## 8. Non-functional | Aspect | Current | Phase 5 target | |---|---|---| | Availability | dev-only | 99.5% (IIS restart, SQL HA optional) | | Latency | <200ms P95 local | <500ms P95 prod | | Concurrency | unrestricted | rate limit 100 req/min/IP | | Observability | Serilog console | + file rolling daily + Seq/ELK | | Security | JWT + HTTPS dev | + rate limit + audit log + CSP | ## 9. Liên quan - [`rules.md`](rules.md) — coding conventions - [`database/database-guide.md`](database/database-guide.md) — DB schema chi tiết - [`flows/`](flows/) — per-feature sequence diagrams - [`workflow-contract.md`](workflow-contract.md) — state machine spec - [`forms-spec.md`](forms-spec.md) — 8 form catalog