Assemble the WHOLE tracker in Solution (you may reuse your
milestone code):
- record Expense(String id, String category, int cents) with
constructor validation (blank id/category, non-positive cents throw
IllegalArgumentException with a specific message)
- interface ExpenseStore { void save(Expense e); Optional<Expense> find(String id);
List<Expense> all(); } + InMemoryExpenseStore
- static class ExpenseService with constructor (ExpenseStore store):
- void add(String id, String category, int cents) — constructs
(letting validation throw), then saves
- Map<String, Integer> totalsByCategory() — stream aggregation
- Optional<String> topExpense() — highest cents, tie → smallest id
The checkpoint test drives the three layers together, exactly as the
milestones promised.
Difficulty: intermediate