Loading content...
You've extracted nouns, distinguished entities from attributes, and prioritized by importance. Now you have a list of candidate entities. But how do you know it's correct?
An incomplete or incorrect entity list leads to designs that:
Validation is not optional:
Professional engineers don't proceed with a design without validation. They apply systematic checks to verify their entity list before investing in detailed design. This investment of 15-30 minutes upfront saves hours of rework later.
By the end of this page, you will master five validation techniques for entity lists: use case walkthroughs, CRUD analysis, relationship validation, domain expert review, and the completeness checklist. You'll know how to systematically verify that your entities are correct, complete, and properly connected.
Entity list validation employs multiple complementary techniques. Each catches different types of problems:
| Technique | What It Validates | Problems It Catches |
|---|---|---|
| Use Case Walkthrough | Entities can support all required behaviors | Missing entities, missing operations |
| CRUD Analysis | Each entity has valid lifecycle | Orphan entities, missing operations |
| Relationship Validation | Entities properly connect | Missing relationships, wrong cardinalities |
| Domain Expert Review | Entities reflect real-world concepts | Misunderstandings, terminology issues |
| Completeness Checklist | Nothing obvious is missing | Overlooked common entities |
Apply all five techniques for thorough validation. In practice, they overlap and reinforce each other.
Start with Use Case Walkthrough—it's the most likely to reveal missing entities. Then apply CRUD Analysis to catch entities without a complete lifecycle. Relationship Validation comes next, followed by the Domain Expert Review. Finish with the Completeness Checklist as a final sweep.
The most powerful validation technique: walk through each use case step-by-step, checking that your entities can support every action.
How to perform a use case walkthrough:
What to look for:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
# Use Case Walkthrough: Library Management System ## Current Entity List- Book, BookCopy, Member, Loan, Fine, LibraryCard ## Use Case: Member Borrows a Book | Step | Description | Entity | Operation | ✓/✗ ||------|-------------|--------|-----------|-----|| 1 | Member presents library card | LibraryCard | validate() | ✓ || 2 | Librarian scans card | LibraryCard | getMemeberId() | ✓ || 3 | System checks member status | Member | isActive() | ✓ || 4 | Member selects a book | Book | - | ✓ || 5 | System finds available copy | BookCopy | findAvailable() | ✓ || 6 | System checks loan limit | Member | canBorrow() | ✓ || 7 | System creates loan | Loan | create() | ✓ || 8 | Copy status updated | BookCopy | markBorrowed() | ✓ || 9 | Due date calculated | Loan | calculateDueDate() | ✓ || 10 | Receipt generated | ??? | ??? | ✗ MISSING | ## Finding: Step 10 - Receipt GenerationNo entity handles receipts. Options:A) Add Receipt entity (if receipts need to be stored/reprinted)B) Add receipt() method to Loan (if it's just a generated view) Likely decision: Add to Loan.generateReceipt() - receipts don't need independent storage. ## Use Case: Member Returns an Overdue Book | Step | Description | Entity | Operation | ✓/✗ ||------|-------------|--------|-----------|-----|| 1 | Member returns book | BookCopy | - | ✓ || 2 | System identifies loan | Loan | findByBookCopy() | ✓ || 3 | System calculates overdue days | Loan | getOverdueDays() | ✓ || 4 | System calculates fine | Fine | calculate() | ✓ || 5 | Fine attached to member | Fine | - | ✓ || 6 | Member pays fine | Fine | pay() | ✓ || 7 | System accepts payment | ??? | ??? | ✗ MISSING || 8 | Loan closed | Loan | close() | ✓ || 9 | Copy made available | BookCopy | markAvailable() | ✓ | ## Finding: Step 7 - Payment HandlingFine.pay() exists but where does payment go?Options:A) Add Payment entity (if payment records needed)B) Add payment processing to Fine directly Decision: Add Payment entity - we need transaction records for accounting. ## Use Case: Member Reserves an Unavailable Book | Step | Description | Entity | Operation | ✓/✗ ||------|-------------|--------|-----------|-----|| 1 | Member searches for book | Book | search() | ✓ || 2 | All copies unavailable | BookCopy | none available | ✓ || 3 | Member requests reservation | ??? | ??? | ✗ MISSING || 4 | System queues request | ??? | ??? | ✗ MISSING || 5 | When copy returns, notify | ??? | ??? | ✗ MISSING | ## Finding: Entire reservation flow missingWe need:- Reservation entity (request queue)- Notification entity (or mechanism) This was in Tier 3 but the use case requires it. Re-prioritize to Tier 2?Walk through error cases and edge cases too. What happens when a card is expired? When inventory is wrong? When payment fails? These scenarios often reveal missing entities or operations.
Every entity should participate in Create, Read, Update, or Delete operations. CRUD analysis asks: For each entity, who creates it, reads it, updates it, and deletes it?
The CRUD Matrix:
Create a matrix with entities as rows and CRUD operations as columns. Fill in who/what performs each operation. If a cell is empty, investigate:
| Entity | Create | Read | Update | Delete |
|---|---|---|---|---|
| ParkingLot | System init/Admin | All | Admin (hours, rates) | Never (business critical) |
| Floor | Admin | System/UI | Admin (config) | Admin (rare) |
| ParkingSpot | Admin | System | System (status) | Admin (spot removal) |
| Vehicle | Entry gate | Exit gate | Never (immutable record) | Cleanup job |
| Ticket | Entry gate | Exit gate, Payment | Payment (exit time) | Archive job |
| Payment | Exit gate | Reports | Never (immutable) | Never (financial record) |
| DisplayBoard | Admin setup | Visitors | System (real-time) | Admin |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
// CRUD VALIDATION: Identifying Entity Lifecycle Issues /** * PROBLEM 1: Entity with no Create * * Entity: RateCard (defines pricing rules) * * CRUD Analysis: * - Create: ??? (who sets up rates?) * - Read: System (calculates fees) * - Update: ??? (how do rates change?) * - Delete: ??? * * Issue: No create/update operations defined * Resolution: Add Admin-created rate management */ class RateCard { id: string; vehicleType: VehicleType; hourlyRate: Money; dailyMaxRate: Money; effectiveFrom: Date; // Rates can change over time // Need Create: Admin.createRateCard() // Need Update: Admin.updateRateCard() or create new dated version} /** * PROBLEM 2: Entity with no Read * * Entity: AuditLog (captures all system actions) * * CRUD Analysis: * - Create: System (automatic) * - Read: ??? (who looks at logs?) * - Update: Never (immutable for audit purposes) * - Delete: Retention policy * * Issue: Logs are created but never read * Resolution: Either add Admin.viewAuditLogs() or question if entity is needed */ // If truly never read, reconsider if it should be an entity// Perhaps it's infrastructure logging, not domain entity /** * PROBLEM 3: Entity that's "Always Mutable" - Missing State * * Entity: Booking * * CRUD Analysis: * - Create: Customer creates booking * - Read: Customer, Admin * - Update: Customer can modify, Admin can modify * - Delete: Customer can cancel (but is that delete?) * * Issue: "Delete" is actually "Cancel" - a state change * Resolution: Bookings should have status, not be deleted */ class Booking { id: string; status: BookingStatus; // CONFIRMED, MODIFIED, CANCELLED, COMPLETED // "Delete" is actually cancel() which changes status cancel(reason: string): void { if (this.status === BookingStatus.COMPLETED) { throw new Error("Cannot cancel completed booking"); } this.status = BookingStatus.CANCELLED; this.cancellationReason = reason; }} /** * PROBLEM 4: Orphan Entity - No Relationships * * Entity: VehicleType * * CRUD Analysis shows it's used but... * - Created by: ??? * - Used by: ParkingSpot, RateCard * - But how does VehicleType relate to Vehicle? * * Issue: VehicleType is referenced but not linked properly * Resolution: Add Vehicle.type: VehicleType relationship */ class Vehicle { licensePlate: string; type: VehicleType; // Needed for spot matching and pricing} enum VehicleType { MOTORCYCLE = "MOTORCYCLE", CAR = "CAR", TRUCK = "TRUCK"} // Decision: VehicleType as enum, not entity// It doesn't need CRUD - it's a fixed set of valuesEntities don't exist in isolation—they're connected by relationships. Relationship validation checks that:
How to validate relationships:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
// RELATIONSHIP VALIDATION: Hotel Booking System /** * Entity List: Hotel, Room, RoomType, Guest, Booking, Payment * * Relationship Analysis: */ // Hotel → Room: One-to-Many// A hotel has many rooms; each room belongs to one hotelclass Hotel { id: string; name: string; rooms: Room[]; // ✓ Can navigate to rooms} class Room { id: string; hotelId: string; // ✓ Can navigate back to hotel roomNumber: string; typeId: string;} // Room → RoomType: Many-to-One// Many rooms share a room type; each room has one typeclass RoomType { id: string; name: string; // "Deluxe", "Standard", "Suite" basePrice: Money; capacity: number; // Does RoomType need to know its rooms? Probably not.} // Guest → Booking: One-to-Many// A guest can have multiple bookings; each booking has one guestclass Guest { id: string; name: string; email: string; // bookingIds?: string[]; // Optional: might not need reverse navigation} class Booking { id: string; guestId: string; // ✓ References guest roomId: string; // ✓ References room checkInDate: Date; checkOutDate: Date; status: BookingStatus;} // Booking → Room: Many-to-One (typically) or Many-to-Many?// Question: Can one booking cover multiple rooms? // VALIDATION FINDING:// Requirement: "Guests can book multiple rooms in a single booking"// Current design: Booking → single Room// Issue: Cardinality is wrong! // CORRECTION:class BookingCorrected { id: string; guestId: string; roomBookings: RoomBooking[]; // One-to-Many with new entity checkInDate: Date; checkOutDate: Date;} // NEW ENTITY DISCOVERED: RoomBooking// Junction entity for booking-room relationshipclass RoomBooking { bookingId: string; roomId: string; rateApplied: Money; specialRequests: string[];} // Payment → Booking: One-to-Many or One-to-One?// Question: Can a booking have multiple payments (deposit, final)? class Payment { id: string; bookingId: string; // ✓ References booking amount: Money; type: PaymentType; // DEPOSIT, BALANCE, REFUND paidAt: Date;}// One booking → many payments is correct for hotel scenarios /** * ORPHAN CHECK: * * Draw connections from each entity: * * Hotel ←→ Room ←→ RoomType (RoomType has no reference back - OK) * Guest ←→ Booking ←→ RoomBooking ←→ Room * Booking ←→ Payment * * No orphans! Every entity connects to the core graph. */ /** * NAVIGABILITY ANALYSIS: * * From guest, can we: * - Find their bookings? ✓ Query Booking by guestId * - Find which rooms they booked? ✓ Through Booking → RoomBooking → Room * - Find their payments? ✓ Through Booking → Payment * * From room, can we: * - Find which bookings? ✓ Query RoomBooking by roomId * - Find the hotel? ✓ Room.hotelId * - Check if room is available? Need logic, but data is there ✓ */| Mistake | Symptom | Fix |
|---|---|---|
| Wrong cardinality | Logic breaks when finding 'the' X but there are multiple | Review requirements, use collection or junction entity |
| Missing junction entity | Many-to-many with no link entity loses relationship data | Add junction entity to capture relationship attributes |
| Bidirectional when unneeded | Entity references back just because you can | Only add bidirectional if queries require it |
| Orphan entity | Entity has no relationships to others | Either connect it or question if it's an entity |
| Circular mandatory dependency | A requires B, B requires A, neither can be created first | Make one relationship optional or use lazy initialization |
Technical validation catches structural problems, but only domain knowledge catches semantic problems. The Domain Expert Review simulates (or actually conducts) a review with someone who understands the business domain.
Key questions for domain review:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
# Domain Expert Review: Ride-Sharing Application ## Entities Presented:Rider, Driver, Ride, Vehicle, Payment, Location, Rating ## Review Session Notes: ### Terminology Issues Found: DEVELOPER TERM → DOMAIN TERM- "Ride" → Industry uses "Trip" (Uber, Lyft terminology)- "Location" → Could be "Address" or "Coordinate" depending on use- Decision: Rename Ride → Trip for consistency with industry ### Missing Concepts Identified: DOMAIN EXPERT: "What about surge pricing?"DEVELOPER: "We have flat pricing in Payment."DOMAIN EXPERT: "Pricing varies by time, demand, and distance. It's complex."FINDING: Need PricingRule or DynamicPricing entity DOMAIN EXPERT: "What about ride types? Pool, X, XL, Black?"DEVELOPER: "Vehicle has a 'type' attribute."DOMAIN EXPERT: "But the ride type determines matching rules, pricing, features."FINDING: Need RideType/ServiceLevel entity ### Lifecycle Clarifications: DOMAIN EXPERT: "When a driver cancels, what happens?"DEVELOPER: "Trip status becomes CANCELLED?"DOMAIN EXPERT: "But we need to track WHO cancelled. Driver cancels = penalty. Rider cancels = maybe fee. System cancels = refund."FINDING: Cancellation needs CancelledBy and CancellationReason ### Relationship Corrections: DOMAIN EXPERT: "Can a rider have multiple active trips?"DEVELOPER: "We assumed one trip at a time."DOMAIN EXPERT: "Yes for riding, but they might schedule future trips."FINDING: Rider → Trip is 1:Many, but only one with status ACTIVE ### Final Recommended Changes:1. Rename Ride → Trip2. Add ServiceLevel entity (X, Pool, XL, Black, etc.)3. Add DynamicPricing or PricingEngine component4. Enhance Trip cancellation with actor and reason5. Add ScheduledTrip concept (future booking)In interviews or personal projects, you ARE the domain expert proxy. Research the domain: read Wikipedia articles, study competitor products, look at industry standards. Then role-play: 'If I were running a parking lot, would I recognize 'DisplayBoard' as a real thing?' The goal is to think like the business, not just the code.
The final validation technique is a completeness checklist — a list of commonly needed entity types that are easy to overlook. Review this list and ask: 'Does our system need one of these?'
Common Entity Categories:
| Category | Entity Types | Questions to Ask |
|---|---|---|
| Core Domain | The main 'thing' being managed | What's the central concept? Product, Order, Booking? |
| Actors | User, Customer, Admin, Agent | Who interacts with the system? What roles exist? |
| Transactions | Order, Payment, Transfer, Booking | What exchanges or commitments are recorded? |
| Line Items | OrderLine, CartItem, InvoiceLine | Do transactions have multiple parts? |
| Inventory/Resources | Stock, Seat, Room, Slot | What's being allocated, reserved, or consumed? |
| Schedules | Appointment, Shift, Event | Is time a critical dimension? |
| Rules/Policies | PricingRule, Discount, Policy | What business rules govern behavior? |
| Configuration | Settings, Preferences, Options | What's configurable per tenant/user? |
| History/Audit | Log, History, Change | Is tracking changes important? |
| Communication | Message, Notification, Alert | How does the system communicate? |
| Location/Address | Address, Location, Region | Is geography relevant? |
| Categorization | Category, Tag, Group | How are things organized? |
| Relationships | Membership, Subscription, Follow | Are there user-to-user relationships? |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
// COMPLETENESS CHECK: Food Delivery System // Current entity list:// Customer, Restaurant, Menu, MenuItem, Order, Driver // Running through checklist: // ✓ Core Domain: Restaurant, MenuItem, Order - covered// ✓ Actors: Customer, Driver, (Restaurant as actor?) - covered// ✓ Transactions: Order - covered// ✗ Line Items: OrderItem - MISSING! // Finding: How do we know what items are in an order?class OrderItem { orderId: string; menuItemId: string; quantity: number; specialInstructions: string; priceAtOrder: Money; // Snapshot price} // ✗ Inventory: Not needed for food delivery// ✗ Schedules: Restaurant hours? Maybe needed. class RestaurantHours { restaurantId: string; dayOfWeek: DayOfWeek; openTime: Time; closeTime: Time;} // ✓ Rules: Pricing in MenuItem - basic// ✗ But what about: Delivery fees? Minimum order? Promotions? class DeliveryZone { restaurantId: string; areaPolygon: Polygon; deliveryFee: Money; minimumOrder: Money; estimatedMinutes: number;} class Promotion { code: string; discountType: DiscountType; // PERCENT, FIXED discountValue: number; validFrom: Date; validTo: Date; conditions: PromotionCondition[];} // ✓ Configuration: User preferences? Yes.class CustomerPreferences { customerId: string; defaultAddressId: string; dietaryRestrictions: string[]; pushNotificationsEnabled: boolean;} // ✗ History: Order history via Order entity - covered// ✓ Communication: Notification needed! class Notification { id: string; recipientId: string; recipientType: 'CUSTOMER' | 'DRIVER' | 'RESTAURANT'; type: NotificationType; title: string; body: string; sentAt: Date; readAt: Date | null;} // ✓ Location: Address needed for delivery class Address { id: string; street: string; city: string; zipCode: string; instructions: string; // "Leave at door" coordinates: Coordinates;} // ✓ Categorization: Restaurant categories, cuisine types class Category { id: string; name: string; // "Italian", "Fast Food", "Vegetarian"} // ✗ Relationships: Following restaurants? Favorites? class FavoriteRestaurant { customerId: string; restaurantId: string; addedAt: Date;} // SUMMARY: Completeness check revealed 7 missing entities!// OrderItem, RestaurantHours, DeliveryZone, Promotion,// CustomerPreferences, Notification, Address, FavoriteRestaurant // Not all are Tier 1, but they're now on the radar.Let's apply all five techniques to a real example: Movie Ticket Booking System.
Initial Entity List: Movie, Theatre, Screen, Show, Seat, Booking, Customer, Payment
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
# Movie Ticket Booking: Complete Entity Validation ## TECHNIQUE 1: USE CASE WALKTHROUGH ### Use Case: Customer Books Tickets | Step | Action | Entity | Check ||------|--------|--------|-------|| 1 | Browse movies | Movie | ✓ || 2 | Select show time | Show | ✓ || 3 | View seat map | Screen, Seat | ✓ || 4 | Select seats | Seat | Need status tracking || 5 | Enter customer info | Customer | ✓ || 6 | Process payment | Payment | ✓ || 7 | Generate tickets | ??? | ❌ MISSING: Ticket || 8 | Send confirmation | ??? | ❌ MISSING: Notification | FINDING: Need Ticket entity (different from Booking)FINDING: Need Notification or Email entity ### Use Case: Customer Cancels Booking | Step | Action | Entity | Check ||------|--------|--------|-------|| 1 | Find booking | Booking | ✓ || 2 | Check cancellation policy | ??? | ❌ MISSING: Policy || 3 | Calculate refund | Payment | Need refund logic || 4 | Process refund | Payment | Add Refund or Payment.type || 5 | Release seats | Seat | ✓ || 6 | Notify customer | Notification | Already flagged | FINDING: Need CancellationPolicy or generic Policy entity --- ## TECHNIQUE 2: CRUD ANALYSIS | Entity | Create | Read | Update | Delete ||--------|--------|------|--------|--------|| Movie | Admin | Customer, System | Admin | Admin || Theatre | Admin | Customer | Admin | Admin || Screen | Admin | System | Admin | Admin (rare) || Show | Admin/System | Customer | System (seats) | (never, has bookings) || Seat | Admin | Customer, Booking | System (status) | ❌ || Booking | Customer | Customer, Staff | System (cancel) | ❌ || Customer | Self-register | System | Customer | GDPR? || Payment | System | Customer, Finance | ❌ | ❌ || Ticket | System | Customer | ❌ | ❌ | FINDING: Seat never deleted - correct, seats are fixedFINDING: Payment never updated - correct, immutable recordFINDING: Customer delete - need soft delete for GDPR compliance --- ## TECHNIQUE 3: RELATIONSHIP VALIDATION Movie (1) ←→ (M) Show: ✓ Movie has many showsTheatre (1) ←→ (M) Screen: ✓ Theatre has many screensScreen (1) ←→ (M) Seat: ✓ Screen has many seatsScreen (1) ←→ (M) Show: ✓ Screen has many showsShow (1) ←→ (M) Booking: ✓ Show has many bookings Booking (1) ←→ (?) Seat: How many seats per booking?→ One booking = multiple seats (family booking)→ Need: Booking (1) ←→ (M) Seat or BookingSeat junction Customer (1) ←→ (M) Booking: ✓ Customer has many bookingsBooking (1) ←→ (1) Payment: ✓ One payment per booking FINDING: Need BookingSeat junction entity for seat-booking relationshipReason: Need to track which seats are in which booking class BookingSeat { bookingId: string; seatId: string; showId: string; // For quick availability check ticketId: string; // Links to generated ticket} --- ## TECHNIQUE 4: DOMAIN EXPERT REVIEW Terminology:- "Show" → Industry uses "Showtime" or "Performance" ✓ OK to use Show- "Booking" → Also called "Reservation" in some contexts ✓ OK Missing Concepts:- "What about seat types?" → Add seatType: REGULAR, PREMIUM, VIP ✓- "Food and beverage?" → Out of scope for MVP, note for future- "Loyalty points?" → Out of scope, note for future- "Group bookings?" → Handle via multiple BookingSeats ✓ Lifecycle Issues:- "Show can sell out?" → Add Show.availableSeats counter- "Double booking prevention?" → Concurrency concern, handle in logic --- ## TECHNIQUE 5: COMPLETENESS CHECKLIST | Category | Needed? | Entity ||----------|---------|--------|| Core Domain | ✓ | Movie, Show, Seat, Booking || Actors | ✓ | Customer (missing: Admin?) || Transactions | ✓ | Booking, Payment || Line Items | ✓ | BookingSeat || Inventory | ✓ | Seat (availability) || Schedules | ✓ | Show (has time) || Rules | ⚠️ | Need: PricingRule, CancellationPolicy || Configuration | Optional | TheatreSettings || History | Optional | BookingHistory || Communication | ✓ | Notification || Location | ✓ | Theatre.address || Categorization | ✓ | Movie.genre | --- ## FINAL VALIDATED ENTITY LIST ### Tier 1 (Core):- Movie (what's showing)- Theatre (where)- Screen (which auditorium)- Show (specific performance)- Seat (what's being booked)- Booking (the transaction)- Customer (who's booking)- Payment (the money)- Ticket (the deliverable)- BookingSeat (seat-booking junction) ### Tier 2 (Supporting):- Notification (confirmation/reminders)- Admin (staff user)- Refund (if cancellation supported) ### Tier 3 (Enhancement):- PricingRule (dynamic pricing)- CancellationPolicy (rules)- Promotion (discounts) ### Discovered Issues Fixed:1. Added Ticket entity (separate from Booking)2. Added BookingSeat junction entity3. Added Notification entity4. Noted need for Policy/Rules entities5. Added seatType to Seat6. Confirmed soft delete for CustomerYou now have a systematic approach to validating your entity list before proceeding to detailed design. These techniques catch problems early when they're cheap to fix.
Module Complete:
You've now mastered the complete process of identifying core entities:
With a validated entity list in hand, you're ready for the next phase of LLD: defining relationships between entities—the subject of the next module.
Congratulations! You've completed Module 3: Identifying Core Entities. You can now systematically extract, filter, prioritize, and validate entities from any LLD problem. This foundational skill ensures your designs are built on solid conceptual ground before you invest in detailed class design.