Back to template

Class Diagram Examples

These class diagram examples show how development teams model domain logic, inheritance hierarchies, and service boundaries in different systems. Use them as a reference for your own design work — the patterns are reusable across languages and frameworks.

Class Diagram Examples

Real examples

User authentication system

Who uses it: Backend developer designing an auth service

User { id: UUID, email: string, passwordHash: string }
+ login(email, password): Token
+ logout(token): void
+ resetPassword(email): void
Token { value: string, expiresAt: DateTime, userId: UUID }
Role { name: string, permissions: string[] }
User *── * Role (many-to-many)

Why this works: Modeling authentication before implementing it surfaces design questions early: should Token be a value object or entity? Is Role separate from Permission? The diagram forces these decisions before code is written.

Content management system

Who uses it: Full-stack developer building a blog platform

Article { id, title, body, status: Draft|Published }
+ publish(): void
+ archive(): void
Author { id, name, bio } ──1 Article
Tag { name } *── * Article
Comment { body, createdAt } *── 1 Article
MediaAsset { url, mimeType } *── * Article

Why this works: The status field as an enum on Article and the many-to-many with Tag drove the decision to use a join table rather than a JSON column — a trade-off that would have been easy to miss without the diagram.

Payment processing domain

Who uses it: Platform engineer designing a payment abstraction layer

PaymentMethod (abstract) { id, userId }
← CreditCard { last4, expiry, token }
← BankAccount { accountNumber, routingNumber }
Payment { id, amount, currency, status }
+ process(): Result
+ refund(amount): void
Payment *── 1 PaymentMethod
Refund { id, amount, reason } *── 1 Payment

Why this works: The abstract PaymentMethod class and its concrete subclasses show the strategy pattern clearly. A reviewer can see the polymorphic design intent without reading the code.

Inventory management system

Who uses it: Developer designing a warehouse management backend

Product { sku, name, description }
InventoryItem { quantity, warehouseId, reservedQty }
1 ── * Product
StockMovement { type: In|Out|Transfer, quantity, timestamp }
*── 1 InventoryItem
PurchaseOrder { status, expectedDelivery }
1 ── * PurchaseOrderLine { product, quantity, unitCost }

Why this works: StockMovement as an immutable event log (rather than updating quantity directly) was a key architectural decision that the diagram made obvious to the whole team.

Notification service

Who uses it: Engineer designing a multi-channel notification system

Notification (abstract) { id, userId, createdAt, + send() }
← EmailNotification { subject, htmlBody, recipient }
← PushNotification { title, body, deviceToken }
← SMSNotification { phone, message }
NotificationPreference { userId, channel, enabled }
NotificationTemplate { name, subject, body }

Why this works: The abstract base class enforces a common send() interface. Adding a new channel means subclassing Notification — the diagram communicates this extension point to future contributors.

Microservices domain boundary

Who uses it: Architect defining service boundaries for a large e-commerce platform

OrderService: Order, OrderItem, OrderStatus
CatalogService: Product, Category, PriceRule
UserService: User, Address, PaymentMethod
FulfillmentService: Shipment, Tracking, Warehouse
─── cross-service references use IDs only (no direct object refs)

Why this works: Using a class diagram to show domain boundaries (rather than infrastructure) helped the team enforce that services communicate via IDs and events, not shared object references.

Tips for better UML class diagrams

  • Keep class diagrams focused on one bounded context or domain area — trying to show an entire system in one diagram produces an unreadable mess.
  • Prefer showing the most important relationships over being exhaustive — a diagram with 30 classes and 50 arrows communicates nothing.
  • Use visibility modifiers consistently (+/-/#) to show the intended public API of a class, not just its internal fields.
  • A class diagram is a design tool, not a source of truth. Keep it updated only for areas where the architecture is still actively evolving.

Related resources

How it compares to similar tools

Class diagram vs ER diagram

An ER diagram models how data is stored: tables, columns, foreign keys. A class diagram models how behavior is organized: methods, inheritance, interfaces. They often look similar for simple CRUD apps, which misleads people into using one for both. If your diagram has no methods on it, you probably drew an ER diagram and labeled it UML.

Class diagram vs sequence diagram

A class diagram is a static structure snapshot — it shows what exists and how types relate. A sequence diagram shows a single scenario unfolding over time. Structure questions ("where should this logic live?") need a class diagram; flow questions ("why is this call made twice?") need a sequence diagram.

Hand-drawn class diagram vs generated-from-code

Tools that reverse-engineer diagrams from source render every class and field, which is exhaustive but unreadable — the important relationships drown in noise. A hand-drawn diagram is valuable precisely because you chose what to leave out. Use generation for reference, hand-drawing for explaining and designing.

Full UML notation vs simplified boxes

Strict UML has distinct notation for aggregation, composition, dependency, and realization. Most teams don't remember which diamond is which, so precise notation actively misleads. Unless you're producing a formal spec, plain boxes with labeled arrows communicate more reliably.

Common mistakes to avoid

  • Putting every field and getter on the diagram

    A class with 20 attributes and 20 accessors takes enormous space and communicates almost nothing. Show only the fields that matter to the relationships you're explaining. The diagram is an argument about design, not an inventory of the code.

  • Using inheritance where composition is meant

    Drawing an inheritance arrow between two classes that merely share a few fields bakes a wrong design into the diagram — and readers will implement it. Ask whether the subclass genuinely *is* the parent in every context. If it only *has* the parent's data, that's composition.

  • Leaving relationships unlabeled and undirected

    A bare line between Order and Customer says almost nothing. Does an Order have one Customer, or many? Does Customer know about Order? Add multiplicity (1, 0..1, *) and a direction; without them the reader invents their own answer and it's usually wrong.

  • Modeling the database instead of the domain

    Join tables and surrogate ID columns are storage artifacts, not domain concepts. Putting them on a class diagram couples your design discussion to the current schema, which is exactly what you want to be able to question during design review.

Frequently asked questions

How many classes should one class diagram contain?+

Roughly 5–15 for a diagram meant to be read and discussed. Past that, split by bounded context or subsystem — one diagram per cohesive area, plus a high-level diagram showing how the areas connect. A 60-class diagram is technically accurate and practically unread.

Should I show method signatures with parameters and return types?+

Only where the signature is the point. If you're explaining a strategy interface, the signature matters and belongs on the diagram. For a class you're merely referencing, the name alone is enough. Full signatures everywhere triples the diagram size for very little added meaning.

Do I need to follow strict UML notation?+

Depends on the audience. For an academic assignment or a formal specification, yes — the notation is part of what's being assessed. For a design discussion with your team, simplified boxes and labeled arrows work better, because most readers can't reliably distinguish UML's four relationship diamonds anyway.

How do I diagram interfaces and abstract classes?+

Mark them clearly with a label («interface» or «abstract») or a visual convention like italics, and place implementations below the abstraction. The reason abstractions belong on the diagram is that they define your extension points — which is usually the most important thing a reader wants to learn from the design.

Is a class diagram still useful for functional or Go-style code?+

Yes, with a shift in reading. Boxes become structs, modules, or types; inheritance arrows mostly disappear and are replaced by interface satisfaction and composition. The value — showing which types exist and how they depend on each other — survives even without classical OOP inheritance.

Start editing online

Go back to the template, swap in your own content, and keep the same structure if it fits your project.

Use this template: /editor/new?template=class-diagram

Use this class diagram template