What a class diagram is
A class diagram is a UML diagram for showing the static structure of a system.
It answers questions like:
- what important things exist in the system
- what data those things hold
- what behavior they expose
- how those things relate to each other
It is called a class diagram because the main building block is the class, but in practice class diagrams are often used more broadly to model:
- domain concepts
- software objects and services
- architectural responsibilities at a medium level
Think of a class diagram as a map of structure, not a movie of behavior.
It does not primarily show:
- time order
- workflow
- branching logic
- runtime message flow
Those belong more naturally to sequence, activity, or state diagrams.
Why class diagrams matter
Class diagrams are one of the most useful UML tools because they help you:
- understand a problem domain before coding
- identify the main entities and their responsibilities
- reason about relationships and ownership
- communicate design to other people
- prevent vague or duplicated concepts
If a team cannot clearly name the main concepts in a system and how they relate, the code usually becomes messy later.
What a class diagram is good for
Use class diagrams when you need to answer:
- what concepts exist in the system?
- what does each concept know?
- what can each concept do?
- which concepts are connected?
- which concept owns which data?
- how many of one thing can relate to another?
Class diagrams are especially useful for:
- domain modeling
- object-oriented design discussions
- API and service design at a conceptual level
- onboarding and documentation
The basic shape of a class
In UML, a class is usually drawn as a rectangle with up to three compartments:
- the class name
- attributes
- operations
Example:
classDiagram class Book { +isbn: String +title: String +author: String +isAvailable(): Boolean }
How to read that
Bookis the class nameisbn,title, andauthorare attributesisAvailable()is an operation+means public visibility
You do not always need to show all three compartments. A diagram can be useful even with only class names and relationships.
Class names
A class name usually:
- is singular, not plural
- is a noun or noun phrase
- represents one kind of thing
Good examples:
BookMemberInvoicePaymentReservation
Less useful examples:
BooksDataManagerStuffSystemThing
Good class names improve the design because they force clearer thinking.
Attributes
Attributes describe the data a class holds.
Example:
classDiagram class Member { +memberId: String +name: String +email: String +joinedOn: Date }
Attribute format
The common format is:
visibility name: Type
Example:
+name: String-balance: Decimal#status: OrderStatus
You can omit types if the diagram is conceptual and types would add noise.
Operations
Operations describe what a class can do.
Example:
classDiagram class Order { +addItem(product: Product, qty: int) +calculateTotal(): Decimal +submit() +cancel() }
Operation format
The common format is:
visibility name(parameter: Type): ReturnType
Example:
+borrow(book: Book)+isExpired(): Boolean+assign(driver: Driver)
In early domain modeling, you can keep operations minimal. If you list too many methods, the diagram starts turning into code.
Visibility
UML commonly uses these visibility markers:
+public-private#protected~package
Example:
classDiagram class Account { -passwordHash: String +email: String +resetPassword() }
For many practical notes, visibility is optional. Use it when it helps understanding.
Types
Types tell you what kind of value an attribute or parameter has.
Examples:
StringDateBooleanMoneyOrderStatus
Types are useful when:
- the domain meaning matters
- you want the model to be more precise
- you are showing constraints or design intent
Types are less useful when:
- you are still brainstorming concepts
- the diagram becomes cluttered
The most important idea: structure, not execution
A class diagram shows what exists and how it is connected.
It does not show:
- step-by-step request flow
- who calls whom first
- branching over time
- lifecycle transitions
If you find yourself writing a story of events, you probably need a sequence or activity diagram instead.
Relationships: the heart of class diagrams
Most of the value in a class diagram comes from the relationships.
The main ones you should know are:
- association
- aggregation
- composition
- inheritance
- realization
- dependency
You do not need to memorize every UML symbol before you can use class diagrams well. First understand the meaning.
1. Association
An association means two classes are related.
Example:
classDiagram class Customer class Order Customer --> Order : places
This means:
CustomerandOrderare connected- the label
placesexplains the meaning of the connection
In plain language:
- a customer places orders
Association is the most common relationship.
A more neutral notation
Sometimes people draw it without suggesting direction:
classDiagram class Teacher class Course Teacher -- Course : teaches
This simply says the classes are related.
2. Directed association
A directed association suggests one class knows about or refers to the other.
Example:
classDiagram class Invoice class Customer Invoice --> Customer : billedTo
This often implies:
Invoiceholds or uses a reference toCustomer- the navigation is mainly from invoice to customer
Do not over-interpret arrows in high-level conceptual diagrams. Use them only when direction matters.
3. Multiplicity
Multiplicity tells you how many instances can participate in a relationship.
Common multiplicities:
1exactly one0..1zero or one*many0..*zero or many1..*one or many2..5between two and five
Example:
classDiagram class Customer class Order Customer "1" --> "0..*" Order : places
This means:
- one
Orderis placed by oneCustomer - one
Customercan place zero or manyOrders
Multiplicity is one of the most useful parts of a class diagram because it reveals business rules.
4. Aggregation
Aggregation is a weak whole-part relationship.
Example idea:
- a
TeamhasPlayers - players can still exist independently of a team
Example:
classDiagram class Team class Player Team o-- Player : has
Meaning:
TeamgroupsPlayers- but
Playeris not completely owned by one team for its existence as a concept
In practice, many teams use plain association unless the distinction is important.
5. Composition
Composition is a strong whole-part relationship.
The part strongly belongs to the whole.
Classic example:
- an
OrdercontainsOrderItems
classDiagram class Order class OrderItem Order *-- OrderItem : contains
Meaning:
OrderItemis part of anOrder- the item usually does not make sense by itself outside the order
Composition often implies stronger lifecycle ownership.
Another good example:
HouseandRoom
This is stronger than simple association because the room is modeled as a structural part of the house.
6. Inheritance
Inheritance shows a more general class and more specific subclasses.
Example:
classDiagram class User { +id: String +name: String } class Member { +membershipLevel: String } class Librarian { +employeeId: String } User <|-- Member User <|-- Librarian
Meaning:
Memberis a kind ofUserLibrarianis a kind ofUser- common structure can live in the parent class
Use inheritance carefully. It is easy to overuse. Prefer it only when there is a genuine “is-a” relationship.
7. Realization
Realization is used when a class implements an interface.
Example:
classDiagram class PaymentGateway { <<interface>> +charge(amount: Money): Receipt } class StripeGateway class PaypalGateway PaymentGateway <|.. StripeGateway PaymentGateway <|.. PaypalGateway
Meaning:
PaymentGatewaydefines a contract- concrete classes implement it
This is more common in software design diagrams than in pure domain modeling.
8. Dependency
A dependency means one class temporarily uses another.
Example idea:
ReportGeneratorusesInvoiceRepository
classDiagram class ReportGenerator class InvoiceRepository ReportGenerator ..> InvoiceRepository : uses
This is weaker than a structural association. It often means:
- method-level use
- temporary collaboration
- no long-term ownership
Reading a class diagram step by step
When you see a class diagram, read it in this order:
- identify the main classes
- read relationship labels
- check multiplicities
- notice whole-part relationships
- look for inheritance or interfaces
- inspect attributes and operations only after the structure is clear
This order prevents you from getting lost in details too early.
Very small example 1: Student and Course
This is a good beginner example for many-to-many structure.
classDiagram class Student { +studentId: String +name: String } class Course { +courseCode: String +title: String } Student "0..*" --> "0..*" Course : enrolls in
What it teaches
- one student can enroll in many courses
- one course can have many students
- multiplicity can exist on both sides
What it does not show
It does not show:
- enrollment date
- grade
- semester
As soon as those details matter, the relationship should often become its own class.
Very small example 2: Student, Course, and Enrollment
This is the better model when the relationship has its own data.
classDiagram class Student { +studentId: String +name: String } class Course { +courseCode: String +title: String } class Enrollment { +enrolledOn: Date +grade: String +status: EnrollmentStatus } Student "1" --> "0..*" Enrollment : has Course "1" --> "0..*" Enrollment : includes
Important lesson
If a relationship has its own attributes, that is often a signal that it should be modeled as a separate class.
This pattern appears constantly in real systems.
Examples:
EnrollmentOrderItemBookingMembershipAssignment
Very small example 3: Order and OrderItem
This is a classic composition example.
classDiagram class Order { +orderNumber: String +createdOn: Date +calculateTotal(): Money } class OrderItem { +quantity: int +unitPrice: Money +subtotal(): Money } class Product { +sku: String +name: String } Order "1" *-- "1..*" OrderItem : contains OrderItem --> Product : refers to
What it teaches
- an order is made of items
- each item refers to a product
- a product is not the same thing as an order item
That distinction matters. A product is catalog information. An order item is a purchased line in a particular order.
Very small example 4: User and Session
This is useful for authentication or web systems.
classDiagram class User { +userId: String +email: String } class Session { +sessionId: String +createdAt: DateTime +expiresAt: DateTime +isExpired(): Boolean } User "1" --> "0..*" Session : owns
What it teaches
- one user can have multiple sessions
- sessions are separate entities, not just fields inside user
- the diagram helps separate account identity from login activity
Real example 1: Online library system
This continues the same domain used in the first note.
classDiagram class Member { +memberId: String +name: String +borrow(copy: BookCopy) +return(copy: BookCopy) } class Book { +isbn: String +title: String +author: String } class BookCopy { +barcode: String +status: CopyStatus +markLost() } class Loan { +loanDate: Date +dueDate: Date +returnedOn: Date +close() } class Reservation { +reservedOn: Date +status: ReservationStatus +cancel() } Book "1" *-- "1..*" BookCopy : has copies Member "1" --> "0..*" Loan : creates BookCopy "1" --> "0..*" Loan : appears in Member "1" --> "0..*" Reservation : makes Book "1" --> "0..*" Reservation : reserved for
Why this model is better than a naive one
Many beginners connect Member directly to Book.
That is too vague because:
- libraries often own multiple physical copies of one book
- loans usually happen on copies, not on the abstract title
This is a key modeling lesson:
- separate the conceptual item from the physical or transactional item
Similar pairs in other systems:
ProductvsInventoryUnitEventvsTicketVehicleModelvsVehicle
Real example 2: E-commerce order model
This is one of the most reusable examples across real software.
classDiagram class Customer { +customerId: String +name: String +email: String } class Address { +line1: String +city: String +postalCode: String +country: String } class Order { +orderId: String +placedOn: DateTime +status: OrderStatus +calculateTotal(): Money } class OrderItem { +quantity: int +unitPrice: Money } class Product { +sku: String +name: String +listPrice: Money } class Payment { +paymentId: String +amount: Money +status: PaymentStatus +authorize() +refund() } Customer "1" --> "0..*" Order : places Customer "1" --> "0..*" Address : has Order "1" *-- "1..*" OrderItem : contains OrderItem --> Product : for Order "1" --> "0..*" Payment : paid by Order --> Address : ships to
What this teaches
OrderItemis not optional noise; it is structurally importantPaymentis separate fromOrderAddressis often its own concept- class diagrams help avoid collapsing too many responsibilities into one giant
Orderclass
Real example 3: Hotel booking system
This is good for reservations, time-based relationships, and lifecycle-heavy domains.
classDiagram class Guest { +guestId: String +name: String +phone: String } class Room { +roomNumber: String +type: RoomType +nightlyRate: Money } class Reservation { +reservationId: String +checkInDate: Date +checkOutDate: Date +status: ReservationStatus +confirm() +cancel() } class Payment { +paymentId: String +amount: Money +paidOn: DateTime } Guest "1" --> "0..*" Reservation : makes Room "1" --> "0..*" Reservation : assigned to Reservation "1" --> "0..*" Payment : settled by
Modeling lesson
Notice that:
- a reservation connects a guest and a room
- the reservation holds the dates and status
This is another case where the relationship itself is a first-class concept.
Real example 4: School management system
This example combines inheritance, relationship classes, and composition-like ownership.
classDiagram class Person { +id: String +name: String +email: String } class Student { +rollNumber: String } class Teacher { +employeeNumber: String } class Course { +code: String +title: String } class Enrollment { +enrolledOn: Date +grade: String } class Assignment { +title: String +dueDate: Date } Person <|-- Student Person <|-- Teacher Teacher "1" --> "0..*" Course : teaches Student "1" --> "0..*" Enrollment : has Course "1" --> "0..*" Enrollment : includes Course "1" *-- "0..*" Assignment : contains
What this teaches
- shared identity can sit in a parent class
- enrollment holds relationship data
- assignments are structural parts of a course in this model
Real example 5: Ride-sharing system
This example helps with roles, transactional entities, and temporary collaborations.
classDiagram class Rider { +riderId: String +name: String } class Driver { +driverId: String +name: String +rating: Decimal } class Vehicle { +plateNumber: String +model: String } class Trip { +tripId: String +requestedAt: DateTime +startedAt: DateTime +endedAt: DateTime +fare: Money } class Payment { +paymentId: String +amount: Money +status: PaymentStatus } Driver "1" --> "1" Vehicle : uses Rider "1" --> "0..*" Trip : requests Driver "1" --> "0..*" Trip : serves Trip "1" --> "1" Payment : settled by
What this teaches
- trips are central business events
- rider and driver are connected through trips, not directly
- payment belongs to the trip context
This pattern is common: many systems are best modeled around a transactional record in the middle.
Real example 6: Issue tracking system
This is useful for software engineering domains.
classDiagram class User { +userId: String +name: String } class Project { +projectKey: String +name: String } class Issue { +issueId: String +title: String +description: String +status: IssueStatus +priority: Priority } class Comment { +commentId: String +body: String +createdAt: DateTime } class Label { +name: String } Project "1" --> "0..*" Issue : contains User "0..1" --> "0..*" Issue : assigned to User "1" --> "0..*" Comment : writes Issue "1" *-- "0..*" Comment : has Issue "0..*" --> "0..*" Label : tagged with
What this teaches
- comments belong structurally to issues
- assignment can be optional
- labels often create many-to-many tagging relationships
How to build a class diagram for any system
When modeling a new system, use this process.
Step 1: list the important nouns
Start from requirements or user stories and extract nouns.
Example from an e-commerce story:
- customer
- cart
- order
- product
- payment
- address
Not every noun becomes a class, but this is the right starting point.
Step 2: remove vague or duplicate concepts
Ask:
- are
AccountandUseractually different? - is
OrderLinethe same asOrderItem? - is
Cataloga concept or just a view over products?
Clarity here matters more than drawing speed.
Step 3: identify relationships
Ask:
- which things are connected?
- which things contain other things?
- which things are just references?
- where does ownership matter?
Step 4: add multiplicity
Ask:
- one or many?
- optional or required?
- can this exist without that?
This is where business rules become concrete.
Step 5: promote important relationships into classes
If a relationship has data of its own, make it a class.
Common signals:
- dates
- status
- quantity
- role
- price
- grade
Step 6: add only important attributes and operations
Do not dump everything from the code or database.
Include:
- identity fields
- domain-relevant attributes
- a few meaningful operations if needed
Skip:
- trivial getters/setters
- implementation detail fields
- framework boilerplate
Step 7: split diagrams if needed
If the diagram becomes crowded:
- make one diagram for billing
- one for ordering
- one for inventory
One giant class diagram is usually a bad diagram.
How class diagrams differ from database schemas
Beginners often confuse class diagrams with ER diagrams or table schemas.
They overlap, but they are not the same.
Class diagrams focus on:
- concepts
- behavior
- responsibilities
- object relationships
Database schemas focus on:
- persistence structure
- normalization
- keys
- storage constraints
Example:
Order.calculateTotal()belongs naturally in a class diagram- index strategy does not
Do not turn your class diagram into a database dump unless the goal is specifically persistence modeling.
How class diagrams differ from code
A class diagram is not a printout of source code.
If you mirror every class, every field, and every method from code, the diagram often becomes useless.
A useful class diagram is selective.
It should emphasize:
- important concepts
- meaningful relationships
- the design choices people need to understand
Common modeling patterns you will reuse
1. Entity and relationship class
Pattern:
- two main concepts
- a middle class stores relationship details
Examples:
Student - Enrollment - CourseOrder - OrderItem - ProductGuest - Reservation - Room
2. Abstract parent with specialized children
Pattern:
- shared identity or behavior in a parent
- specialized roles in children
Examples:
User -> Member, LibrarianPerson -> Student, TeacherPaymentMethod -> CardPayment, UpiPayment
3. Whole and parts
Pattern:
- one thing structurally contains others
Examples:
Order -> OrderItemCourse -> AssignmentInvoice -> InvoiceLine
4. Central transaction model
Pattern:
- major actors are linked through an event or record
Examples:
Rider - Trip - DriverBuyer - Order - SellerPatient - Appointment - Doctor
These patterns cover a large share of real systems.
Mermaid syntax for practical class diagrams
Since you are keeping notes in Markdown, Mermaid is a good default.
Simple class
classDiagram class Book
Class with attributes and operations
classDiagram class Book { +isbn: String +title: String +isAvailable(): Boolean }
Association
classDiagram Customer --> Order : places
Multiplicity
classDiagram Customer "1" --> "0..*" Order : places
Composition
classDiagram Order *-- OrderItem : contains
Aggregation
classDiagram Team o-- Player : has
Inheritance
classDiagram User <|-- Admin
Interface realization
classDiagram Gateway <|.. StripeGateway
Dependency
classDiagram ReportService ..> Repository : uses
Practical rules for good class diagrams
Use these rules consistently:
1. Model concepts, not raw storage
Ask:
- what thing matters in the domain?
Do not start from tables or JSON fields.
2. Keep one abstraction level per diagram
Do not mix:
- business actors
- database rows
- REST endpoints
- UI widgets
in the same class diagram.
3. Use multiplicity carefully
Multiplicity is not decoration. It encodes rules.
If you are unsure, ask:
- is this optional?
- can there be many?
- can this exist without the other?
4. Use composition only when ownership is strong
Not every “has-a” relationship is composition.
Order *-- OrderItem makes sense.
Company *-- Employee is usually more debatable, because employees still exist independently as people and often as domain entities.
5. Do not overuse inheritance
Prefer inheritance only for strong “is-a” relationships.
Bad inheritance usually appears when people try to force code reuse instead of modeling truth.
6. Keep diagrams small enough to think with
A diagram should reduce confusion, not become a wall of boxes.
Common beginner mistakes
1. Making every noun a class
Some nouns are just attributes or labels, not full classes.
Example:
Addressmay deserve a classColormight just be an attribute, depending on context
2. Forgetting multiplicity
Without multiplicity, important rules stay hidden.
3. Confusing product with purchased item
Examples of important distinctions:
ProductvsOrderItemBookvsBookCopyCoursevsEnrollment
4. Treating relationships with data as plain lines
If the relationship has dates, status, quantity, or price, it often deserves its own class.
5. Putting workflow into a class diagram
If you are drawing steps, decisions, or message order, you are probably using the wrong diagram type.
6. Showing too much code detail
A class diagram full of getters, setters, helpers, and framework methods usually hides the real design.
When to use class diagrams
Use class diagrams when:
- exploring a new domain
- designing a feature before implementation
- documenting a subsystem
- explaining object relationships to a team
- clarifying ambiguous concepts
Do not force a class diagram when:
- the main problem is process flow
- the main problem is runtime interaction order
- a short paragraph would explain the idea better
What you should know to be competent with class diagrams
You are already doing useful class-diagram work if you can:
- identify the main domain concepts
- distinguish attributes from separate classes
- model associations and multiplicities clearly
- recognize when a relationship should become its own class
- use composition and inheritance carefully
- keep diagrams focused and readable
- choose examples that reflect real business rules
That is the practical skill that matters.
A final modeling checklist
Before you finish a class diagram, ask:
- Are the main concepts named clearly?
- Does each class represent one coherent thing?
- Are the relationships labeled meaningfully?
- Are multiplicities present where they matter?
- Did I separate abstract concepts from physical or transactional ones?
- Did I turn relationship-with-data cases into classes?
- Is the diagram focused on one viewpoint?
- Would another engineer understand the model without a spoken explanation?
If the answer to most of those is yes, the diagram is probably useful.
Bottom line
Class diagrams are the most practical UML tool for modeling system structure.
They help you answer:
- what exists
- what those things know
- what they do
- how they relate
If you learn to model:
- classes
- attributes
- operations
- associations
- multiplicities
- composition
- inheritance
and if you practice on systems like libraries, e-commerce, booking, school, and issue tracking, you will be able to create class diagrams for most real software domains with confidence.