Architecture
System Architecture
How EasyFlow's layers fit together — API, workflow engine, messaging bus, and UI.
Overview
EasyFlow is a multi-tenant supply chain process operating system. It is built as a monorepo with five distinct layers, each with a clear responsibility. They communicate via REST APIs and an async message queue.
The five layers
Web app — apps/web
Next.js 14 application with Tailwind CSS and shadcn/ui. This is the product shell — the canvas, dashboards, admin portal, and all user-facing screens.
- Business process canvas — visual drag-and-drop workflow designer built on React Flow
- Tenant dashboards — procurement, inventory, logistics, suppliers, users per workspace
- Globe view — animated entry point showing all tenant workspaces
- Admin portal — super admin and tenant admin management
- Integrations UI — n8n setup guide, webhook credentials, connector status
- Docs site — this documentation, built in-app
- Auth — Google OAuth + email/password via better-auth
API server — apps/api
FastAPI (Python) service that handles all business logic, access control, and data persistence.
- Auth + RBAC — every request is verified against the actor's role and tenant scope
- Tenant registry — create, list, update workspaces and users
- Workflow APIs — save, retrieve, and simulate workflow definitions
- Connector APIs — register and test ERP connections per tenant
- Inbound webhooks — receive events from ERPs and n8n workflows
- Notification publisher — push events to RabbitMQ for async processing
- Alembic migrations — versioned database schema management
Workflow engine — packages/engine
Pure Python library with no framework dependencies. The engine validates workflow graphs, simulates execution, and generates execution timelines.
- Graph validation — detects cycles, disconnected nodes, missing required fields
- Workflow simulation — walks through a workflow definition and produces a step-by-step execution plan
- Execution modelling — tracks state transitions, assignees, deadlines, and escalation rules
- No database coupling — the engine is stateless and receives all context it needs as input
Async runtime — RabbitMQ + worker
RabbitMQ carries all async events between the API and the background worker. The worker consumes events, runs them through the workflow engine, and publishes results.
- Event-driven — every workflow action is a message, not a synchronous call
- Retry and DLQ — failed events are retried with backoff, then moved to a dead-letter queue for inspection
- Prometheus metrics — execution counts, latency, and error rates exposed at /metrics
- Webhook inbound — events from ERPs arrive via webhook, get normalised, and published to RabbitMQ
Connector layer — packages/connectors
Pluggable integration SDK. Each connector implements two methods: test_connection() and fetch_master_data(). The n8n / webhook pattern means most ERPs don't need a custom connector — n8n handles the ERP-specific auth and sends normalised payloads to EasyFlow.
Technology stack
| Layer | Technology | Why |
|---|---|---|
| Frontend | Next.js 14, Tailwind CSS, shadcn/ui | App router, server components, rapid UI development |
| API | FastAPI, SQLAlchemy, Alembic | Async Python, auto-generated docs, type safety |
| Database | PostgreSQL (Neon in production) | Multi-tenant schema, reliable, scales to large datasets |
| Auth | better-auth | Google OAuth, email/password, session management |
| Messaging | RabbitMQ | Reliable async delivery, DLQ, management UI |
| Automation | n8n (self-hosted) | 400+ ERP integrations, visual workflows, open source |
| Analytics & ML | Databricks (AWS us-east-2) | Batch ML scoring for inventory risk, supplier delay, and order slip — results read via SQL Statement API |
| Engine | Pure Python | Portable, testable, no framework coupling |
| Containers | Docker + docker compose | One command to start everything |
Monorepo structure
EasyFlow/
├── apps/
│ ├── api/ FastAPI backend
│ │ ├── app/
│ │ │ ├── main.py Application entry point + routes
│ │ │ ├── connectors.py ERP connector CRUD APIs
│ │ │ ├── webhooks.py Inbound webhook receiver
│ │ │ ├── access.py RBAC — roles and tenant scoping
│ │ │ ├── models.py SQLAlchemy ORM models
│ │ │ ├── store.py Data access layer
│ │ │ └── worker.py RabbitMQ consumer
│ │ └── migrations/ Alembic database migrations
│ └── web/ Next.js frontend
│ ├── app/ App router pages
│ │ ├── page.tsx Landing page
│ │ ├── login/ Auth pages
│ │ ├── globe/ Tenant workspace selector
│ │ ├── dashboard/ Operations dashboard
│ │ ├── workflows/ Process canvas
│ │ ├── admin/ Admin portal
│ │ ├── settings/ Integrations
│ │ └── docs/ This documentation
│ ├── components/ Shared UI components
│ └── lib/ Utilities, stores, types
├── packages/
│ ├── engine/ Workflow execution engine
│ │ └── easyflow_engine/
│ │ ├── engine.py Graph traversal and execution
│ │ └── models.py Workflow data models
│ └── connectors/ ERP connector SDK
│ ├── base.py Connector interface
│ ├── factory.py Connector catalog + factory
│ ├── http_adapter.py Generic HTTP connector
│ └── webhook_connector.py n8n / webhook connector
├── examples/
│ └── n8n-workflows/ Ready-to-import n8n templates
│ ├── sap-to-easyflow.json
│ └── oracle-to-easyflow.json
└── docker-compose.yml Full stack: API + n8n + Postgres + RabbitMQ