Spilitwise

This I have written as part of my LLD Practice. Please let me know further imporvements that can be done.

Also current design is Strong/Average/Lean Hire

Other Design Problems

/*
Flow->
Domain ===> Reposoitory(DB) ==> Service (BuisLogic) ==> Controller (Client Code)

/* ======= Domain ======= */
SpilitType
User
Expense
Transaction
SpilitStrategy

/* ======= Repository (DB Logic) ======= */
UserRepository
GroupRepository
ExpenseRepository
TransactionRepository

/* ======= Service (Buisness Logic) ======= */

// User Group Service too for creating/updating user/group
ExpenseService
TransactionService

/* ======= Controller (Client Interaction) ======= */

SpilitwiseController (Client Interaction)

Client Code
*/
/*

Requirements

1. User management.
    - Track user balance
2. Group management.
    - Add/remove group member
    - Track group expenses
3. Expense management.
    - Add expense to group/individuals
    - Support different split types (EQUAL, EXACT, PERCENTAGE)
4. Balance Management
    - Tracks who owes whom
    - Handle settelements
5. Transaction Management
    - Record transaction
    - Track Payment Status
*/

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;


/* ======== Domain ======== */

enum SpilitType{
    EQUAL, EXACT, PERCENTAGE
}

class User{
    String userId;
    String userName;
    double balance;
}

class Group{
    String groupId;
    String groupName;
    List<String> participantIds;
}

class Expense{
    String id;
    Group group;
    String paidBy; //userId of payer
    double amount;
    SpilitType spilitType;
    Map<User,Double> spilitDetails;
}

class Transaction{
    String id;
    String fromUserId;
    String toUserId;
    double amount;
    boolean settled;
}

/*    Strategy+Factory    */

interface SpilitStrategy{
    Map<User,Double> spilit(Group group, User paidBy, double amount, Map<User,Double> input);
}

class EqualSpilitStrategy implements SpilitStrategy{
    @Override
    public Map<User,Double> spilit(Group group, User paidBy, double amount, Map<User,Double> input){

    }
}

class ExactSpilitStrategy implements SpilitStrategy{
    @Override
    public Map<User,Double> spilit(Group group, User paidBy, double amount, Map<User,Double> input){

    }
}

class PercentageSpilitStrategy implements SpilitStrategy{
    @Override
    public Map<User,Double> spilit(Group group, User paidBy, double amount, Map<User,Double> input){

    }
}

class SpilitStrategyFactory{
    static SpilitStrategy getSpilitStrategy(SpilitType spilitType){
        switch (spilitType){
            case EQUAL: return new EqualSpilitStrategy();
            case EXACT: return new ExactSpilitStrategy();
            case PERCENTAGE: return new PercentageSpilitStrategy();
            default: throw new RuntimeException("Invalid Spilit Strategy");
        }
    }
}


/* ======== Repository ======== */

class UserRepository{
    Map<String,User> userDb=new HashMap<>();

    void addUser(User user){
        userDb.put(user.userId, user);
    }

    User getUser(String userId){
        return userDb.get(userId);
    }
}

class GroupRepository{
    Map<String,Group> groupDb=new HashMap<>();

    // add and get fn for group
}

class ExpenseRepository{
    Map<String,Expense> expenseDb=new HashMap<>();

    void addExpense(Expense expense){
        expenseDb.put(expense.id, expense);
    }

    Expense getExpense(String expenseId){
        return expenseDb.get(expenseId);
    }
}

class TransactionRepository{
    Map<String,Transaction> transactionDb=new HashMap<>();

    void addTransaction(Transaction transaction){
        transactionDb.put(transaction.id, transaction);
    }

    Transaction getTransaction(String transactionId){
        return transactionDb.get(transactionId);
    }
}


/* ======== Service ======== */

class ExpenseService{
    ExpenseRepository expenseRepository;

    synchronized Expense addExpense(String id, Group group, User paidBy, double amount, SpilitType spilitType, Map<User,Double> input){
        SpilitStrategy spilitStrategy=SpilitStrategyFactory.getSpilitStrategy(spilitType);
        Map<User,Double> spilitDetails=spilitStrategy.spilit(group,paidBy,amount,input);

        Expense expense=new Expense();
        expenseRepository.addExpense(expense);
        
        //updating balance of each user after adding expsense
        for(Map.Entry<User,Double> entry:spilitDetails.entrySet()){
            User user=entry.getKey();
            double share=entry.getValue();
            if(!user.userId.equals(paidBy.userId)){
                user.balance-=share;
                paidBy.balance+=share;
            }
        }

        return expense;
    }
}

class TransactionService{
    TransactionRepository repository;

    synchronized Transaction addTransaction(String id, User from, User to, double amount){
        Transaction transaction=new Transaction();
        repository.addTransaction(transaction);
        from.balance+=amount;
        to.balance-=amount;
        return transaction;
    }
}

/* ======== Controller ======== */

class SpilitwiseController{
    ExpenseService expenseService;
    TransactionService transactionService;

    // also have create user, create group, add user fns also

    void addExpense(Group group, User paidBy, double amount, SpilitType spilitType, Map<User,Double> details){
        expenseService.addExpense(UUID.randomUUID().toString(),group,paidBy,amount,spilitType,details);
    }

    void recordTransaction(User from, User to, double amount){
        transactionService.addTransaction(UUID.randomUUID().toString(),from,to,amount);
    }
}

/* ======== Client Code ======== */

public class Spilitwise {
    public static void main(String[] args) {
        SpilitwiseController controller=new SpilitwiseController();
    }
}
Comments (0)