vampire-like/skills/ui-five-layer-architecture/references/ui-five-layer-standard.md

275 lines
5.8 KiB
Markdown

# UI Five-Layer Architecture Standard
## Table of Contents
1. [Scope and Intent](#scope-and-intent)
2. [Core Model](#core-model)
3. [Layer Definitions](#layer-definitions)
4. [UI Module Levels](#ui-module-levels)
5. [Dependency Rules](#dependency-rules)
6. [Event Communication Rules](#event-communication-rules)
7. [Interaction Flow](#interaction-flow)
8. [Naming and Folder Conventions](#naming-and-folder-conventions)
9. [Testing Policy](#testing-policy)
10. [Anti-Patterns](#anti-patterns)
11. [Delivery Checklist](#delivery-checklist)
## Scope and Intent
Use this standard to define and enforce a stable UI architecture that can be reused across projects.
Primary goals:
- keep business logic independent from UI rendering
- keep UI rendering deterministic and testable
- make reviews and refactors consistent
- reduce coupling and regression risk
## Core Model
Base chain:
```text
External Flow
-> Controller
-> UseCase
-> RawData / Result
-> BuildContext
-> View
View
--(UI-local event)--> Controller
```
Rules:
- `UseCase` produces business outputs only.
- `Controller` is the only layer that creates `Context`.
- `View` only renders and emits interaction events.
## Layer Definitions
### UseCase
Responsibilities:
- own business rules and state transitions
- validate business actions
- return `RawData` or result objects
Constraints:
- do not depend on `Context`, `View`, or rendering types
- do not format display strings or map visual assets
- do not publish UI-local events
### RawData
Responsibilities:
- carry business data from `UseCase` to `Controller`
Constraints:
- keep data business-oriented
- do not reference any `*Context` type
- do not contain rendering objects (for example UI components)
### Controller
Responsibilities:
- be the external entry point for open/close/refresh
- bind and call `UseCase`
- transform `RawData/Result` into `Context`
- subscribe/unsubscribe UI-local events
- coordinate full or partial refresh
Constraints:
- do not let `View` bypass controller orchestration
- do not hide heavy business logic that belongs in `UseCase`
### Context
Responsibilities:
- carry display-ready data for rendering
Constraints:
- construct/update only in `Controller`
- do not enter `UseCase`
- allow composition (`FormContext`, `ItemContext`, `AreaContext`)
### View
Responsibilities:
- bind controls and render `Context`
- emit UI-local interaction events
Constraints:
- do not call `UseCase`
- do not mutate domain state
- do not subscribe to global business events
- do not become external entry points
## UI Module Levels
### Standard Five-Layer Module
Structure:
- `UseCase + RawData + Controller + Context + View`
Use when:
- module owns business rules, validations, or branching state transitions
- user actions mutate domain state
- behavior requires automated verification
### Lightweight Module
Structure:
- `Controller + Context + View`
Use when:
- module is display/navigation/confirmation only
- no independent business rules are present
Rule:
- upgrade to standard five-layer as soon as business rules appear
## Dependency Rules
Allowed:
- `UseCase -> domain/services/RawData/Result`
- `Controller -> UseCase/RawData/Result/Context/View/UI-local events`
- `Context -> child context/value objects`
- `View -> Context/UI-local events`
Forbidden:
- `UseCase -> Context/View/rendering types`
- `RawData/Result -> Context/View`
- `Context -> UseCase/View`
- `View -> UseCase`
- `View -> global business events`
- `View -> domain state mutation`
## Event Communication Rules
### Event Ownership
- `View -> Controller` uses UI-local events only
- UI-local events are not global business contracts
- business/domain modules must not consume UI-local event semantics
### Safety Requirements
- validate sender ownership in `Controller`
- scope handling to the active UI instance
- keep subscribe/unsubscribe symmetric
## Interaction Flow
### Standard Module
```text
External Flow
-> create/bind UseCase
-> open UI via Controller
Controller
-> UseCase action
-> RawData/Result
-> BuildContext
-> View refresh
View
--(UI-local event)--> Controller
```
### Lightweight Module
```text
External Flow
-> open UI via Controller(userData)
Controller
-> BuildContext(userData)
-> View refresh
View
--(UI-local event)--> Controller
```
## Naming and Folder Conventions
Recommended folders:
- `UI/<Domain>/UseCase`
- `UI/<Domain>/RawData`
- `UI/<Domain>/Controller`
- `UI/<Domain>/Context`
- `UI/<Domain>/View`
Recommended naming:
- `XXXFormUseCase`
- `XXXFormRawData`
- `XXXFormController`
- `XXXFormContext`, `XXXItemContext`, `XXXAreaContext`
- `XXXResult`, `XXXActionResult`
## Testing Policy
Policy:
- if a UI has a `UseCase` and automated tests are added, use EditMode tests for the `UseCase`
Priority coverage:
- initial model generation
- business branch and validation behavior
- action result correctness
- boundary and invalid input handling
Manual verification focus:
- first open
- interaction refresh
- partial refresh
- close and reopen
- null/invalid userData behavior
## Anti-Patterns
Do not allow:
- `UseCase` returning `Context`
- `RawData` carrying `*Context`
- `View` subscribing global business events
- direct domain mutation inside `View`
- skipping `Controller` as module entry
- module marked lightweight while carrying business state transitions
## Delivery Checklist
Use this checklist before marking work complete:
1. classify each module as standard or lightweight
2. verify business rules sit in `UseCase` only
3. verify `Context` is built only in `Controller`
4. verify `RawData` has no presentation model leakage
5. verify `View` is render-and-emit only
6. verify UI-local events are scoped and sender-checked
7. verify dependency direction constraints pass
8. verify tests/manual checks match the testing policy