System Architecture
High-level system design and technical architecture
This page describes the technical architecture of the PaulRentACar CRM — the technology stack, frontend and backend design, multi-tenant model, workflow engine, and data flow patterns.
Architecture Overview
PaulRentACar CRM follows a modern three-tier architecture with clear separation of concerns: a Next.js frontend, a Python FastAPI backend, and a PostgreSQL database. The system is designed for multi-tenant isolation, horizontal scaling, and enterprise-grade security.
High-level architecture — frontend communicates with the backend API, which enforces business rules and persists data to PostgreSQL.
Technology Stack
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | Next.js 16 (React 19) | Server-rendered React application with App Router |
| UI Framework | TypeScript, Tailwind CSS | Type safety and utility-first styling |
| Documentation | Fumadocs | MDX-based documentation site (this site) |
| Backend API | Python 3.12+ / FastAPI | Async REST API with automatic OpenAPI docs |
| ORM | SQLAlchemy 2.0 | Database models, relationships, and query building |
| Validation | Pydantic v2 | Request/response schemas and data validation |
| Database | PostgreSQL 16 | Primary data store with JSONB support |
| Migrations | Alembic | Version-controlled database schema migrations |
| Authentication | JWT (HS256) | Access tokens (60 min) + refresh tokens (7 days) |
| Password Hashing | bcrypt | Secure one-way password hashing |
| UUIDs | uuid4 | Primary keys for all business entities |
| Containerization | Docker Compose | Local development and deployment orchestration |
Frontend Architecture
The frontend is a Next.js 16 application using the App Router, TypeScript, and a feature-based folder structure.
App Router Structure
| Directory | Purpose |
|---|---|
app/ | Route pages organized by module (login, dashboard, customers, vehicles, etc.) |
app/login/ | Login page and authentication flow |
app/dashboard/ | Main dashboard with KPIs and widgets |
app/customers/ | Customer list, detail, create, and edit pages |
app/vehicles/ | Vehicle list, detail, status, and maintenance pages |
app/reservations/ | Reservation list, create, confirm, and manage pages |
app/contracts/ | Contract checkout, active, check-in, and history pages |
app/billing/ | Invoices, payments, deposits, and credit notes |
app/complaints/ | Complaint intake, tracking, and resolution pages |
app/compliance/ | Compliance tasks, legal cases, and corporate accounts |
app/admin/ | Users, roles, permissions, tenants, branches, workflows, pricing |
Component Architecture
| Directory | Purpose |
|---|---|
components/ui/ | Base UI primitives — buttons, inputs, modals, badges, cards, tables |
components/layout/ | App shell — sidebar, header, content area, breadcrumbs |
components/forms/ | Form builders for customer, vehicle, reservation, contract, and billing forms |
components/tables/ | Reusable data tables with sorting, filtering, pagination, and row actions |
components/workflow/ | Workflow status badges, action buttons, timeline, task inbox, approval panel |
components/audit/ | Audit log timeline and change history displays |
features/ | Domain feature modules — auth, customers, vehicles, reservations, contracts, billing, admin |
lib/ | Shared utilities — API client, auth helpers, permission checks, constants |
hooks/ | Custom React hooks for data fetching, form handling, and permission checks |
types/ | TypeScript type definitions for all API entities |
Frontend Design Principles
- Server components for static content and data fetching where possible
- Client components for forms, tables, modals, and interactive elements
- Permission-aware rendering — UI elements are hidden based on the user's role permissions
- Central API client — all HTTP calls go through a single configured client with auth headers
- Automatic token refresh — expired access tokens are refreshed transparently
- Responsive layout — works on desktop (1920px) and tablet (768px) with mobile card views
Backend Architecture
The backend follows a layered service pattern with clear boundaries between concerns:
Request → Route → Dependency Injection → Service → Repository → Model → DatabaseBackend Layer Responsibilities
| Layer | Directory | Responsibility |
|---|---|---|
| Routes | backend/app/api/routes/ | Thin HTTP handlers — parse request, call service, return response. No business logic here. |
| Dependencies | backend/app/api/deps.py | Reusable FastAPI dependencies — get current user, check permissions, verify tenant access |
| Services | backend/app/services/ | Business logic and orchestration — validate rules, execute workflows, coordinate transactions |
| Repositories | backend/app/repositories/ | Database access layer — query construction, filtering, pagination, bulk operations |
| Models | backend/app/models/ | SQLAlchemy ORM models — table definitions, relationships, indexes, constraints |
| Schemas | backend/app/schemas/ | Pydantic request/response models — validation rules, field types, nested structures |
| Security | backend/app/security/ | JWT generation/validation, password hashing, permission checking |
| Core | backend/app/core/ | Configuration, logging, error handling, constants |
| Database | backend/app/db/ | Session management, base model, migration support |
API Design Pattern
All endpoints follow a consistent REST design with a standard response envelope:
{
"success": true,
"data": { ... },
"message": "Operation completed",
"errors": []
}Paginated responses include:
{
"success": true,
"data": {
"items": [],
"page": 1,
"page_size": 20,
"total": 0
}
}Route Naming Convention
| Method | Pattern | Example |
|---|---|---|
GET | /api/{resource} | GET /api/customers — list customers |
POST | /api/{resource} | POST /api/customers — create customer |
GET | /api/{resource}/{id} | GET /api/customers/{id} — get customer detail |
PUT | /api/{resource}/{id} | PUT /api/customers/{id} — update customer |
DELETE | /api/{resource}/{id} | DELETE /api/customers/{id} — soft-delete customer |
POST | /api/{resource}/{id}/{action} | POST /api/reservations/{id}/confirm — confirm reservation |
Multi-Tenant Model
PaulRentACar CRM is multi-tenant by design. Every business entity includes a tenant_id foreign key, ensuring complete data isolation between tenants.
Tenant Isolation Hierarchy
Tenant
└── Business Unit
└── Branch
└── Users (with explicit access grants)
└── Vehicles, Customers, Contracts, etc.Access Control Tables
| Table | Purpose |
|---|---|
tenants | Top-level tenant configuration and settings |
business_units | Organizational divisions within a tenant (e.g., regions) |
branches | Physical locations within a business unit |
users | User accounts with credentials and profile |
roles | Named role definitions (Branch Manager, Front Desk, etc.) |
permissions | Granular permission strings (e.g., reservation:create) |
user_roles | Many-to-many: users to roles |
role_permissions | Many-to-many: roles to permissions |
user_tenant_access | Explicit tenant-level access grants |
user_business_unit_access | Explicit business unit-level access grants |
user_branch_access | Explicit branch-level access grants |
Authorization Flow
Every API request is authorized through this chain:
- JWT validation — token is valid and not expired
- Token not revoked — token has not been logged out or blacklisted
- User exists — the user ID in the token corresponds to an active user
- Tenant access — the user has access to the
tenant_idin the request - Branch access — the user has access to the
branch_idin the request (where applicable) - Permission check — the user has the required permission string (e.g.,
reservation:create) - Record ownership — the requested record belongs to an allowed tenant/branch
Frontend permission checks are for UI convenience only. Backend authorization is always the source of truth. Never rely solely on hiding buttons in the frontend to prevent unauthorized actions.
Workflow Engine
The platform includes a lightweight, domain-specific workflow engine that manages state transitions, task assignment, approvals, SLA tracking, and audit logging for every major business entity.
Workflow-Managed Entities
| Entity | Typical Stages |
|---|---|
| Reservation | Draft → Quoted → Pending Confirmation → Confirmed → Converted to Contract / Cancelled / No Show |
| Rental Contract | Draft → Checkout Pending → Active → Extension Requested → Return Pending → Damage Review → Billing Review → Closed / Cancelled |
| Lead | New → Contacted → Qualified → Converted / Lost |
| Quote | Draft → Submitted → Approved → Converted / Expired |
| Complaint | Open → Assigned → In Progress → Waiting Customer / Escalated → Resolved → Closed |
| Legal Case | Open → Under Review → Filed → Hearing Scheduled → Resolved / Closed |
| Damage Assessment | Reported → Assessed → Approved → Repaired / Billed |
| Invoice | Draft → Issued → Partially Paid → Paid → Overdue / Written Off |
| Security Deposit | Collected → Held → Released / Forfeited / Adjusted |
| Refund | Requested → Approved → Paid → Completed |
| Traffic Fine | Received → Charged to Customer → Paid / Disputed |
| Salik Charge | Recorded → Charged to Customer → Paid |
| Maintenance Request | Submitted → Approved → In Workshop → Completed → Vehicle Available |
| Compliance Task | Created → Assigned → In Progress → Completed / Overdue |
| Customer/Driver Document | Uploaded → Under Review → Verified / Rejected |
Workflow Engine Components
| Component | Purpose |
|---|---|
| WorkflowDefinition | Named workflow type (e.g., "Reservation Workflow") |
| WorkflowVersion | Versioned definition — stages and transitions for a specific version |
| WorkflowStage | Named state in the workflow (e.g., "Confirmed", "Active") |
| WorkflowTransition | Allowed move from one stage to another, with action code, required permission, and validation rule |
| WorkflowInstance | Running instance — tracks current stage for a specific business entity |
| WorkflowTask | Actionable task created by the workflow (e.g., "Review complaint", "Approve refund") |
| WorkflowApprovalRequest | Approval gate — requires designated approver before transition completes |
| WorkflowSlaRule | SLA deadline definition — due time per stage, warning thresholds |
| WorkflowActionLog | Immutable audit log — every transition, actor, timestamp, comment, old/new values |
Transition Execution Pattern
Every workflow action follows this sequence:
- Load the workflow instance for the target entity
- Verify tenant and branch access for the current user
- Load the current active workflow version
- Find the transition matching the current stage and requested action
- Check the required permission against the user's role
- Run the validation rule (e.g., "can this contract be checked in?")
- If approval is required, create an approval request and stop
- Execute the status update and workflow instance update in one transaction
- Create a workflow action log entry
- Create or complete tasks as needed
- Calculate the next SLA deadline
- Trigger a notification event
- Return the updated entity, available actions, and timeline
The workflow engine never silently drops events. Every transition is logged, every approval is recorded, and every SLA breach is captured. This ensures full auditability for compliance and dispute resolution.
Workflow API Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
GET | /api/workflows/definitions | List all workflow definitions |
GET | /api/workflows/instances/{entity_type}/{entity_id} | Get workflow instance for a business entity |
GET | /api/workflows/instances/{id}/timeline | Get full timeline of transitions for an entity |
GET | /api/workflows/instances/{id}/actions | Get available actions for the current stage |
POST | /api/workflows/instances/{id}/actions/{action_code} | Execute a workflow action |
GET | /api/workflow-tasks/my | Get tasks assigned to the current user |
POST | /api/workflow-tasks/{id}/complete | Mark a task as complete |
GET | /api/workflow-approvals/pending | Get approvals pending for the current user |
POST | /api/workflow-approvals/{id}/approve | Approve a workflow request |
POST | /api/workflow-approvals/{id}/reject | Reject a workflow request |
Data Flow Diagram
The following describes the end-to-end data flow for a typical rental transaction:
Customer walks in / books online
│
▼
┌─────────────┐
│ Lead │ Capture interest, source, assigned agent
└──────┬──────┘
│ Convert
▼
┌─────────────┐
│ Quote │ Pricing engine calculates rates, discounts, extras
└──────┬──────┘
│ Accept
▼
┌─────────────┐
│ Reservation │ Vehicle held, confirmation sent, SLA starts
└──────┬──────┘
│ Checkout
▼
┌─────────────┐
│ Contract │ Vehicle condition recorded, mileage, fuel, signature
└──────┬──────┘
│ Active rental period
▼
┌─────────────┐
│ Check-in │ Return inspection, damage assessment, final mileage
└──────┬──────┘
│ Billing
▼
┌─────────────┐
│ Invoice │ Line items: rental, extras, fines, damage, Salik
└──────┬──────┘
│ Payment
▼
┌─────────────┐
│ Payment │ Allocation across invoices, deposit release
└──────┬──────┘
│ Accounting
▼
┌─────────────────────┐
│ Accounting Subledger │ Journal entries, posting batches, export to ERP
└─────────────────────┘Each step in this flow is managed by the workflow engine, creating audit log entries and SLA tracking at every transition.
Accounting Subledger
The platform includes a summary journal posting subledger that feeds the tenant's existing accounting/ERP system. This is not a full general ledger — it is an operational subledger that generates balanced journal entries and exports them in CSV, Excel, JSON, or API format.
Accounting Data Flow
Operational Transaction (invoice, payment, deposit, fine, etc.)
│
▼
AccountingEvent (immutable record of the financial event)
│
▼
AccountingEntry + AccountingEntryLine (debit/credit pairs)
│
▼
Subledger Entries (customer, contract, deposit, corporate)
│
▼
AccountingPostingBatch (grouped by date, account, cost center)
│
▼
Export / Acknowledgement → Tenant's ERP/Accounting SystemPosting Batch Lifecycle
| Status | Description |
|---|---|
| Generated | Batch created from approved entries |
| Validated | Debit/credit balance confirmed, COA mappings verified |
| Pending Approval | Awaiting finance approval |
| Approved | Ready for export |
| Exported | File generated or API call made |
| Acknowledged | External system confirmed receipt |
| Failed | Export or validation error — requires retry |
Security Architecture
JWT Token Structure
Access Token (expires in 60 minutes):
| Claim | Purpose |
|---|---|
sub | User ID |
username | User email |
display_name | Full name |
tenant_id | Current tenant context |
business_unit_ids | Allowed business units |
branch_ids | Allowed branches |
roles | Assigned role names |
permissions | Effective permission strings |
token_type | "access" |
jti | Unique token ID (for revocation) |
Refresh Token (expires in 7 days):
| Claim | Purpose |
|---|---|
sub | User ID |
token_type | "refresh" |
jti | Unique token ID |
Security Measures
| Measure | Implementation |
|---|---|
| Password hashing | bcrypt with configurable rounds |
| JWT signing | HS256 with configurable secret key |
| Token revocation | Refresh tokens tracked in token_sessions; revoked tokens in revoked_tokens |
| Tenant isolation | Enforced at query level — never trust frontend tenant claims alone |
| Input validation | Pydantic schemas validate all incoming data |
| Audit logging | Immutable log for all critical operations with actor, timestamp, and change summary |
| Sensitive data | Passwords, tokens, Emirates IDs, passport numbers, and payment card data are never logged |
| CORS | Configurable allowed origins for frontend-backend communication |
Database Design Principles
| Principle | Implementation |
|---|---|
| Primary keys | UUID v4 for all business tables |
| Audit fields | Every table includes created_at, created_by, updated_at, updated_by |
| Tenant isolation | tenant_id on every tenant-owned table |
| Branch tracking | branch_id on branch-scoped records |
| Foreign keys | Enforced for all business relationships |
| Indexes | On tenant_id, branch_id, status, date ranges, document numbers, search fields |
| JSONB usage | Flexible metadata and external payloads only — never for core relational data |
| Status enums | Controlled status values via database enums or status tables |
| History tables | Status change history for reservations, contracts, vehicles, complaints |
| Migrations | Every schema change via Alembic — no manual production edits |
Deployment Architecture
Docker Compose (Local Development)
| Service | Port | Purpose |
|---|---|---|
| PostgreSQL | 5432 | Primary database |
| Backend API | 8000 | FastAPI application server |
| Frontend | 3000 | Next.js development server |
Environment Configuration
| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL connection string |
JWT_SECRET_KEY | Secret key for JWT signing |
JWT_ALGORITHM | Signing algorithm (HS256) |
JWT_ACCESS_TOKEN_EXPIRE_MINUTES | Access token lifetime |
JWT_REFRESH_TOKEN_EXPIRE_DAYS | Refresh token lifetime |
CORS_ALLOWED_ORIGINS | Allowed frontend origins |
APP_ENV | Environment name (local, staging, production) |
Never commit real secrets or credentials to the repository. Use environment variables and .env files (which are gitignored) for all sensitive configuration.
What's Next?
- Getting Started — Log in and explore the interface
- Business Processes — Leads, quotes, reservations, and contracts
- Workflow Configuration — Configure workflows, stages, and transitions