Files
cymelle/README.md
T
2026-06-15 11:38:04 +03:00

454 lines
12 KiB
Markdown

# 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.
```text
+-----------------------------+
| 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:
```text
+------------------------------------------------------------+
| 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
```text
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
```text
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
```text
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_STOCK` when available quantity is `0`
- `LOW_STOCK` when available quantity is at or below the reorder level
- `IN_STOCK` otherwise
### Checkout Quote
```text
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
```text
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
```text
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
```text
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
```text
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
```sh
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:
```sh
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.
```sh
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:
```sh
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:
```sh
cd orders-frontend
corepack enable
pnpm install
VITE_API_BASE_URL=http://localhost:8080/api pnpm dev
```
## Development Checks
Backend tests:
```sh
cd orders-backend
./gradlew test
```
Frontend checks:
```sh
cd orders-frontend
pnpm test
pnpm lint
pnpm check
```
## Deployment
See [DEPLOYMENT.md](./DEPLOYMENT.md) for Render deployment steps. The deployed setup uses:
- `cymelle-api`: Spring Boot API Docker service
- `cymelle-web`: TanStack/Nitro frontend Docker service
- Neon PostgreSQL through `DATABASE_URL`
## Real-Time Location Design
See [PART_2.md](./PART_2.md) for the proposed backend architecture for real-time location updates across delivery and ride trips.