Problem statement
Design the object model and core APIs for an invoice generator a freelancer or small business uses to assemble line items, apply discounts and taxes, issue an immutable invoice, and track payment against it.
Operating context. Single tenant, in-process, single currency. An invoice carries an issuer, a customer, dates, and a list of line items (description, quantity, unit price). Discounts apply at line or invoice level (percentage or fixed); taxes apply per line as a rate (e.g. a GST or VAT band). An invoice is a draft that can be edited freely, then it is issued: it receives a unique sequential number, its contents freeze, and it becomes immutable. Payments (full or partial) are recorded against an issued invoice; a mistake is corrected only by issuing a credit note that reverses part or all of it.
Out of scope. PDF rendering and email delivery, a persistence layer, payment-gateway integration, multi-currency and FX, and posting entries into a general ledger.
What to produce. The class hierarchy (Invoice, LineItem, Money value object, discount and tax policies, the invoice-number sequence, CreditNote), the public APIs each exposes, the computation order for discount versus tax, and the invoice state machine (draft, issued, partially paid, paid, void). Be explicit about how issuance makes the invoice immutable and how the number sequence stays unique and gap-free under concurrent issuance.
Functional requirements
- Add, update, and remove line items on a draft invoice, each with a description, quantity, and unit price.
- Apply line-level and invoice-level discounts and per-line taxes, then compute subtotal, tax total, and grand total.
- Assign a unique sequential number and freeze the invoice's contents when it is issued.
- Record full or partial payments against an issued invoice and update its paid and outstanding amounts.
- Issue a credit note that reverses all or part of an already-issued invoice without mutating the original.
Non-functional requirements
- Total computation is deterministic and O(number of line items); recomputing the same invoice yields byte-identical totals.
- Once issued, an invoice is immutable — no method may alter its line items or totals; corrections happen only via a credit note.
- Invoice-number assignment is thread-safe and gap-free under concurrent issuance from multiple threads.
- Discount and tax rules are pluggable strategies — adding a new tax regime must not touch the totalling core.
- Monetary amounts use exact decimal or minor-unit arithmetic with an explicit, documented rounding policy.
- The model is unit-testable without I/O; the number sequence and clock are injected.
Topics
- System Design LLD
- Oop Solid
- Patterns Strategy
- Patterns State
- Finance Billing