Architecture overview
What it is
ManpowerIQ is a multi-tenant workforce-rostering platform for ports and terminals, built as a .NET 8 backend with a React (Vite) web frontend, on PostgreSQL via EF Core 8 / Npgsql. The backend follows Clean Architecture — four projects with a strict inward dependency rule — and is service-based, not CQRS/MediatR (see Clean Architecture).
This page is the entry point to the architecture section; each subsystem has its own page below.
Why it's built this way
The platform serves several operating companies from one deployment, so two properties dominate the design:
- Hard tenant isolation — one company must never see or touch another's data, by accident or on purpose. This is enforced in three independent layers (see Multi-tenancy).
- Fine-grained, consistent access control — a port has planners, supervisors, managers, HR, and employees, each needing a scoped slice. Access is permission-centric so two business units can shape roles differently while the same capabilities stay consistently enforced (see Authentication & RBAC).
The Clean Architecture split keeps the domain model and business rules independent of the database and web framework, so the allocation engine, rules, and entities can be reasoned about and tested without infrastructure.
How it works
The verified stack (sheet 01 §build-status; sheet 00-index):
flowchart TB
subgraph Client
WEB[React + Vite web app<br/>responsive, not a PWA]
MOB[Expo/RN mobile<br/>health-check skeleton only]
end
subgraph API[ManpowerIQ.API · ASP.NET Core]
CTRL[Controllers · permission-guarded]
end
subgraph APP[ManpowerIQ.Application]
SVC[Services · business rules<br/>allocation engine, demand, leave…]
end
subgraph DOM[ManpowerIQ.Domain]
ENT[Entities · AuditableEntity / TenantEntity]
end
subgraph INFRA[ManpowerIQ.Infrastructure]
EF[EF Core 8 · DbContext<br/>query filters + interceptors]
HF[Hangfire · import job only]
end
DB[(PostgreSQL<br/>RLS + snake_case)]
WEB -->|JWT| CTRL
MOB -.->|/api/health| CTRL
CTRL --> SVC
SVC --> ENT
SVC --> EF
EF --> DB
SVC -.enqueue.-> HF
HF --> DB
- Frontend — a React/Vite SPA authenticating with a JWT; responsive (Tailwind) but not a PWA. The Expo/React-Native mobile project is a health-check skeleton only (sheet 20). For all practical purposes, ManpowerIQ is a web application.
- API — ASP.NET Core controllers, each guarded by a permission policy. The request pipeline runs authentication, then a tenant-context middleware that pushes the tenant into a Postgres session variable, then authorization (sheet 01 §build-status,
Program.cs:476-478). - Application — business logic in plain service classes (the allocation engine, demand planning, leave, attendance…). No mediator indirection.
- Domain — entities built on two base classes,
AuditableEntityandTenantEntity(sheet 01 §entities). - Infrastructure — EF Core 8 with global query filters and write interceptors, plus Hangfire for the one asynchronous import job.
- Database — PostgreSQL with snake-case naming and row-level security; EF migrations are the schema source of truth (sheet 00-index).
Gotchas / constraints
- No CQRS/MediatR. A .NET reader will assume MediatR; it is not present. Services are injected and called directly (see Clean Architecture).
- The schema lives in migrations, not a live DB dump. Anything needing live row counts is flagged per-sheet; the verified RBAC totals come from a live extract (
Sprint/RBAC_RolePermission_Extract.md). - Two PostgreSQL connection strings — an owner connection (BYPASSRLS, migrations only) and a runtime connection bound by RLS. Runtime can never bypass tenant isolation (sheet 01 §decisions,
Program.cs:147-149).
Build status
Available — the layered .NET 8 + React + PostgreSQL stack is the live product. Component-by-component status is on each page below and summarised on the Developer Guide overview.
Related
- Solution layout
- Clean Architecture
- Multi-tenancy
- Authentication & RBAC
- Fact sheets: 01 (foundation), 00-index, 20 (mobile).