EngiFlow is a multi-tenant B2B SaaS platform for managing Engineering Change Order (ECO) workflows. It provides a governed environment for engineering, manufacturing, and quality teams to track and approve product lifecycle changes while enforcing ISO 9001-compliant segregation of duties and tenant-specific workflow policies.
- Global Search: Tenant-isolated resource navigation for locating ECOs and system resources.
- Role-Based Dashboards: Data-dense interfaces that adjust visibility and metrics based on user permissions.
- Workflow Governance: Configurable approval quorums and material action compliance enforcement.
- Internationalization: Multi-language support (English and Portuguese) implemented via Next.js 16 Edge proxying and cookie-based locale synchronization.
The platform is built on a Clean Architecture foundation with real-time security enforcement, automated audit trails, and strict data isolation across the full stack.
Engineering changes affect released designs, supplier requirements, manufacturing instructions, quality records, cost, schedule, and customer commitments. EngiFlow treats an ECO as a controlled business record instead of a generic task:
- Requesters create and revise ECO drafts.
- Authorized reviewers make formal approval decisions.
- Tenant policy determines the approval quorum.
- The author of an ECO cannot approve their own change.
- Every material workflow action creates an audit event.
- Attachments are stored in S3-compatible object storage with database metadata.
- Administrators can change roles or deactivate users, and affected sessions are updated immediately over SignalR.
The result is an enterprise-ready foundation for PLM workflows where compliance controls are enforced in the backend domain and reflected immediately in the frontend.
EngiFlow is a monorepo with a .NET 10 API and a Next.js 16 web application.
flowchart LR
Browser["Browser"]
Web["Next.js web app<br/>Material UI, App Router"]
Api["ASP.NET Core API<br/>CQRS, JWT, SignalR"]
Db[("PostgreSQL<br/>EF Core, xmin")]
ObjectStore[("MinIO or S3<br/>ECO attachments")]
Mail["Mailpit or SMTP<br/>password reset"]
Browser --> Web
Browser --> Api
Web --> Api
Api --> Db
Api --> ObjectStore
Api --> Mail
sequenceDiagram
actor Client as User Client
participant Frontend as Next.js Proxy Layer
participant API as .NET 10 API Controller
participant Pipeline as MediatR Unit-of-Work
participant DB as PostgreSQL (EF Core)
Client->>Frontend: HTTP Request (Bearer JWT)
Frontend->>API: Proxy Request (Authorization: Bearer)
API->>API: Unpack JWT Claims (`sub`, `tenant`, `role`, `company_name`)
API->>Pipeline: Dispatch Command (Inject Tenant Context)
Pipeline->>DB: Query/Write with Global Tenant Row-Level Filter
DB-->>Pipeline: Tenant-Isolated Result
Pipeline-->>API: Application Response
API-->>Frontend: JSON Payload
Frontend-->>Client: Rendered UI
Browser HTTP calls normally use the Next.js /api/... proxy unless a public API URL is configured. SignalR connections intentionally connect directly to the ASP.NET Core API at /hubs/ecos and /hubs/security so WebSocket traffic bypasses the Next.js proxy.
| Area | Technology |
|---|---|
| Frontend | Next.js 16, React 19, TypeScript, Material UI, MUI X DataGrid, MUI X Date Pickers |
| Backend | .NET 10, ASP.NET Core Web API, SignalR, JWT bearer authentication |
| Application | Internal CQRS Dispatcher-backed CQRS, FluentValidation, post-commit notifications |
| Domain | Clean Architecture, DDD aggregate roots, strongly typed IDs |
| Persistence | EF Core 10, Npgsql, PostgreSQL 18, xmin optimistic concurrency |
| Storage | S3-compatible attachment storage, MinIO for local development |
| MailKit SMTP, Mailpit for local password reset mail | |
| Orchestration | Docker Compose |
| Tests | xUnit for API, application, domain, and infrastructure projects |
.
+-- api/
| +-- src/
| | +-- EngiFlow.Api/
| | +-- EngiFlow.Application/
| | +-- EngiFlow.Domain/
| | +-- EngiFlow.Infrastructure/
| +-- tests/
| +-- EngiFlow.Api.Tests/
| +-- EngiFlow.Application.Tests/
| +-- EngiFlow.Domain.Tests/
| +-- EngiFlow.Infrastructure.Tests/
+-- web/
| +-- app/
| +-- components/
| +-- lib/
+-- docker-compose.yml
stateDiagram-v2
[*] --> Draft: Create ECO
Draft --> UnderReview: Submit for Review
Draft --> Canceled: Cancel
state UnderReview {
[*] --> PendingQuorum
PendingQuorum --> Approved: Quorum Threshold Met
PendingQuorum --> RevisionRequired: Request Changes
}
Approved --> [*]
RevisionRequired --> Draft: Return for Revision
Canceled --> [*]
note right of UnderReview
Compliance Rule: AllowSelfApproval = false
Author participation in quorum is blocked.
end note
Important workflow rules:
- Draft ECOs can be edited, commented on, and have affected items or attachments added.
- Submitting an ECO starts a new review round.
- Only decisions from the active review round count toward quorum.
RequestChangesreturns the ECO to draft and requires a new review round.- Approved and canceled ECOs are terminal for the active workflow.
- The domain aggregate creates audit events as part of state transitions.
EngiFlow enforces a hard segregation-of-duties rule:
Compliance Rule: The author of the ECO cannot participate in its approval quorum
The rule is enforced in the ECO decision path and in the aggregate itself, so compatibility routes and future callers cannot bypass it. This implements the core quality-system principle that the person requesting or authoring a controlled change cannot be the person approving that same change into effect.
Tenant owners and administrators manage workflow settings through:
| Method | Route | Purpose |
|---|---|---|
GET |
/api/settings |
Read tenant workflow settings |
PUT |
/api/settings |
Update minApprovalsRequired |
MinApprovalsRequired must be at least 1. If an older tenant lacks a settings row, EngiFlow creates default settings on first read or update.
The frontend warns administrators when the configured quorum is higher than the count of active users whose exact role is Approver:
Warning: You require X approvals, but only have Y Approvers active. ECOs may become stuck.
Owner and Administrator users can approve by authorization policy, but the warning intentionally focuses on active Approver role users to ensure a healthy quorum buffer.
| Capability | Owner | Administrator | Approver | Requester | Viewer |
|---|---|---|---|---|---|
| Read ECOs | Yes | Yes | Yes | Yes | Yes |
| Comment on ECOs | Yes | Yes | Yes | Yes | Yes |
| Create ECOs | Yes | Yes | No | Yes | No |
| Edit draft ECOs | Yes | Yes | No | Yes | No |
| Submit ECOs for review | Yes | Yes | No | Yes | No |
| Approve or request changes | Yes | Yes | Yes | No | No |
| Manage users | Yes | Yes | No | No | No |
| Manage workflow policy | Yes | Yes | No | No | No |
Additional immutable rules:
Ownerinherits Administrator privileges.- Owner users cannot be modified or deactivated.
- No endpoint can create or promote a user to Owner.
- Users cannot change their own role.
- Users cannot deactivate themselves.
- Deactivated users cannot authenticate or act in the domain.
EngiFlow uses two authenticated SignalR hubs:
| Hub | Purpose |
|---|---|
/hubs/ecos |
Tenant-scoped ECO timeline/status updates after committed commands |
/hubs/security |
Targeted current-user role/deactivation enforcement |
ECO events are broadcast to the tenant group. Security events are targeted to the affected SignalR user ID, which is derived from the JWT sub claim.
Security events:
UserPermissionsChanged(userId, newRole): the current browser session updates its stored role immediately.UserDeactivated(userId): the current browser session clears auth storage and hard redirects to/login.
The API also refreshes the role from the database during JWT validation and rejects inactive users. This prevents stale JWT role claims from retaining old privileges after an administrative change.
Important
Infrastructure Status & FinOps Strategy:
Terraform IaC templates and GitHub Actions CI/CD pipelines are currently in development and are NOT present in this branch. To ensure maximum cost efficiency and credit preservation, the application is designed for ephemeral cloud execution: it is spun up on AWS for verification cycles and torn down immediately via terraform destroy.
Evaluation Standard: Localhost via Docker Compose is the official, fully operational, zero-cost first-class citizen environment for testing and evaluating all application features.
- Docker Desktop or Docker Engine with Compose.
- .NET SDK 10 for local backend work.
- Node.js 24 for local frontend work.
From the repository root:
docker compose up --buildServices:
| Service | URL or Port | Purpose |
|---|---|---|
web |
http://localhost:3000 |
Next.js frontend |
api |
http://localhost:8080 |
ASP.NET Core API |
| Swagger | http://localhost:8080/swagger |
API explorer in Development |
postgres |
localhost:5432 |
PostgreSQL database |
minio |
http://localhost:9001 |
S3-compatible object storage console |
mailpit |
http://localhost:8025 |
Local SMTP inbox |
Named volumes preserve local database and MinIO object state:
postgres-dataminio-data
Stop the stack:
docker compose downRemove volumes too:
docker compose down -vSince the environment is empty by default, start by registering a new tenant:
http://localhost:3000/register
Once registered, you can log in at:
http://localhost:3000/login
Docker Compose supplies local defaults for PostgreSQL, MinIO, and Mailpit. Production deployments must override at least JWT signing settings and external service credentials.
JWT configuration:
{
"EngiFlow": {
"Authentication": {
"Jwt": {
"Issuer": "EngiFlow.Api",
"Audience": "EngiFlow.Clients",
"SigningKey": "replace-with-at-least-32-characters",
"AccessTokenMinutes": 60
}
}
}
}S3-compatible storage:
{
"EngiFlow": {
"Storage": {
"S3": {
"BucketName": "engiflow-attachments",
"Region": "us-east-1",
"ServiceUrl": "http://localhost:9000",
"AccessKey": "minioadmin",
"SecretKey": "minioadmin",
"ForcePathStyle": true
}
}
}
}Frontend API configuration:
| Variable | Purpose |
|---|---|
API_INTERNAL_BASE_URL |
Server-side Next.js proxy target, defaulting to http://api:8080 in Docker |
NEXT_PUBLIC_API_URL |
Browser-visible API base URL |
NEXT_PUBLIC_API_BASE_URL |
Browser-visible API base URL fallback |
If no public API base URL is set, SignalR uses http://localhost:8080 for direct browser connections.
| Method | Route | Purpose |
|---|---|---|
POST |
/api/auth/login |
Authenticate, update LastLoginAt, issue JWT |
POST |
/api/auth/register-company |
Create tenant, first Owner, default settings, JWT |
POST |
/api/auth/forgot-password |
Send password reset mail through SMTP |
GET |
/api/settings |
Read tenant workflow settings |
PUT |
/api/settings |
Update tenant workflow settings |
GET |
/api/users |
List active tenant users with lastLoginAt |
POST |
/api/users |
Create Administrator, Approver, Requester, or Viewer |
PUT |
/api/users/{id}/role |
Change a mutable user's role |
PUT |
/api/users/{id}/deactivate |
Soft-deactivate a mutable user |
POST |
/api/ecos |
Create a draft ECO |
GET |
/api/ecos |
List paged ECO summaries |
GET |
/api/ecos/{id} |
Read an ECO with timeline details |
PUT |
/api/ecos/{id}/details |
Update draft ECO details |
POST |
/api/ecos/{id}/affected-items |
Add an affected item |
DELETE |
/api/ecos/{id}/affected-items/{itemId} |
Remove an affected item |
POST |
/api/ecos/{id}/comments |
Add a timeline comment |
POST |
/api/ecos/{id}/attachments |
Upload attachment metadata and S3 object |
PUT |
/api/ecos/{id}/submit |
Submit draft ECO for review |
POST |
/api/ecos/{id}/review-decisions |
Submit Approve or RequestChanges |
PUT |
/api/ecos/{id}/cancel |
Cancel a draft or under-review ECO |
PUT |
/api/ecos/{id}/approve |
Compatibility approval route |
PUT |
/api/ecos/{id}/reject |
Compatibility request-changes route |
Backend:
dotnet test api/EngiFlow.slnx /m:1Frontend:
cd web
npm run lint
npm run buildUseful Docker checks:
docker compose config
docker compose build
docker compose psThe following table maps the current Local Evaluation tier components to their targeted AWS Production Cloud equivalents:
| Component | Local Evaluation (Docker) | AWS Production Cloud (Target) |
|---|---|---|
| App Shell | Next.js 16 (Node.js 24) | AWS App Runner + Amazon CloudFront |
| Web API | ASP.NET Core (.NET 10) | AWS App Runner |
| Database | PostgreSQL 18 | Amazon RDS PostgreSQL (Fully Managed) |
| File Storage | MinIO (S3-Compatible) | Amazon S3 |
| Notifications | Mailpit (SMTP) | Amazon SES |
| Provisioning | Docker Compose | HashiCorp Terraform (Planned) |