Cymelle
Cymelle is a commerce, delivery, and ride operations application. Customers can browse products, place orders, request delivery rides, and complete a simulated checkout. Admins manage products, inventory, orders, users, and rides. Drivers accept and complete delivery rides.
Architecture
The current system is a three-tier application: a React frontend, a Spring Boot API, and PostgreSQL.
+-----------------------------+
| Browser |
| Customer / Admin / Driver |
+-------------+---------------+
|
| HTTP
| http://localhost:3000
v
+-----------------------------+
| orders-frontend |
| React + TypeScript |
| TanStack Router + Query |
| Vite dev server |
+-------------+---------------+
|
| REST API
| VITE_API_BASE_URL
| http://localhost:8080/api
v
+-----------------------------+
| orders-backend |
| Spring Boot REST API |
| Security + Services + JPA |
| Swagger/OpenAPI |
+--------+--------------------+
|
| JDBC
v
+----------------+
| PostgreSQL |
| app data |
+----------------+
Local Docker Compose runs the same shape with three services:
+------------------------------------------------------------+
| Docker Compose project: cymelle |
| |
| web: node:24 api: temurin 17 |
| pnpm dev --host 0.0.0.0 ./gradlew bootRun |
| localhost:3000 -------------> localhost:8080 |
| | | |
| v v |
| orders-frontend/ db: postgres:18 |
| bind mount localhost:5432 |
| | |
| v |
| postgres-data volume |
+------------------------------------------------------------+
Project Layout
orders-frontend/: React web app, route files, API clients, hooks, shared UI, dashboard and storefront screens.orders-backend/: Spring Boot API, controllers, services, repositories, DTOs, entities, security, OpenAPI, and seed data.compose.yml: Local development stack for Postgres, API, and web..env.example: Local environment variables consumed by Docker Compose.render.yaml: Render Blueprint for Docker-backed deployment.DEPLOYMENT.md: Render and Neon deployment notes.PART_2.md: Real-time location tracking backend design walkthrough.
Tech Stack
Frontend
- React 19
- TypeScript
- Vite
- TanStack Router for file-based routing
- TanStack Query for server state
- TanStack Table and TanStack Form
- Tailwind CSS 4
- Radix UI, local UI components, Lucide React, and Tabler Icons
- pnpm
- Biome for linting and formatting
- Vitest for tests
- Nitro output for production server builds
Backend
- Java 17
- Spring Boot 3.5
- Spring Web
- Spring Security
- Spring Data JPA
- Bean Validation
- PostgreSQL driver
- H2 test dependency
- Springdoc OpenAPI / Swagger UI
- Gradle Kotlin DSL
Infrastructure
- Docker Compose for local development
- PostgreSQL 18 Alpine locally
- Render Docker web services for deployment
- Neon PostgreSQL for deployed database
- Simulated checkout for local and deployed testing
Domain Model
AppUser
id, username, displayName, passwordHash, role, enabled
role: ADMIN | CUSTOMER | DRIVER
|
+--> CUSTOMER owns Orders and requested Rides
+--> DRIVER can accept and complete Rides
+--> ADMIN manages products, users, orders, inventory, and rides
Product
id, name, sku, price, currency, active
|
+-- one-to-one --> InventoryItem
|
+-- many-to-one from --> OrderItem
InventoryItem
id, product, availableQuantity, reservedQuantity, reorderLevel
status: IN_STOCK | LOW_STOCK | OUT_OF_STOCK
Order
id, customerName, customer, status, totalAmount, currency, createdAt
status: PENDING | SHIPPED | DELIVERED | CANCELLED
|
+-- one-to-many --> OrderItem
|
+-- optional delivery association --> Ride
|
+-- one-to-one --> Payment
OrderItem
id, order, product, productName, quantity, unitPrice, lineTotal
stores product name and price snapshot at checkout time
Ride
id, customer, driver, order, pickupLocation, dropoffLocation
distanceKm, fareAmount, currency, status
status: REQUESTED | ACCEPTED | COMPLETED | CANCELLED
Payment
id, order, ride, reference, productSubtotal, rideFare
amountMajor, amountSubunits, currency, status
status: PENDING | PAID | FAILED | ABANDONED | REFUNDED
Main Application Flows
Authentication
User registers or logs in
|
v
Spring Security authenticates credentials
|
v
Frontend calls /api/auth/me
|
v
Routes and backend endpoints use the user's role
Roles drive most behavior:
- Customers can browse, order, pay, request rides, and view their own orders.
- Drivers can see requested rides, accept rides, and complete assigned rides.
- Admins can manage users, products, orders, inventory, and ride status.
Product and Inventory
Admin creates product
|
v
Product stored with SKU, price, currency, active flag
|
v
InventoryItem tracks available quantity and reorder level
|
v
Inventory status is derived from available quantity
Inventory status is computed:
OUT_OF_STOCKwhen available quantity is0LOW_STOCKwhen available quantity is at or below the reorder levelIN_STOCKotherwise
Checkout Quote
Customer submits cart + optional delivery ride
|
v
Backend validates products and stock
|
v
Backend calculates product subtotal
|
+--> optional fare calculation for delivery ride
|
v
Quote returns productSubtotal, rideFare, grandTotal, currency, expiresAt
The quote endpoint is useful before checkout so the customer can see the final total.
Order Placement
Customer places order
|
v
Backend loads active products
|
v
Backend locks inventory rows for update
|
+--> insufficient stock -> request fails
|
v
Inventory is deducted
|
v
Order and OrderItems are created as PENDING
|
+--> optional delivery ride is created as REQUESTED
Order items store productName, unitPrice, and lineTotal, so historical orders keep their checkout-time product snapshot even if the product changes later.
Simulated Checkout
Customer starts checkout
|
v
Backend calculates quote and creates pending order
|
v
Payment row is created with unique reference
|
v
Backend marks the local simulated payment as PAID
|
v
Frontend clears the cart and sends the customer to orders
No external payment provider is required for local or deployed testing.
Order Status
PENDING -> SHIPPED -> DELIVERED
|
+-> CANCELLED
Rules in the domain model:
- Only pending orders can be cancelled by the normal cancellation flow.
- Cancelling restores inventory quantities.
- Final orders cannot be moved to another status.
- Completing an attached ride marks the order as delivered.
Ride Status
REQUESTED -> ACCEPTED -> COMPLETED
|
+-> CANCELLED
Ride behavior:
- Customers request rides for their own orders.
- Drivers see requested rides plus rides assigned to them.
- Drivers accept requested rides.
- Drivers complete rides assigned to them.
- Completing a ride also marks the attached order as delivered.
- Admins can cancel or complete rides.
API Surface
All backend endpoints are under /api.
| Area | Endpoints |
|---|---|
| Auth | POST /auth/register, POST /auth/login, POST /auth/logout, GET /auth/me, GET /auth/csrf |
| Products | GET /products, GET /products/{id}, POST /products, PUT /products/{id}, DELETE /products/{id} |
| Inventory | GET /inventory, GET /inventory/low-stock |
| Orders | POST /orders, GET /orders, GET /orders/{id}, DELETE /orders/{id}, PATCH /orders/{id}/status |
| Checkout | POST /checkout/quote, POST /checkout/simulate |
| Rides | GET /rides, POST /rides, POST /rides/{id}/accept, POST /rides/{id}/complete, DELETE /rides/{id}, PATCH /rides/{id}/status |
| Users | GET /users, GET /users/customers, GET /users/drivers, POST /users, PUT /users/{id}, DELETE /users/{id} |
| Fare | GET /fare/calculate |
| Health | GET /health |
Swagger UI is available locally at http://localhost:8080/swagger-ui.html.
Seed Data
Local development seeds demo data when SEED_DATA=true.
Demo users:
| Role | Username | Password |
|---|---|---|
| Admin | admin |
admin123 |
| Driver | driver |
driver123 |
| Driver | driver2 |
driver123 |
| Driver | driver3 |
driver123 |
| Customer | customer |
customer123 |
| Customer | customer2 |
customer123 |
| Customer | customer3 |
customer123 |
| Customer | customer4 |
customer123 |
Seeded products include accessories and office equipment such as wireless mice, keyboards, cables, stands, webcams, headphones, monitors, docks, backpacks, power banks, drawing tablets, and chairs.
Local Development
Prerequisites
- Docker and Docker Compose
Run Everything With Docker Compose
cp .env.example .env
docker compose up
Services:
- Web:
http://localhost:3000 - API base URL:
http://localhost:8080/api - Swagger UI:
http://localhost:8080/swagger-ui.html - OpenAPI JSON:
http://localhost:8080/api-docs - Postgres:
localhost:5432 - Default database/user/password:
cymelle/cymelle/cymelle
Seeded test credentials are available when SEED_DATA=true:
| Role | Username | Password |
|---|---|---|
| Admin | admin |
admin123 |
| Driver | driver |
driver123 |
| Customer | customer |
customer123 |
Useful commands:
docker compose logs -f api
docker compose logs -f web
docker compose restart api
docker compose down
docker compose down -v
The web service mounts orders-frontend/, so frontend edits reload through Vite. Restart the api service after backend code changes. Use docker compose down -v only when you want to delete local Postgres data and dependency cache volumes.
Environment Variables
Copy .env.example to .env, then adjust values as needed.
POSTGRES_DB=cymelle
POSTGRES_USER=cymelle
POSTGRES_PASSWORD=cymelle
POSTGRES_PORT=5432
API_PORT=8080
WEB_PORT=3000
VITE_API_BASE_URL=http://localhost:8080/api
APP_CORS_ALLOWED_ORIGINS=http://localhost:3000
SEED_DATA=true
LOW_STOCK_THRESHOLD=10
FARE_BASE_FARE=100.00
FARE_PER_KM_RATE=50.00
FARE_MINIMUM_FARE=150.00
FARE_DEFAULT_SURGE_MULTIPLIER=1.0
FARE_CURRENCY=KES
Run Services Manually
Docker Compose is recommended, but the services can run separately.
Backend:
cd orders-backend
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/cymelle \
SPRING_DATASOURCE_USERNAME=cymelle \
SPRING_DATASOURCE_PASSWORD=cymelle \
APP_CORS_ALLOWED_ORIGINS=http://localhost:3000 \
./gradlew bootRun
Frontend:
cd orders-frontend
corepack enable
pnpm install
VITE_API_BASE_URL=http://localhost:8080/api pnpm dev
Development Checks
Backend tests:
cd orders-backend
./gradlew test
Frontend checks:
cd orders-frontend
pnpm test
pnpm lint
pnpm check
Deployment
See DEPLOYMENT.md for Render deployment steps. The deployed setup uses:
cymelle-api: Spring Boot API Docker servicecymelle-web: TanStack/Nitro frontend Docker service- Neon PostgreSQL through
DATABASE_URL
Real-Time Location Design
See PART_2.md for the proposed backend architecture for real-time location updates across delivery and ride trips.