https://github.com/gautam84/foodike-backend
π A microservice-ready modular monolith backend for a food delivery app built with Kotlin & Ktor. Clean architecture with explicit extraction seams - event bus, query ports & per-module migrations - scale from one JAR to independent services without rewriting business logic. π
https://github.com/gautam84/foodike-backend
backend clean-architecture event-driven exposed food-delivery kotlin kotlin-coroutines ktor microservices modular-monolith open-source postgresql redis rest-api websockets
Last synced: 4 days ago
JSON representation
π A microservice-ready modular monolith backend for a food delivery app built with Kotlin & Ktor. Clean architecture with explicit extraction seams - event bus, query ports & per-module migrations - scale from one JAR to independent services without rewriting business logic. π
- Host: GitHub
- URL: https://github.com/gautam84/foodike-backend
- Owner: gautam84
- License: mit
- Created: 2026-04-08T18:39:56.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-06-13T11:51:55.000Z (about 1 month ago)
- Last Synced: 2026-06-13T13:21:06.798Z (about 1 month ago)
- Topics: backend, clean-architecture, event-driven, exposed, food-delivery, kotlin, kotlin-coroutines, ktor, microservices, modular-monolith, open-source, postgresql, redis, rest-api, websockets
- Language: Kotlin
- Homepage:
- Size: 168 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Foodike Backend
A microservice-ready modular monolith backend for a food delivery app, built with Kotlin and Ktor. It is being developed as a reference architecture that can scale from a single deploy to independent microservices without rewriting business logic.
## Why This Exists
Most food delivery backend tutorials are either too simple (single-file CRUD) or jump straight to microservices (overkill for 99% of projects). This project sits in the sweet spot: a production-grade modular monolith where every module is shaped like a future microservice, with explicit extraction seams documented and enforced by Gradle.
## Architecture
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Single JVM Process β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β shared/ β β
β β common Β· events Β· auth Β· persistence β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β user β βrestaurantβ β order β β
β β service β β service β β service β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β payment β β notific. β β tracking β β
β β service β β service β β service β β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β EventBus (SharedFlow) β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
The target architecture is for each service module to contain its own `domain/`, `infrastructure/`, `api/`, `di/`, and `migrations/`. The current repository has a lighter scaffold in place and is moving toward that structure incrementally.
## Current Status
Implemented today:
- Multi-module Gradle build
- Shared `common`, `events`, `auth`, and `persistence` modules
- Service module boundaries for user, restaurant, order, payment, notification, and tracking
- Ktor application shell in `app/`
- Placeholder routes and minimal domain models
- Basic integration test for app startup
Planned but not implemented yet:
- PostgreSQL/PostGIS setup
- Redis integration
- Flyway migrations
- Razorpay, Firebase, and S3 integrations
- Swagger/OpenAPI docs
- Micrometer/Prometheus metrics
- Full domain services, repositories, DTOs, and production flows
- Kotest, MockK, and Testcontainers-based test suite
## Tech Stack
| Layer | Tool |
|---|---|
| Language | Kotlin 2.x |
| Framework | Ktor 3.x (Netty) |
| Serialization | kotlinx.serialization |
| Database | H2 today, PostgreSQL/PostGIS planned |
| ORM | Exposed DSL |
| Migrations | Planned |
| Cache | Planned |
| DI | Koin 4 |
| Event Bus | In-process SharedFlow (swappable to RabbitMQ) |
| Auth | JWT (java-jwt) |
| Payments | Planned |
| Push Notifications | Planned |
| Storage | Planned |
| Observability | Logback today, broader observability planned |
| Testing | Ktor `testApplication` today, broader test stack planned |
## Getting Started
### Prerequisites
- JDK 17+
- No external infrastructure is required for the current scaffold
### Run Locally
```bash
# Clone the repo
git clone https://github.com/gautam84/foodike-backend.git
cd foodike-backend
# Run the application
./gradlew :app:run
```
The server starts at `http://localhost:8080`.
### Environment Variables
The current scaffold only uses application config from `app/src/main/resources/application.yaml`.
Planned environment variables for future infrastructure:
| Variable | Description | Required |
|---|---|---|
| `DB_URL` | Postgres JDBC URL | Planned |
| `DB_USER` | Postgres username | Planned |
| `DB_PASSWORD` | Postgres password | Planned |
| `REDIS_URL` | Redis connection URL | Planned |
| `JWT_SECRET` | Secret for signing JWTs | Planned |
| `JWT_ISSUER` | JWT issuer claim | Planned |
| `RAZORPAY_KEY` | Razorpay API key | For payments |
| `RAZORPAY_SECRET` | Razorpay API secret | For payments |
| `FIREBASE_CREDENTIALS` | Path to Firebase service account JSON | For notifications |
| `S3_BUCKET` | S3 bucket name for image uploads | For storage |
| `S3_REGION` | AWS region | For storage |
### Run Tests
```bash
# Unit tests
./gradlew :tests:test --tests "com.example.foodike.unit.*"
# All tests
./gradlew :tests:test
```
## Project Structure
```
foodike-backend/
βββ shared/
β βββ common/ # Value objects, exceptions, utilities
β βββ events/ # Event contracts + EventBus interface
β βββ auth/ # JWT validation, shared across all modules
β βββ persistence/ # Database factory, query helpers
β
βββ services/
β βββ user-service/ # Auth (OTP), profiles, addresses
β βββ restaurant-service/ # Restaurants, menus, search
β βββ order-service/ # Cart, orders, coupons
β βββ payment-service/ # Razorpay integration
β βββ notification-service/ # FCM, SMS, email
β βββ tracking-service/ # WebSocket order tracking
β
βββ app/ # Ktor entry point, plugin config, Koin wiring
βββ tests/ # Unit + integration tests
```
Target per-service structure:
```
services/xyz-service/
βββ domain/
β βββ model/ # Entities and value objects
β βββ repository/ # Interfaces (ports)
β βββ service/ # Business logic
β βββ port/ # Cross-service query interfaces (if needed)
βββ infrastructure/
β βββ persistence/
β β βββ tables/ # Exposed table definitions
β β βββ *Impl.kt # Repository implementations
β βββ adapter/ # In-process adapters for query ports
β βββ events/ # Event publishers and consumers
βββ api/
β βββ routes/ # Ktor route definitions
β βββ dto/ # Request/response objects
β βββ mapper/ # Domain β DTO mappers
βββ di/
β βββ XyzModule.kt # Koin module
βββ migrations/
βββ db/migration/ # Flyway SQL files
```
## Module Dependency Rules
```
shared/common β no dependencies
shared/events β common
shared/auth β common
shared/persistence β common
services/* β shared modules only (NEVER other services)
app β all services (composition root)
```
**The critical rule:** service modules should never depend on each other. The project is structured around that rule, and module dependencies should continue to preserve it. If `order-service` needs data from `restaurant-service`, it should go through a query port interface rather than a direct import.
## Three Extraction Seams
This architecture is intended so that extracting any module into an independent microservice can happen with minimal business-logic changes. These seams are part of the target design and are only partially implemented today:
### 1. Event Bus
Services communicate asynchronously through `EventBus`. Today the repo includes an in-process event bus. A broker-backed event bus is planned for extraction scenarios.
```kotlin
// Today (monolith)
single { InProcessEventBus() }
// Planned after extraction
single { RabbitMqEventBus(connectionFactory) }
```
### 2. Query Ports
When a service needs synchronous data from another service, it should call a port interface. The concrete in-process and HTTP adapters shown below are target examples and are not fully implemented yet.
```kotlin
// Target monolith form
single { InProcessMenuAdapter(get()) }
// Planned extraction form
single { HttpMenuClient(httpClient, env("RESTAURANT_SERVICE_URL")) }
```
### 3. Per-Module Migrations
Each service is intended to own its own migrations. Flyway integration is planned and not wired into the current scaffold yet.
### Extraction Checklist
To extract any service into an independent microservice:
1. Copy `services/xyz-service/` into its own Gradle project
2. Add its own `Application.kt` and `Dockerfile`
3. Swap `InProcessEventBus` β `RabbitMqEventBus`
4. Swap `InProcess*Adapter` β `Http*Client` for query ports
5. Point Flyway at a dedicated database
6. Add to API gateway route config
7. Remove from monolith's `settings.gradle.kts` and `AppModule.kt`
## API Overview
### Auth
- `POST /auth/send-otp` β send OTP to phone
- `POST /auth/verify-otp` β verify and get JWT pair
- `POST /auth/refresh` β refresh access token
### Users
- `GET /users/me` β current profile
- `PUT /users/me` β update profile
- `GET /users/me/addresses` β list addresses
- `POST /users/me/addresses` β add address
- `DELETE /users/me/addresses/{id}` β remove address
### Restaurants
- `GET /restaurants` β list (paginated, filterable)
- `GET /restaurants/nearby?lat=&lng=&radius=` β geo query
- `GET /restaurants/{id}` β detail
- `GET /restaurants/{id}/menu` β grouped menu
### Cart
- `GET /cart` β current cart
- `POST /cart/items` β add item
- `PUT /cart/items/{id}` β update quantity
- `DELETE /cart/items/{id}` β remove item
- `DELETE /cart` β clear
- `POST /cart/coupon` β apply coupon
### Orders
- `POST /orders` β place order
- `GET /orders` β list (paginated)
- `GET /orders/{id}` β detail
- `PATCH /orders/{id}/status` β update status
- `WS /track/{orderId}` β live tracking
### Payments
- `POST /payments/initiate` β create Razorpay order
- `POST /payments/verify` β verify payment signature
### Search
- `GET /search?q=&type=` β full-text search
The endpoint list above reflects the target API surface. The current implementation only exposes a small subset of placeholder routes while the modules are being built out.
## License
This project is licensed under the MIT License. See [LICENSE.md](LICENSE.md) for details.
## Contributing
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a PR.