Mastering Logistics Data Modeling: A Moving Company ERD with PlantUML

In the fast-paced logistics industry, the transition from a simple customer inquiry to a finalized moving quote relies heavily on accurate data modeling. A moving company’s operational backbone is not just about trucks and drivers; it is a complex web of customer profiles, property surveys, room-by-room inventory tracking, and dynamic cost calculations. When a customer requests a move, the system must seamlessly link their contact details to a specific move request, which triggers a physical survey. This survey then populates a detailed inventory list, categorizing items by room and type, while simultaneously calculating the necessary packing materials and volume estimates. Without a robust data structure, these workflows become fragmented, leading to inaccurate quotes, lost inventory items, and operational bottlenecks.

Real-world system context and operational workflow illustration

Visualizing this complexity through an Entity Relationship Diagram (ERD) is essential for architects and developers building such systems. By modeling the relationships between customers, move requests, surveys, and inventory items, teams can ensure data integrity and scalability from day one. Using diagram-as-code with PlantUML allows these models to be versioned, reviewed, and updated alongside the application code, ensuring that the documentation never drifts from the actual implementation. This tutorial demonstrates how to construct a professional ERD for a household goods inventory and relocation quote generator, providing a clear blueprint for managing the entire lifecycle of a move.

Understanding the Model: Purpose, Scope & Problem Framing

Diagram Abstraction & Representation
An Entity Relationship Diagram (ERD) is the foundational blueprint for relational databases. In this specific context, the ERD abstracts the physical world of a moving job into logical data entities. It defines how a Customer relates to a MoveRequest, how a Survey acts as the bridge between the request and the physical items, and how InventoryItems are categorized and linked to specific rooms. The diagram also captures the financial logic through the Quote and QuoteLineItem entities, showing how the physical inventory translates into a monetary estimate. This abstraction allows developers to focus on the rules of data association—such as one-to-many or one-to-one relationships—without getting bogged down in SQL syntax immediately.

Target Domain Scope & Scenario
The scope of this model covers the core operational workflow of a moving company. It begins with the Customer entity, capturing personal and contact details. It extends to the MoveRequest, which defines the logistics (origin, destination, date). The critical link is the Survey, representing the on-site assessment. From the survey, the model branches into InventoryItem entities, which are further refined by Room definitions and ItemCategory classifications. Finally, it includes the PackingMaterial and Quote entities to handle the commercial side of the operation. The model intentionally excludes external billing gateways or driver scheduling systems to keep the focus on the inventory and quote generation logic.

Key Takeaways & Educational Insights
By studying this diagram, readers will gain insights into how to structure a database for complex, multi-step business processes. Key takeaways include understanding the necessity of a Survey entity to decouple the initial request from the final inventory list, the use of junction tables (like ItemPackingRequirement) to handle many-to-many relationships between items and packing materials, and the strategy for generating dynamic quotes through QuoteLineItem records. This model demonstrates best practices for handling optional data (like survey notes) and mandatory constraints (like required customer details).

Complete Diagram & Full Source Code

Descriptive Alt Text

@startuml
!theme plain
title ERD - Moving Company Household Goods Inventory & Relocation Quote Generator

/' 
Diagram Context:
This ERD models the core data structures for a moving company's system that manages 
household goods inventory and generates relocation quotes. The system tracks customers, 
their move requests, detailed inventory items categorized by room and type, packing 
requirements, and the resulting cost estimates. It supports the workflow from initial 
customer inquiry through on-site survey, inventory recording, quote generation, and 
final move execution. Key entities include Customer, MoveRequest, Survey, InventoryItem, 
Room, ItemCategory, PackingMaterial, Quote, and QuoteLineItem. The relationships 
capture how a customer's move request leads to a survey, which produces an inventory 
list used to generate a detailed quote.
'/

entity "Customer" as customer {
  * customer_id : INT <<PK>>
  --
  first_name : VARCHAR
  last_name : VARCHAR
  email : VARCHAR
  phone : VARCHAR
  address : TEXT
  created_at : DATETIME
}

