This project implements a backend system for expense sharing, inspired by applications like Splitwise.
This README is intentionally detailed to clearly explain:
- How the problem was broken down
- Why specific design decisions were taken
- How correctness, scalability, and clarity were prioritized
Design and implement a backend system that allows users to:
- Create groups
- Add shared expenses
- Track balances
- Settle dues
- Equal split
- Exact amount split
- Percentage split
The system was divided into four core domains, each with a single responsibility:
| Domain | Responsibility |
|---|---|
| Users | Identity & ownership |
| Groups | Context for shared expenses |
| Expenses | Recording shared spending |
| Balances | Net money flow between users |
This separation ensures:
- Low coupling
- High readability
- Easier testing and debugging
- Why: Floating point (
double) leads to rounding errors - Trade-off: Slightly more verbose code
- Benefit: Financial correctness and precision
Instead of storing raw transactions, the system maintains net balances only.
Example:
A owes B ₹500
B owes A ₹300
→ Net: A owes B ₹200
This keeps balances:
- Minimal
- Easy to understand
- Efficient to query
All balance-related operations are handled in BalanceService:
- Expense balance updates
- Reverse balance cancellation
- Settlements
This avoids duplicated logic and ensures consistency.
API requests use DTOs instead of entities to:
- Prevent over-posting
- Keep APIs stable if database models change
- Improve clarity and validation
| Layer | Responsibility |
|---|---|
| Controller | Handles REST APIs |
| Service | Business logic |
| Repository | Database access |
| Model | Domain entities |
| DTO | API contracts |
| Category | Technology |
|---|---|
| Language | Java |
| Framework | Spring Boot |
| ORM | Hibernate / JPA |
| Database | MySQL |
| API Style | REST |
| Build Tool | Maven |
| Testing | Postman |
src/main/java/com/expensesplit
│
├── Controllers
│ ├── UserController.java
│ ├── GroupController.java
│ ├── ExpenseController.java
│ └── BalanceController.java
│
├── Services
│ ├── UserService.java
│ ├── GroupService.java
│ ├── ExpenseService.java
│ └── BalanceService.java
│
├── Repositories
│ ├── UserRepository.java
│ ├── GroupRepository.java
│ ├── ExpenseRepository.java
│ ├── ExpenseSplitRepository.java
│ └── BalanceRepository.java
│
├── DTO
│ ├── CreateExpenseRequest.java
│ ├── ExactSplitRequest.java
│ ├── PercentageSplitRequest.java
│ ├── SettleRequest.java
│ └── BalanceSummary.java
│
├── model
│ ├── User.java
│ ├── Group.java
│ ├── Expense.java
│ ├── ExpenseSplit.java
│ ├── Balance.java
│ └── SplitType.java
│
└── ExpenseApplication.java
CREATE DATABASE expensesplit;spring:
datasource:
url: jdbc:mysql://localhost:3306/expensesplit
username: root
password: YOUR_PASSWORD
jpa:
hibernate:
ddl-auto: update
show-sql: true
server starts at http://localhost:8080
POST http://localhost:8080/user
{
"name": "Neha",
"email": "neha@gmail.com"
}Output:
POST http://localhost:8080/groups
{
group name: "group_name",
userIds:
[
"USER_ID_1",
"USER_ID_2",
"USER_ID_3"
]
}
Output:
POST http://localhost:8080/expenses
{
"groupId": "GROUP_ID",
"paidBy": "USER_ID",
"amount": 3000,
"description": "Hotel"
}Output:
POST http://localhost:8080/expenses/exact
{
"groupId": "GROUP_ID",
"paidById": "USER_ID",
"description": "Taxi",
"splits": {
"USER_ID_1": 500,
"USER_ID_2": 300
}
}Output:
POST http://localhost:8080/expenses/percentage
{
"groupId": "GROUP_ID",
"paidById": "USER_ID",
"amount": 3000,
"description": "Resort",
"splits": {
"USER_ID_1": 50,
"USER_ID_2": 30,
"USER_ID_3": 20
}
}GET http://localhost:8080/balances/user/{userId}
Output:
GET http://localhost:8080/balances/summary/{userId}
Output:
GET http://localhost:8080/balances/group/{groupId}
POST http://localhost:8080/balances/settle
{
"fromUserId": "USER_ID",
"toUserId": "USER_ID",
"amount": 500
}Output:








