2dbi

Design a Loyalty Points System

viaLeetCode

Problem Low-level design of a loyalty points system: users earn points from qualifying events and redeem (exhaust) them, with balances kept consistent and points that can expire.

Requirements

  • earn(userId, amount, source, expiryPolicy) on qualifying actions (bookings, promos); redeem(userId, amount) failing on insufficient balance; getBalance(userId); expiry of unspent points; full auditable history.

Core design

  • LEDGER over mutable balance: PointTransaction(id, userId, type EARN/REDEEM/EXPIRE/ADJUST, amount, earnedAt, expiresAt, remaining) — append-only; balance = Σ active earn.remaining. Cached Balance object maintained transactionally for O(1) reads (and reconciled from the ledger).
  • Redemption consumes earn lots FIFO-by-expiry (spend soonest-expiring first — the customer-friendly and standard policy): walk open lots decrementing remaining — this is why lots carry remaining, not just amount.
  • Expiry: scheduled job (or lazy-on-read) emitting EXPIRE transactions for lots past expiresAt with remaining > 0.
  • Classes: User, PointsAccount, PointTransaction/EarnLot, RedemptionService + EarningService behind a PointsService facade; EarningRule strategy (per source/promo multipliers).

Discussion points

  • Why append-only + derived balance beats update-in-place: audit, concurrent-correction safety, replayability; idempotency keys per event so retries don't double-credit.
  • Concurrency on redeem: two simultaneous redemptions — transactional check-and-decrement (row lock on account or optimistic version).
  • Extensions: tiers, points holds (pending bookings), partial refunds re-crediting expired lots — where each fits the model.
Add a follow-up question they asked
No follow-ups yet. Be the first to add one.
asked …
LeaderboardSalary
Language
Account