entity "MoveRequest" as move_request {
  * request_id : INT <<PK>>
  --
  customer_id : INT <<FK>>
  origin_address : TEXT
  destination_address : TEXT
  requested_move_date : DATE
  status : VARCHAR
  created_at : DATETIME
}

entity "Survey" as survey {
  * survey_id : INT <<PK>>
  --
  request_id : INT <<FK>>
  survey_date : DATE
  surveyor_name : VARCHAR
  notes : TEXT
}

entity "Room" as room {
  * room_id : INT <<PK>>
  --
  room_name : VARCHAR
  description : VARCHAR
}

entity "ItemCategory" as item_category {
  * category_id : INT <<PK>>
  --
  category_name : VARCHAR
  fragile : BOOLEAN
  default_weight_kg : DECIMAL
}

entity "InventoryItem" as inventory_item {
  * item_id : INT <<PK>>
  --
  survey_id : INT <<FK>>
  room_id : INT <<FK>>
  category_id : INT <<FK>>
  item_name : VARCHAR
  quantity : INT
  condition : VARCHAR
  estimated_weight_kg : DECIMAL
  volume_cbm : DECIMAL
}

entity "PackingMaterial" as packing_material {
  * material_id : INT <<PK>>
  --
  material_name : VARCHAR
  unit_cost : DECIMAL
  unit_type : VARCHAR
}

entity "ItemPackingRequirement" as item_packing {
  * requirement_id : INT <<PK>>
  --
  item_id : INT <<FK>>
  material_id : INT <<FK>>
  quantity_used : INT
}

entity "Quote" as quote {
  * quote_id : INT <<PK>>
  --
  request_id : INT <<FK>>
  survey_id : INT <<FK>>
  quote_date : DATE
  valid_until : DATE
  total_estimated_cost : DECIMAL
  status : VARCHAR
}

entity "QuoteLineItem" as quote_line {
  * line_id : INT <<PK>>
  --
  quote_id : INT <<FK>>
  description : VARCHAR
  category : VARCHAR
  quantity : DECIMAL
  unit_price : DECIMAL
  line_total : DECIMAL
}

' Relationships
customer ||--o{ move_request : "submits"
move_request ||--o| survey : "has"
survey ||--o{ inventory_item : "records"
room ||--o{ inventory_item : "contains"
item_category ||--o{ inventory_item : "classifies"
inventory_item ||--o{ item_packing : "requires"
packing_material ||--o{ item_packing : "used in"
move_request ||--o{ quote : "generates"
survey ||--o{ quote : "based on"
quote ||--o{ quote_line : "includes"

@enduml

Step-by-Step Architectural Walkthrough

Phase 1: Canvas Configuration & Layout Directives

The first step in building a professional PlantUML ERD is setting the stage. We begin by defining the theme to ensure the diagram looks clean and consistent with enterprise standards. In this case, we use the !theme plain directive to remove decorative elements and focus on the data structure. We also add a title directive to provide immediate context for anyone viewing the diagram. Crucially, we insert a comment block using the / syntax to document the business logic and scope of the diagram, ensuring that the visual model is self-documenting.

@startuml
!theme plain
title ERD - Moving Company Household Goods Inventory & Relocation Quote Generator

/' 
Diagram Context: [Context description here]
'/

Phase 2: Declaring Core Entities, Actors, and Boundaries

With the canvas ready, we define the core entities. In PlantUML, we use the entity keyword. Each entity is defined with a display name (e.g., “Customer”) and an alias (e.g., customer) for use in relationship definitions. Inside the curly braces {}, we list the attributes. We distinguish Primary Keys (PK) using the * symbol and Foreign Keys (FK) using <>. For example, the Customer entity holds personal data, while the MoveRequest entity links back to the customer via customer_id. We also define the Survey entity, which acts as the pivotal link between the initial request and the detailed inventory.

entity "Customer" as customer {
  * customer_id : INT <>
  --
  first_name : VARCHAR
  last_name : VARCHAR
  ...
}

entity "MoveRequest" as move_request {
  * request_id : INT <>
  --
  customer_id : INT <>
  ...
}

Phase 3: Mapping Data Flows & Key Interactions

The heart of the ERD lies in the relationships. We use the double-bar syntax || for mandatory participation (e.g., a move request must belong to a customer) and the crow’s foot { for one-to-many relationships. For instance, customer ||--o{ move_request indicates that one customer can submit zero or many move requests. We also model the complex relationship between InventoryItem, Room, and ItemCategory. The InventoryItem is linked to a Survey (the specific move instance), a Room (the location), and a ItemCategory (the type of item). This ensures that every item in the inventory is fully contextualized.

' Relationships
customer ||--o{ move_request : "submits"
survey ||--o{ inventory_item : "records"
room ||--o{ inventory_item : "contains"
item_category ||--o{ inventory_item : "classifies"

Phase 4: Grouping, Annotations & Visual Polish

Finally, we handle the commercial aspects of the model. The Quote entity is linked to both the MoveRequest and the Survey, capturing the state of the quote at a specific point in time. The QuoteLineItem entity breaks down the total cost into individual line items, allowing for granular pricing of labor, materials, or specific goods. We also introduce the ItemPackingRequirement entity, which is a junction table. This allows a single inventory item to require multiple packing materials (e.g., a piano might need both a blanket and a box), resolving a many-to-many relationship between items and materials. The diagram is then finalized with relationship labels like “submits”, “has”, and “generates” to make the flow intuitive.

entity "ItemPackingRequirement" as item_packing {
  * requirement_id : INT <>
  --
  item_id : INT <>
  material_id : INT <>
  quantity_used : INT
}

inventory_item ||--o{ item_packing : "requires"
packing_material ||--o{ item_packing : "used in"

Syntax & Keyword Deep Dive

To effectively model this logistics system in PlantUML, it is crucial to understand the specific syntax features used:

  • entity "Name" as alias: Defines a new table. The string in quotes is the display name, and the as keyword assigns a short alias for use in relationship definitions.
  • * attribute : Type: The asterisk * before an attribute denotes it as a Primary Key. The Type specifies the data type (e.g., INT, VARCHAR, DECIMAL).
  • <>: This stereotype explicitly marks an attribute as a Foreign Key, linking it to a primary key in another entity.
  • ||--o{: This relationship connector defines a mandatory one-to-many relationship. The || means “one and only one” (mandatory), and the { means “many” (zero or more).
  • --o|: This connector defines an optional one-to-one or one-to-many relationship. The o indicates optional (zero), and | indicates exactly one.
  • !theme plain: A directive that applies a minimal, clean visual style to the entire diagram, removing default borders and colors for a professional look.

Best Practices & Pitfalls to Avoid

When designing ERDs for logistics and inventory systems, adhering to best practices ensures your model remains maintainable and scalable.

  • Normalize Your Data: Avoid storing redundant data. For example, do not store the room_name directly inside the InventoryItem table if it is better represented by a separate Room entity linked via a Foreign Key. This allows you to update room names in one place without affecting thousands of inventory records.
  • Use Junction Tables for Many-to-Many: Never try to link two entities directly if the relationship is many-to-many. As seen with ItemPackingRequirement, using an intermediate table allows you to store additional attributes (like quantity_used) that describe the relationship itself.
  • Consistent Naming Conventions: Use clear, singular nouns for entity names (e.g., Customer not Customers) and consistent camelCase or snake_case for attributes. This reduces ambiguity when writing SQL queries later.
  • Document the Flow: Use the comment block and relationship labels to explain why entities are connected. A relationship labeled “generates” tells a story about the business process that a simple line cannot convey.

Start Building Logistics ERDs Faster with VPasCode

Design, test, and refine your moving company database schema instantly in your browser with VPasCode’s free PlantUML editor—no installation required.

Scroll to Top