Building a Hotel Management ERD with PlantUML and VPasCode

A YouTube thumbnail-style graphic featuring the bold text "BUILD PROFESSIONAL DATABASE DIAGRAMS" in large uppercase letters, with a pointing finger icon on the right and a rendered Entity Relationship Diagram displayed in the background.

When designing robust database architectures for complex domains like hospitality, visualizing relationships clearly is critical. Whether you are mapping customer profiles, tracking room inventories, or managing billing, a well-structured Entity Relationship Diagram (ERD) saves countless hours during development. In this masterclass, I will walk you through how I built a comprehensive Hotel Management ERD from scratch using PlantUML inside the free PlantUML editor, VPasCode.

1. Initializing the PlantUML Environment

I start by setting up the foundational structure of the script. When working with complex schemas containing numerous attributes, layout readability is paramount. I configure PlantUML to hide unnecessary clutter and optimize the diagram layout direction:


@startuml 
title Hotel Management ERD

hide circle
left to right direction
    

Why this approach? Hiding the default attribute circles keeps the entity boxes clean and modern. Setting the direction to left to right direction helps prevent wide ERDs from becoming vertically unmanageable, spreading out multi-table relationships logically.

2. Defining Core Entities and Attributes

Next, I need to define the core entities representing our business domain. I structure each entity with its primary keys, unique constraints, and typed attributes. Let’s look at how I set up the core Customer and RoomType entities:


entity "Customer" as customer {
  * customer_id : UUID <<PK>>
  --
  first_name : VARCHAR(50)
  last_name : VARCHAR(50)
  email : VARCHAR(100) <<UK>>
  phone : VARCHAR(20)
  address : TEXT
  loyalty_points : INTEGER
  total_stays : INTEGER
  date_registered : TIMESTAMP
  date_of_birth : DATE
  nationality : VARCHAR(50)
}

entity "RoomType" as roomtype {
  * room_type_id : UUID <<PK>>
  --
  type_name : VARCHAR(50) <<UK>>
  description : TEXT
  standard_capacity : INTEGER
  base_price : DECIMAL(10,2)
  extra_bed_charge : DECIMAL(10,2)
  has_kitchenette : BOOLEAN
  has_balcony : BOOLEAN
}
    

By defining explicit markers like <<PK>> and <<UK>>, anyone reviewing the code instantly understands the indexing and uniqueness rules before the database migration scripts are even written.

3. Mapping Operational Entities (Rooms, Reservations, and Staff)

With our reference data in place, I move on to the transactional heart of the hotel management system: rooms, reservations, and staff workflows. Each entity links back to our foundational tables using foreign keys:


entity "Room" as room {
  * room_id : UUID <<PK>>
  --
  room_number : VARCHAR(10) <<UK>>
  floor_number : INTEGER
  room_type_id : UUID <<FK>>
  capacity : INTEGER
  base_price : DECIMAL(10,2)
  is_available : BOOLEAN
  has_view : BOOLEAN
  square_feet : INTEGER
  last_renovated : DATE
}

entity "Reservation" as reservation {
  * reservation_id : UUID <<PK>>
  --
  customer_id : UUID <<FK>>
  room_id : UUID <<FK>>
  check_in_date : DATE
  check_out_date : DATE
  number_of_guests : INTEGER
  total_amount : DECIMAL(10,2)
  status : VARCHAR(20)
  special_requests : TEXT
  created_at : TIMESTAMP
  updated_at : TIMESTAMP
  confirmation_code : VARCHAR(20) <<UK>>
}
    

4. Establishing Financial and Service Relationships

To wrap up the architecture, I map out the billing records (Payments and Invoices) along with auxiliary services like Room Service handled by hotel staff. To connect everything together, I explicitly define the cardinality using standard PlantUML notation:


' Relationships
customer ||--o{ reservation : "makes"
room ||--o{ reservation : "assigned to"
reservation ||--|| payment : "has"
reservation ||--|| invoice : "generates"
reservation ||--o{ roomservice : "reuests"
staff ||--o{ roomservice : "fulfills"
roomtype ||--o{ room : "categorizes"

@enduml
    

Design Choice: Using precise crow’s foot notation (such as ||--o{ for mandatory-to-optional one-to-many relationships) ensures the business logic rules—like a reservation requiring a customer, but a customer potentially having multiple or zero active bookings—are accurately modeled.

 

The Complete PlantUML Script

Here is the full, assembled source code for our hotel management ERD. You can copy and paste this code directly into your favorite free PlantUML editor to edit or render vector graphics:

@startuml 

title Hotel Management ERD

hide circle
left to right direction

' Define entities with attributes
entity "Customer" as customer {
  * customer_id : UUID <>
  --
  first_name : VARCHAR(50)
  last_name : VARCHAR(50)
  email : VARCHAR(100) <>
  phone : VARCHAR(20)
  address : TEXT
  loyalty_points : INTEGER
  total_stays : INTEGER
  date_registered : TIMESTAMP
  date_of_birth : DATE
  nationality : VARCHAR(50)
}

entity "Room" as room {
  * room_id : UUID <>
  --
  room_number : VARCHAR(10) <>
  floor_number : INTEGER
  room_type_id : UUID <>
  capacity : INTEGER
  base_price : DECIMAL(10,2)
  is_available : BOOLEAN
  has_view : BOOLEAN
  square_feet : INTEGER
  last_renovated : DATE
}

entity "RoomType" as roomtype {
  * room_type_id : UUID <>
  --
  type_name : VARCHAR(50) <>
  description : TEXT
  standard_capacity : INTEGER
  base_price : DECIMAL(10,2)
  extra_bed_charge : DECIMAL(10,2)
  has_kitchenette : BOOLEAN
  has_balcony : BOOLEAN
}

entity "Reservation" as reservation {
  * reservation_id : UUID <>
  --
  customer_id : UUID <>
  room_id : UUID <>
  check_in_date : DATE
  check_out_date : DATE
  number_of_guests : INTEGER
  total_amount : DECIMAL(10,2)
  status : VARCHAR(20)
  special_requests : TEXT
  created_at : TIMESTAMP
  updated_at : TIMESTAMP
  confirmation_code : VARCHAR(20) <>
}

entity "Payment" as payment {
  * payment_id : UUID <>
  --
  reservation_id : UUID <>
  amount : DECIMAL(10,2)
  payment_date : TIMESTAMP
  payment_method : VARCHAR(30)
  transaction_id : VARCHAR(50) <>
  status : VARCHAR(20)
  receipt_url : TEXT
  refund_amount : DECIMAL(10,2)
}

entity "Invoice" as invoice {
  * invoice_id : UUID <>
  --
  reservation_id : UUID <>
  invoice_number : VARCHAR(20) <>
  issued_date : DATE
  due_date : DATE
  subtotal : DECIMAL(10,2)
  tax : DECIMAL(10,2)
  service_charge : DECIMAL(10,2)
  total_amount : DECIMAL(10,2)
  status : VARCHAR(20)
}

entity "Staff" as staff {
  * staff_id : UUID <>
  --
  first_name : VARCHAR(50)
  last_name : VARCHAR(50)
  email : VARCHAR(100) <>
  phone : VARCHAR(20)
  role : VARCHAR(30)
  hire_date : DATE
  salary : DECIMAL(10,2)
  shift_schedule : VARCHAR(50)
  is_active : BOOLEAN
}

entity "RoomService" as roomservice {
  * service_id : UUID <>
  --
  reservation_id : UUID <>
  staff_id : UUID <>
  service_type : VARCHAR(50)
  description : TEXT
  request_time : TIMESTAMP
  completion_time : TIMESTAMP
  status : VARCHAR(20)
  price : DECIMAL(10,2)
}

' Relationships
customer ||--o{ reservation : "makes"
room ||--o{ reservation : "assigned to"
reservation ||--|| payment : "has"
reservation ||--|| invoice : "generates"
reservation ||--o{ roomservice : "requests"
staff ||--o{ roomservice : "fulfills"
roomtype ||--o{ room : "categorizes"

@enduml

Editing a ERD in Visual Paradigm VPasCode


Bring Your Database Designs to Life with VPasCode

Building complex database models doesn’t have to mean wrestling with clumsy drag-and-drop user interfaces. By leveraging a diagram-as-code approach, your architecture stays version-controlled, clean, and effortlessly maintainable.

Ready to try building your own system architectures? Head over to VPasCode to experience real-time rendering, automatic format detection, and seamless exports for your technical documentation today!

Scroll to Top