Domain placement rules
Frontend feature code is organized by domain ownership, not by “which role logged in.” Shared workflows live once; role folders keep only unique experiences.
Related: Features · Routing · Shared pages.
Layout (high level)
apps/web/src/
domains/
shared/pages/ # multi-role workflows (users, maintenance, system, …)
admin/pages/ # admin-only experiences (e.g. dashboard)
hod/pages/ # department-head-only surfaces
… # other roles as needed
lib/services/ # HTTP (not UI)
context/ # app shell state
query/ # TanStack keys + shared query helpers
router/ # routes + guardsRules
Shared by default — If more than one role uses the same workflow, put the page under
domains/shared/pages/<feature>/.Gate with permissions — Visibility of actions differs by permission; do not copy the whole page per role.
Role folders are exceptions — Dashboards or flows that are semantically unique to one role stay under
domains/<role>/pages/.Folder-per-page — Shared features use:
textusers/list/index.tsx + skeleton.tsx users/detail/index.tsx + skeleton.tsx users/create/index.tsx + skeleton.tsxSame pattern for maintenance (and other shared CRUD). Skeletons stay local to the page so loading UI matches that surface.
No server lists in context — Lists/details go through TanStack Query; context is app shell only (context).
Current placement (examples)
| Feature | Location |
|---|---|
| Users management | domains/shared/pages/users/* |
| Maintenance | domains/shared/pages/maintenance/* |
| Departments | domains/shared/pages/departments/* |
| System (monitor, logs, audit, settings) | domains/shared/pages/system/* |
| Admin dashboard | domains/admin/pages/dashboard/* (role-specific) |
Why this shape
- One implementation of each workflow → fewer bugs when permissions change
- Clear ownership for contributors (“edit shared users list, not three role copies”)
- Matches backend permission IDs instead of hard-coded role branches in every page
When in doubt: shared page + permission checks, then extract a role page only if the UX truly diverges.