Problem statement
Design the object model and core APIs for a loyalty programme that accrues points on purchases and lets members redeem them, for a single retailer's rewards service.
Operating context. Each member has a points account. A completed order earns points via the active earn rules for the member's tier and the order's contents. Points are granted as dated lots with a validity horizon and expire when it passes. Members redeem points for a reward (a cart credit or a catalogue item), spending the oldest un-expired lot first. Tiers (e.g. silver, gold) move up or down from a member's rolling qualifying activity. Accrual and redemption for one member may occur concurrently, and the whole service is a single in-process component.
Out of scope. The payment step, placing the order itself, tax, notification/email delivery, and the admin UI for authoring earn rules and tiers (assume they exist in memory).
What to produce. The class hierarchy (Member, PointsAccount/ledger with point lots, EarnRule, Tier, RewardOption, LoyaltyService facade), the public API each exposes, and the state transitions for tiers and for lot expiry. Be explicit about: how a new earn rule is added without editing the accrual engine, how expiry is reflected in a balance query without a manual sweep, and how concurrent accrual and redemption keep the balance correct.
Functional requirements
- Accrue points for a completed order by running the active earn rules for the member's tier and the order's contents.
- Redeem points for a reward, deducting from the oldest un-expired lot first.
- Expire points once their lot passes its validity horizon, without the caller running a manual sweep.
- Recompute a member's tier from their rolling qualifying activity and apply the resulting up/downgrade.
- Report a member's redeemable balance, pending (not-yet-cleared) balance, and next expiry date.
Non-functional requirements
- Balance and next-expiry queries are O(L) in the member's open lots, not O(all transactions).
- Earn rules are pluggable strategies; adding a promotion does not modify the accrual engine.
- Concurrent accrual and redemption for one member never drive the balance negative or double-count a lot.
- Tier transitions form a total, testable state machine driven by an injected clock, not wall-clock reads.
- The ledger is append-only and reconstructable, so any balance is auditable from its entries.
- The model runs without a database, with the clock and rule set injected for tests.
Topics
- System Design LLD
- Commerce Loyalty
- Patterns Strategy
- Patterns State
- Oop Solid