githubEdit

5. Design Splitwise

Difficulty: Medium Topics: Strategy Pattern, Graph Simplification, Observer Pattern Key Concepts: Managing debts, different split types (Equal, Exact, Percent).

Phase 1: Requirements Gathering

Goals

  • Design an expense sharing application.

  • Identify users, groups, and expense types.

  • Define how debts are recorded and settled.

1. Who are the actors?

  • User: Adds expenses, views balances, settles debts.

  • Group: A collection of users sharing expenses.

  • System: Calculates splits and updates balances.

2. What are the must-have features? (Core)

  • Add Expense: User pays, others owe.

  • Split Types: Support Equal, Exact, and Percentage splits.

  • Balance Sheet: Show net balance per user.

  • Settle Up: Record payments to clear debts.

3. What are the constraints?

  • Validation: Percentages must add to 100%. Exact amounts must equal total.

  • Precision: Handle currency rounding (2 decimal places).


Phase 2: Use Cases

UC1: Add Expense

Actor: User Flow:

  1. User selects a group or friends.

  2. User enters total amount and selects split type (e.g., Equal).

  3. System validates the split.

  4. System calculates individual shares.

  5. System updates the Balance Sheet (Graph).

  6. System notifies involved users.

UC2: Settle Up

Actor: User Flow:

  1. User A pays User B X amount.

  2. System records a "Payment" transaction.

  3. System updates the debt graph (A owes B reduced by X).


Phase 3: Class Diagram

Step 1: Core Entities

  • SplitwiseService: Facade for operations.

  • ExpenseManager: Manages expenses and balances.

  • Expense: Stores details (amount, payer, splits).

  • Split: Abstract class for split logic.

  • User: Participant.

Step 2: Relationships

  • Expense has-many Split.

  • Split references User.

  • ExpenseManager has-many Expense.

UML Diagram

spinner

Phase 4: Design Patterns

1. Strategy Pattern

  • Description: Defines a family of algorithms, encapsulates each one, and makes them interchangeable.

  • Why used: Different split types (EqualSplit, ExactSplit, PercentSplit) require different validation and calculation logic. Strategy allows adding new split types easily.

2. Observer Pattern

  • Description: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

  • Why used: To notify group members when an expense is added, modified, or settled, ensuring all users have up-to-date balance information.


Phase 5: Code Key Methods

Java Implementation


Phase 6: Discussion

Debt Simplification

Q: How to simplify debts (minimize transactions)?

  • A: "Use a Min-Heap and Max-Heap. Calculate the net balance for each user (Credits - Debts).

    1. Push users with net > 0 to Max-Heap (Creditors).

    2. Push users with net < 0 to Min-Heap (Debtors).

    3. Pop max creditor ($C$) and max debtor ($D$).

    4. Settle amount min(|D|, |C|).

    5. Update remaining balance and push back to heaps if not zero.

    6. Repeat until heaps are empty."

Scalability

Q: How to handle millions of users in different groups?

  • A: "Use Consistent Hashing to shard the database. Since most expenses are within a Group, shard by GroupID and ensure all group data resides on the same DB shard to avoid expensive cross-shard transactions when adding expenses or settling up."

Concurrency

Q: Handling race conditions (two people editing same expense)?

  • A: "Use Optimistic Locking (versioning) on the Expense object. If version mismatch during save, prompt user to refresh. If high contention is expected, queue updates per group in Kafka and process them sequentially via a dedicated worker."

Financial Precision (SDE-3 Concept)

Q: How do you handle $100 split 3 ways ($33.33)? Who pays the extra $0.01?

  • A: "Use BigDecimal for all calculations to prevent floating-point errors. For rounding remainders: calculate exact shares (amount / participants). Distribute the integer pennies evenly. If there's a remainder (e.g., $100 / 3 = $33.33 with $0.01 left over), distribute the remaining pennies randomly or give them to the payer."


SOLID Principles Checklist

  • S (Single Responsibility): Expense stores data, ExpenseManager calculates logic, Split defines type.

  • O (Open/Closed): New Split types (e.g., specific share) can be added by extending Split.

  • L (Liskov Substitution): EqualSplit works wherever Split is needed.

  • I (Interface Segregation): Not heavily used, but interfaces are clean.

  • D (Dependency Inversion): ExpenseManager depends on Split abstraction.

Last updated