The complete
Calitex system
architecture
End-to-end order lifecycle management for wholesale distribution. From customer ordering through warehouse operations, last-mile delivery, to invoicing and accounting reconciliation.
What Calitex Does
A B2B wholesale distribution platform serving business customers across the Netherlands. Manages the complete chain from product ordering to warehouse fulfillment and last-mile delivery with integrated invoicing.
User Roles Doc 01
Customer
Mobile app. Browse products, place orders, select delivery dates, pay invoices, track deliveries.
Order Picker
Web + mobile. Pick & pack orders with barcode scanning, manage loading lists, deliver goods.
Admin
Web panel. Manage stock, customers, products, regions, carriers. View-only on order status.
SuperAdmin
Full control. Manage admin accounts, reports, force-cancel orders, approve sensitive changes.
Core Capabilities Doc 01, 02
Payment Gating
Orders blocked if customer has unpaid invoices >30 days or exceeds credit limit. AI-enhanced risk scoring.
Cut-off Time Engine
Orders before 14:00 eligible for next-day delivery. After 14:00, earliest is day-after-tomorrow. Region-configurable.
Capacity Management
Weight and volume limits per delivery day per region. Pessimistic locks prevent overbooking.
Barcode Pick & Pack
Warehouse staff scan product barcodes during picking. System validates against order. Wrong product = immediate alert.
QR Delivery Tracking
QR codes on pallets. Driver scans to identify order and recipient. Signature capture for proof-of-delivery.
Accounting Sync
Invoices and payments sync to Exact Online. Event-driven with daily reconciliation. OAuth token lifecycle managed.
Application Interfaces
Three purpose-built applications serve different user roles. Customer orders via mobile, pickers operate in the warehouse, administrators manage the business through a web dashboard.
| Order | Klant | Bedrag | Status |
|---|---|---|---|
| CAL-0416-0247 | Vloeren Discount Amsterdam | €4,342.50 | Picking |
| CAL-0416-0246 | Woonwarenhuis Den Haag | €2,189.00 | Confirmed |
| CAL-0416-0245 | Interieur Plus Rotterdam | €8,750.80 | Delivery |
| CAL-0416-0244 | FloorPro Eindhoven | €1,894.30 | Confirmed |
Order Lifecycle
The complete journey from customer order to invoice completion. Every step is transactionally protected with defined failure recovery paths.
Order
Validation
Scheduling
Confirmed
Pack
QR Codes
Delivery
Accounting
Transaction Boundary Doc 12
Order confirmation is a single atomic database transaction that reserves delivery capacity (FOR UPDATE lock on capacity slot), reserves inventory (FOR UPDATE lock on products, sorted by ID to prevent deadlocks), creates the order, and creates the delivery record. If any step fails, everything rolls back. Async side effects (email, stock sync) run after commit via BullMQ.
System Architecture
Modular monolith deployed on AWS. Clean module boundaries with defined data ownership. Every module communicates through explicit service calls (transactional) or BullMQ queues (async).
Why Modular Monolith Doc 02
Team of 4-6 engineers. All domains tightly coupled in business logic. Single deployment unit for simpler CI/CD, debugging, and on-call. Clear module boundaries allow future service extraction.
Why Not Microservices
Distributed transactions across services for "validate payment + check capacity + confirm order" adds unnecessary complexity. No independent scaling needs at this volume.
Critical Workflows
Step-by-step operational flows with failure handling at each stage.
Phase 1 — Pre-validation (read-only, outside transaction)
Validate customer exists and is active. Load product details. AI OrderDecisionAgent evaluates risk (if applicable). Payment validation checks overdue invoices and credit limit. Cut-off time engine calculates earliest delivery date. Capacity engine verifies availability.
Phase 2 — Atomic reservation (single DB transaction)
SELECT FOR UPDATE on delivery_capacity_slots. SELECT FOR UPDATE on products (sorted by ID). Insert inventory transactions. Create order with status CONFIRMED. Create delivery record. Generate order number via PostgreSQL SEQUENCE.
Phase 3 — Async side effects (after commit)
BullMQ: order confirmation email, push notification, webshop stock sync. For critical events (invoice, Exact sync): transactional outbox pattern ensures delivery even if process crashes after commit.
If payment required: Order created with PENDING_PAYMENT status. Stripe PaymentIntent created with orderId in metadata. Webhook processes payment and advances to CONFIRMED. Cleanup job cancels stale PENDING_PAYMENT orders after 30 minutes.
Trigger: Daily scheduled job at 16:00 CET generates pick lists for tomorrow's deliveries.
Picker opens app, selects order from pick list. System transitions order to IN_PROGRESS (customer can no longer cancel). Picker scans each product barcode. System validates barcode matches expected product and checks quantity. Wrong barcode = immediate audio/visual alert.
All items picked → status PICKED. Picker confirms physical packing → status PACKED. If product not in stock: picker marks item SHORT. Admin decides: ship partial or hold.
Concurrency protection: Optimistic locking (version column) on pick list items prevents two pickers from picking the same item.
Loading: Aggregate all PACKED orders for a region + delivery date. Generate loading list. Assign driver. Generate QR codes for each order/pallet (contains order ID, customer name, address). Print QR stickers. Confirm loading → status LOADED.
Delivery: Driver opens app, sees delivery list ordered by route. Starts route → all orders status OUT_FOR_DELIVERY. Customers notified via push. At each stop: driver scans QR code on pallet. System shows order details + recipient. Driver delivers goods. Customer signs on device. Signature stored in S3 with GPS coordinates and timestamp. Confirm → status DELIVERED.
Failure: Customer absent → DELIVERY_FAILED. Admin can reschedule (returns to CONFIRMED for re-pick) or cancel. App works offline with sync-on-reconnect.
Invoice generation is a resumable multi-step process triggered by order delivery. Step 1: Create invoice record. Step 2: Generate PDF, upload to S3. Step 3: Email to customer. Step 4: Advance order to COMPLETED. Step 5: Enqueue Exact sync via outbox. Each step checks if previous step completed — safe to retry.
Payment application: Customer selects invoices to pay, or enters amount (applied to oldest first via FIFO). Stripe PaymentIntent created. On webhook success: amount split across invoices. SELECT FOR UPDATE on invoice rows prevents double-application. Webhook handler always fetches current PaymentIntent state from Stripe API (handles out-of-order delivery).
Order State Machine
Strict state transitions enforced via optimistic locking. Every transition logged in audit trail.
Cancellation Rules Doc 04
Invoice States
Delivery States
Core Entities
PostgreSQL 16 with Prisma ORM. Strong relational integrity for financial data. ACID transactions protect all state changes.
Inventory Truth Doc 11
Source of truth: inventory_transactions ledger (append-only). products.stock_quantity is a cached counter, recalculated in the same transaction. Daily reconciliation verifies consistency. CHECK constraint prevents negative stock.
Outbox Pattern Doc 15
Critical async events (invoice generation, Exact sync) written to outbox_events table inside the DB transaction. Poller reads every 5 seconds and enqueues to BullMQ. Survives process crashes after commit.
Concurrency & Data Integrity
Every race condition identified, analyzed, and fixed with specific locking strategies. No vague "optimistic locking" — every lock type is chosen and justified.
Running in Production
Structured logging, business + technical metrics, actionable alerts with runbooks, zero-downtime deployments.
Monitoring
CloudWatch + Grafana. Business metrics (orders/hour, delivery success rate, overdue invoices) and technical metrics (API latency, DB connections, queue depth).
Alerting
P1: API error spike, DB pool exhaustion, payment webhook DLQ, pick lists not generated. P2: invoice stuck, Exact failing, capacity near full. P3: latency, queue backlog.
Runbooks
Step-by-step recovery guides: payment webhook failure, stuck invoices, capacity overbooking, Exact sync failure. What system does, what human does.
CI/CD
GitHub Actions. PR: lint + type-check + unit tests (parallel) → integration tests → staging deploy. Main: manual approval gate → ECS rolling update (zero-downtime).
Migration Safety
Every migration backward-compatible. Add nullable columns, CREATE INDEX CONCURRENTLY, dual-write for renames. SET lock_timeout = 5s. Never break running code.
Structured Logging
JSON format. Every entry: timestamp, requestId, userId, module, level. Correlation IDs flow from API through BullMQ workers via AsyncLocalStorage.
SLA Targets Doc 16
What Breaks & How We Recover
Every critical failure scenario analyzed with specific recovery design. Chaos testing plan validates all scenarios before launch.
AI Decision Layer
An advisory AI layer that sits on top of the deterministic backbone. AI suggests. The system decides. Transactions remain atomic. The system works without AI.
AI Agents Doc 17
Three-Layer Guardrails
Layer 1: Zod schema validation on every AI output.
Layer 2: Business rule post-check (hard rules always win).
Layer 3: Confidence gate (below threshold = deterministic fallback).
Feedback Loop
Every AI decision logged with input, output, confidence, and whether it was accepted or overridden. Outcomes tracked (did customer pay on time? did delivery succeed?). Monthly prompt refinement based on override patterns.
Documentation Provenance
This presentation synthesizes 17 architecture documents produced through four review rounds. Each section traces to its source material.