OOD & LLD Reference/API & Component Design

Errors, Invariants & Validation

Fail-fast vs Result types, enforcing invariants in constructors, null-object pattern, and where validation lives (entity vs service) in LLD designs.

3/5Overview: 25m

Invariants and where to enforce them

An invariant is a condition that must always hold for an object to be valid: balance >= 0, loan.dueDate > loan.startDate. Corrupt state is worse than a thrown exception — design so invalid objects cannot exist.

LayerValidatesExample
ConstructorObject creation rulesMoney rejects negative amount
Entity methodState transition ruleswithdraw() checks sufficient funds
ServiceCross-entity rulesborrow limit across all active loans
API boundaryInput formatnull checks, range validation

Fail-fast vs Result types

Exceptions for truly exceptional paths (out of stock, network down). Result/Either (Optional, Result<T,E>) for expected failures callers should handle (insufficient funds, duplicate reservation). Avoid boolean soup: if (!withdraw()) tells the caller nothing — prefer InsufficientFundsException or Result.failure(REASON).

Null-object and defensive design

NullNotificationChannel that no-ops instead of null checks everywhere — use sparingly, only when "do nothing" is a valid policy. Prefer Optional for absent values. Don't return null from public APIs without documenting contract.

Senior-level signal

For BankAccount.withdraw(amount): "I enforce amount > 0 in the method, check balance here (entity invariant), and let the service coordinate distributed lock if accounts span shards — out of scope today." State which exceptions are checked vs unchecked and why. Mention concurrent withdraw: optimistic versioning or synchronized block — pick one.

Aggregate consistency

In DDD terms, one aggregate (e.g., BankAccount) owns its invariants; cross-aggregate rules belong in an application service. In LLD, draw the boundary: don't let Loan directly mutate BookItem.status — route through LibraryService.

Where this goes next

You've completed the LLD reference arc — revisit SOLID Principles when reviewing designs, and practice full timed rounds on Parking Lot or LRU Cache to integrate process, patterns, and API discipline.

Further Reading

Practice Tasks (Optional)

Design or implement locally in any language — no autograding. Focus on class structure, extensibility, and being able to explain trade-offs out loud.

  • Design BankAccount with invariants

    withdraw(amount) must not allow negative balance. Where do you enforce: constructor, withdraw(), or separate validator? Handle concurrent withdraw attempts conceptually. Write method signatures and thrown exceptions.

    25m