| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-05-21 |
| Context | UMS Web App — Frontend Architecture |
| Deciders | Architecture Team |
Frontend applications often mix concerns: business logic in components, HTTP calls in UI code, and state management scattered across files. This makes testing difficult, reuse impossible, and the codebase fragile to change.
Apply Clean Architecture (Hexagonal) to the React frontend with strict layer boundaries:
src/
├── domain/ # Enterprise business rules (PURE)
│ ├── entities/ # Enterprise entities (Tenant, Branch, IdP)
│ ├── value-objects/ # Value objects (Email, TenantCode)
│ ├── schemas/ # Zod validation schemas
│ └── constants/ # Domain constants
│
├── application/ # Use cases and application logic
│ ├── hooks/ # React hooks (use cases)
│ ├── stores/ # Zustand stores (state)
│ ├── errors/ # Error handling utilities
│ ├── utils/ # Application utilities (logger, i18n)
│ └── i18n/ # Internationalization
│
├── infrastructure/ # External concerns
│ ├── http/ # HTTP client, GraphQL client, CSRF
│ └── services/ # External service adapters
│
└── presentation/ # UI layer
├── shared/ # Shared components, layouts, theme
└── identity/ # Bounded context presentation
├── tenant/ # Tenant aggregate screens
├── profile/ # Profile aggregate screens
└── hooks/ # Context-specific hooks
Dependencies flow inward: Presentation → Application → Domain. Infrastructure is injected via dependency inversion.
presentation ──▶ application ──▶ domain
▲
│
infrastructure (injected)
DOM manipulation (e.g., document.body.classList) is performed in the presentation layer, not in stores. Stores expose state; components react to it.
Positive:
Negative:
no-restricted-imports can enforce boundaries (future)index.ts) define public APIs per layerAGENTS.md documents conventions for AI agents