SplitNest is a Splitwise-like expense sharing backend system built using Go microservices, SQLite, RabbitMQ, and an NGINX API Gateway.
The project focuses on clean service boundaries, correct expense math, transaction safety, and event-driven architecture, rather than over-engineering.
- User registration & login (JWT-based authentication)
- Group creation and member management
- Expense creation with equal split logic
- Net balance calculation per group
- Asynchronous notifications using RabbitMQ
- Event-driven extensibility
- API Gateway using NGINX
- Independent database per service
- Dockerized setup
- User Service โ user registration, login, identity
- Group Service โ group creation & membership
- Expense Service โ expense tracking & balance calculation
- Notification Service โ consumes events asynchronously
- API Gateway โ routes requests to services (NGINX)
-
Synchronous (HTTP)
Used for core business logic (e.g., Expense โ Group) -
Asynchronous (RabbitMQ)
Used for side effects like notifications
- Language: Go
- Databases: SQLite (one per service)
- Messaging: RabbitMQ
- Auth: JWT
- Gateway: NGINX
- Containerization: Docker & Docker Compose
.
โโโ api-gateway
โ โโโ nginx.conf
โโโ docker-compose.yaml
โโโ user-service
โโโ group-service
โโโ expense-service
โโโ notification-service
โโโ Makefile
Each service follows a clean internal structure:
cmd/โ entry pointinternal/httpโ HTTP handlersinternal/storageโ database logicinternal/middlewareโ JWT validationinternal/typesโ request/response models
- JWT-based authentication
- Token passed via header:
Authorization: Bearer <JWT_TOKEN>
Each service validates JWT independently.
GET /
POST /register
{
"username": "aditya",
"email": "[email protected]",
"password": "secret123"
}POST /login
{
"email": "[email protected]",
"password": "secret123"
}Response:
{
"token": "<jwt-token>"
}GET /find-user/{username}
GET /
POST /create-group
Headers:
Authorization: Bearer <JWT>
{
"name": "Goa Trip"
}POST /add-members/{groupId}
{
"username": "rohan"
}GET /user-groups
Returns all groups where the user is a member or admin.
GET /group-members/{groupId}
Response:
[
{ "user_id": 1 },
{ "user_id": 2 },
{ "user_id": 4 }
]Used internally by Expense Service.
GET /
POST /add-expense/{groupId}
{
"amount": 1500
}Behavior:
- Equal split among group members
- Transaction-safe writes
- Publishes
expense.createdevent to RabbitMQ
GET /get-expense/{groupId}
For each expense:
net_balance = money_paid - money_owed
- Positive โ user gets money
- Negative โ user owes money
- Balances always sum to 0
Balances are incrementally updated, not recomputed.
- Name:
events - Type:
topic
{
"event": "expense.created",
"group_id": 3,
"expense_id": 2,
"paid_by": 4,
"amount": 900
}{
"event": "group.member_added",
"group_id": 3,
"user_id": 4
}Events are published after successful DB transaction commit.
- Docker
- Docker Compose
docker compose up --builddocker compose down- Analytics Service (event-driven)
- Settlement optimization (who pays whom)
- gRPC for internal calls
- Idempotency keys
- Observability & metrics