# UML Diagrams

Course: [Low-Level Design](https://www.devtutorials.ai/learn/system-design/lld) · Section: LLD Foundations
Source: https://www.devtutorials.ai/learn/system-design/lld/foundations/uml-diagrams

> Learn to read and draw the two UML diagrams teams really use, class diagrams and sequence diagrams, then turn a QuickBite class diagram into working code.

Before a house is built, an architect creates a plan. Builders, plumbers, and electricians all use the same plan. This helps everyone understand where things should go without making assumptions.

Software teams work in a similar way. Before writing code, or while developing it, they often create diagrams to represent the system. The standard drawing language used for this is called **UML** (Unified Modeling Language).

**In simple words:** UML is a set of simple diagramming rules used to show classes and the relationships between them.

## What You Will Learn

In this lesson, you will learn:

1. How to read and create a class box.
2. The five types of lines used to connect class boxes and what each one represents.
3. How to read a sequence diagram.
4. How to create diagrams using text with Mermaid.
5. How to generate a diagram from code and how to write code based on a diagram.

You do not need to learn every part of UML. Most development teams mainly use two types of diagrams: the **class diagram** and the **sequence diagram**. This lesson focuses only on those two diagram types.

## Why Draw at All?

Code can be long and detailed. A diagram gives a quick overview. One diagram can show many classes and their relationships, while the equivalent code may take several screens to read.

Diagrams are also easy to update. Moving a box in a diagram takes only a few seconds, while making the same structural change in code can take much longer.

Without diagrams, each team member may have a different understanding of how the system works. Those mental pictures are often not exactly the same.

## The Class Box

A **class diagram** shows classes and the relationships between them. Each class is represented by a box with three sections.

### The Three Parts

- **Top:** the name of the class.
- **Middle:** the fields, which store data.
- **Bottom:** the methods, which define actions or behavior.

A minus sign (`-`) indicates a private member. A plus sign (`+`) indicates a public member. The type appears after a colon, such as `balance: number`.

### From Box to Code

Here is a `Rider` class box. Each line of the box becomes a line of the TypeScript class.

Step through reading the box and using the class it describes:

Here is the same idea in code:

```ts runnable title="From a class box to code"
// The box says:  Rider | - name: string | - trips: number | + finishTrip(): void, + summary(): string
class Rider {
  private trips = 0;
  constructor(private readonly name: string) {}

  finishTrip(): void {
    this.trips += 1;
  }

  summary(): string {
    return `${this.name} has done ${this.trips} trips`;
  }
}

const ravi = new Rider("Ravi");
ravi.finishTrip();
ravi.finishTrip();
console.log(ravi.summary());
```

Expected output:

```text
Ravi has done 2 trips
```

`name` and `trips` have a minus sign in the diagram, which means they are `private` in the code. `finishTrip()` and `summary()` have a plus sign, which means they are public methods.

## The Five Lines

Class boxes alone do not tell the full story. The lines between them show how classes are connected. These five types of lines are enough to represent most relationships in UML.

**Easy way to remember the lines:** a **triangle** means "is a type of". A **diamond** means "owns". A normal **arrow** means "uses" or "knows about". A **dashed** line means the connection is temporary or less direct.

### Composition: Owns It

Composition means one class **creates and owns** another class. If the owner object is removed, the owned object is also removed. In UML, this relationship is shown using a filled diamond on the owner's side.

A restaurant and its menu are a good example. Each restaurant has its own menu. The menu belongs to that restaurant and is not meaningful on its own.

Step through two restaurants being made:

Here is the same idea in code:

```ts runnable title="Composition: each restaurant makes and owns its own menu"
class Menu {
  private readonly dishes: string[] = [];
  add(dish: string): void { this.dishes.push(dish); }
  get count(): number { return this.dishes.length; }
}

class Restaurant {
  readonly menu = new Menu(); // created here, owned here
  constructor(readonly name: string) {}
}

const a = new Restaurant("Saravana");
const b = new Restaurant("Paradise");
a.menu.add("Idli");
a.menu.add("Vada");

console.log(a.name, a.menu.count);
console.log(b.name, b.menu.count);
```

Expected output:

```text
Saravana 2
Paradise 0
```

Saravana has 2 dishes, while Paradise has 0 dishes. Each time `new Restaurant()` is called, a new `Menu` object is created for that restaurant.

### Association: Knows About It

Association means one class **has a reference to** another class, but it does not create or own it. In UML, this relationship is shown with a simple line and an open arrow.

An order knows which restaurant it belongs to. However, the order did not create the restaurant, and multiple orders can refer to the same restaurant.

<LldDiagram caption="Association: Order keeps a reference to a Restaurant it was given" code={"classDiagram\n  class Restaurant {\n    +name: string\n  }\n  class Order {\n    -restaurant: Restaurant\n    -id: number\n    +label() string\n  }\n  Order \"*\" --> \"1\" Restaurant : knows"} />

Step through two orders made with the same restaurant:

Here is the same idea in code:

```ts runnable title="Association: an order is given a restaurant from outside"
class Restaurant {
  constructor(readonly name: string) {}
}

class Order {
  // The order does not create the restaurant. It is handed one.
  constructor(private readonly restaurant: Restaurant, private readonly id: number) {}
  label(): string {
    return `Order ${this.id} from ${this.restaurant.name}`;
  }
}

const saravana = new Restaurant("Saravana");
const first = new Order(saravana, 1);
const second = new Order(saravana, 2);

console.log(first.label());
console.log(second.label());
```

Expected output:

```text
Order 1 from Saravana
Order 2 from Saravana
```

Both orders refer to the same `saravana` object. The restaurant object is shared, and no copy of it is created.

### Inheritance, Implements and Dependency

You learned about inheritance and interfaces in the OOP Basics lesson. In UML, both relationships use a hollow triangle arrow:

- **Inheritance** (`extends`): shown with a solid line and a hollow triangle.
- **Implements** (`implements`): shown with a dashed line and a hollow triangle.

**Dependency** is the weakest type of relationship. One class uses another class only for a short time, often as a method parameter. In UML, it is shown with a dashed arrow.

Here are all three in one small checkout diagram:

<LldDiagram caption="Card implements PaymentMethod (dashed, hollow triangle). Checkout only uses a Coupon as a parameter (dashed arrow)." code={"classDiagram\n  class PaymentMethod {\n    <<interface>>\n    +pay(amount) string\n  }\n  class Card {\n    +pay(amount) string\n  }\n  class Delivery\n  class ExpressDelivery\n  class Coupon {\n    +code: string\n    +off: number\n  }\n  class Checkout {\n    +priceWith(subtotal, coupon) number\n  }\n  PaymentMethod <|.. Card : implements\n  Delivery <|-- ExpressDelivery : extends\n  Checkout ..> Coupon : uses for a moment"} />

Here is the same idea in code:

```ts runnable title="Inheritance, implements and dependency in code"
interface PaymentMethod {          // drawn with <<interface>>
  pay(amount: number): string;
}

class Card implements PaymentMethod { // dashed line, hollow triangle
  pay(amount: number): string { return `card ${amount}`; }
}

class Coupon {
  constructor(readonly code: string, readonly off: number) {}
}

class Checkout {
  // Dependency: Coupon is only a parameter. Checkout does not keep it.
  priceWith(subtotal: number, coupon: Coupon): number {
    return subtotal - coupon.off;
  }
}

const total = new Checkout().priceWith(500, new Coupon("SAVE50", 50));
console.log(total);
console.log(new Card().pay(total));
```

Expected output:

```text
450
card 450
```

In code, `new Checkout().priceWith(500, new Coupon("SAVE50", 50))` gives `450`, and `new Card().pay(450)` gives `card 450`. `Checkout` uses a `Coupon` only while the method is running and does not store it afterward. This is why the relationship is a dependency and not an association.

### Multiplicity: How Many?

Small numbers written at the ends of relationship lines show how many objects can take part in that relationship. This is called **multiplicity**.

| Written | Means |
|---|---|
| `1` | exactly one |
| `0..1` | zero or one |
| `*` | any number, including zero |
| `1..*` | one or more |
| `0..2` | up to two |

For example, "Menu `1` to `*` MenuItem" means that one menu can contain any number of menu items.

## The Sequence Diagram

A class diagram shows **what classes exist**. A **sequence diagram** shows **what happens and in what order**.

You read a sequence diagram from top to bottom, like a comic strip. Each class has a vertical dotted line called a **lifeline**. Each arrow represents a call from one class to another. A dashed arrow represents a response coming back.

### The Order Example

Here is the place-order flow written as a sequence diagram:

<LldDiagram caption="Placing an order: three calls and two answers, read from top to bottom" code={"sequenceDiagram\n  participant Customer\n  participant OrderService\n  participant Payment\n  participant Notifier\n  Customer->>OrderService: placeOrder(480)\n  OrderService->>Payment: pay(480)\n  Payment-->>OrderService: ok\n  OrderService->>Notifier: send(placed)\n  OrderService-->>Customer: order id 1"} />

Now step through the same five arrows as the calls really happen in the code:

Here is the same idea in code:

```ts runnable title="A sequence diagram, written down as the calls happen"
const steps: string[] = [];

class Notifier {
  send(text: string): void { steps.push(`OrderService -> Notifier: send(${text})`); }
}
class Payment {
  pay(amount: number): boolean {
    steps.push(`OrderService -> Payment: pay(${amount})`);
    steps.push(`Payment --> OrderService: ok`);
    return true;
  }
}
class OrderService {
  constructor(private readonly payment: Payment, private readonly notifier: Notifier) {}
  placeOrder(amount: number): number {
    steps.push(`Customer -> OrderService: placeOrder(${amount})`);
    this.payment.pay(amount);
    this.notifier.send("placed");
    steps.push(`OrderService --> Customer: order id 1`);
    return 1;
  }
}

new OrderService(new Payment(), new Notifier()).placeOrder(480);
for (const s of steps) console.log(s);
```

Expected output:

```text
Customer -> OrderService: placeOrder(480)
OrderService -> Payment: pay(480)
Payment --> OrderService: ok
OrderService -> Notifier: send(placed)
OrderService --> Customer: order id 1
```

The calls happen in exactly the same order as the arrows in the sequence diagram, from top to bottom.

A sequence diagram represents one specific flow or scenario, not every possible scenario. Create a diagram for the main flow, such as "place an order". Avoid putting all error cases into the same diagram, because it quickly becomes difficult to read and understand.

## Diagrams as Text

Drawing class boxes and relationships by hand can take time, and manually created diagrams can become outdated as the code changes. Because of this, many teams write diagrams as text using a simple language called **Mermaid**. A tool can then automatically generate the diagram from that text.

Here is a small QuickBite class diagram written as Mermaid text. Each line of text creates either a box or a relationship:

| Line of text | What it draws |
|---|---|
| `classDiagram` | Starts a class diagram |
| `class Restaurant` | A box named Restaurant (the same for Menu, MenuItem and Order) |
| `Restaurant *-- Menu : owns` | A filled diamond: Restaurant owns Menu |
| `Menu --> MenuItem : holds many` | An arrow: Menu knows its items |
| `Order --> Restaurant : uses` | An arrow: Order knows its restaurant |

A tool turns those eight lines into this picture:

<LldDiagram caption="The diagram drawn from the eight lines of Mermaid text" code={"classDiagram\n  class Restaurant\n  class Menu\n  class MenuItem\n  class Order\n  Restaurant *-- Menu : owns\n  Menu --> MenuItem : holds many\n  Order --> Restaurant : uses"} />

Change one line of text, and the picture changes with it.

### The Class Diagram Lines in Mermaid

A class diagram starts with the keyword `classDiagram`. After that, each relationship is written on a separate line using a class name, a relationship symbol, and another class name.

| You want | Write | Read it as |
|---|---|---|
| Inheritance | `Delivery <\|-- ExpressDelivery` | ExpressDelivery is a type of Delivery |
| Implements | `PaymentMethod <\|.. Card` | Card implements PaymentMethod |
| Composition | `Restaurant *-- Menu` | Restaurant creates and owns Menu |
| Association | `Order --> Restaurant` | Order has a reference to Restaurant |
| Dependency | `Checkout ..> Coupon` | Checkout uses a Coupon temporarily |
| An interface | `<<interface>> PaymentMethod` | PaymentMethod is an interface |

The special shape (triangle or diamond) is always placed next to the **parent**, the **interface**, or the **owner**.

### The Sequence Diagram Arrows in Mermaid

A sequence diagram starts with `sequenceDiagram`. Each arrow is written on its own line in the same order that the actions occur.

| You want | Write |
|---|---|
| A box for each class | `participant OrderService` |
| A method call | `Customer->>OrderService: placeOrder()` |
| A response coming back | `OrderService-->>Customer: order id 1` |

Two dashes create a dashed arrow. Because of that, `-->>` represents a response, while `->>` represents a method call.

## Draw It Yourself: Diagram From Code

Now it is your turn to read the code and create its diagram. Type the Mermaid diagram text on the right side. The diagram updates automatically as you type. When you are done, click **Check diagram**.

The checker compares your classes and relationships with the code. If there is a mistake, it tells you which pair of classes to review and explains the reason.

### How to Choose the Line

For every pair of classes, ask these four questions in this order:

1. Does one class use `extends` or `implements` with another class? If yes, use a triangle relationship.
2. Does one class **create** another class using `new`, keep it inside itself, and own it? If yes, use composition, `*--`.
3. Does one class **store** an object that was passed to it in a field? If yes, use association, `-->`.
4. Does one class use another class only as a **method parameter**? If yes, use dependency, `..>`.

### Fix a Wrong Diagram

Someone created this diagram too quickly, and it contains three mistakes. Compare the diagram with the code and correct those mistakes.

### Draw a Sequence Diagram From Code

Follow the `cancelOrder(42)` method step by step. Each call made to another object becomes a solid arrow. Each value returned back becomes a dashed arrow.

Having trouble with a sequence diagram? Start at the first line of the method and read downward. Whenever you see `this.something.method()`, draw a solid arrow. Whenever a `return` statement sends a value back to the caller, draw a dashed arrow.

## Lab: Code From Diagram

Now you will work in the opposite direction. A diagram is provided, and you need to write the code that matches it. Read each relationship carefully before you begin.

Convert the diagram into code. The class names and method names are already provided. Your task is to complete the method implementations.

1. `Menu` stores a private list of `MenuItem` objects. The `priceOf` method should throw an error if the requested dish is not found on the menu.
2. `Restaurant` owns its `Menu`. Create the menu inside the restaurant class (composition).
3. `Order` receives a `Restaurant` through its constructor (association). The `total()` method should calculate the total by multiplying the price by the quantity and adding the results.

Run the tests after completing each step. The lab automatically creates a class diagram from your code on the right side. When you finish, the generated diagram should match the diagram shown above.

## Predict the Output

Here is a composition example in C#. Read the code and try to predict what it prints before running it.

<CsPredict id="uml-1" code={"var a = new Restaurant();\nvar b = new Restaurant();\na.Menu.Add(\"Idli\");\na.Menu.Add(\"Vada\");\nb.Menu.Add(\"Dosa\");\nConsole.WriteLine(a.Menu.Count);\nConsole.WriteLine(b.Menu.Count);\n\nclass Restaurant\n{\n    public List<string> Menu { get; } = new List<string>();\n}"} expected={"2\n1"} why="Each Restaurant object creates its own List. This is an example of composition because every restaurant owns a separate menu. Restaurant a has 2 dishes, while restaurant b has 1 dish." />

## Common Mistakes

| Mistake | Wrong | Correct | Why it happens |
|---|---|---|---|
| Using diamonds everywhere | A diamond from `Order` to `Restaurant`. | Use a simple arrow because the order only knows about the restaurant. | The diamond looks important, so people often use it for every relationship. |
| Sharing an "owned" object | `static menu` shared by all restaurants. | Each restaurant creates its own `new Menu()`. | A static field is quick to create and may appear to work in simple examples. |
| Wrong arrow direction | The triangle points to the child class. | The triangle should point to the parent class. | People often think of a family tree and naturally draw the arrow in the wrong direction. |
| Putting too much in one diagram | Showing every class in the application in a single diagram. | Create a separate diagram for each problem or question. | It feels more complete, but it becomes difficult to read. |
| Diagram and code do not match | The diagram still shows a class that no longer exists. | Keep the diagram as text and update it along with the code. | Images stored in documents or slides are easy to forget and become outdated. |

## Practice

## What to Remember

- A class box has three sections: class name, fields, and methods. `-` means private, and `+` means public.
- A **triangle** means "is a type of". A **diamond** means "owns". An **arrow** means "knows about". A **dashed line** means a weaker relationship.
- **Multiplicity** values show how many objects can take part in a relationship.
- A **sequence diagram** is read from top to bottom, with each arrow representing a call.
- Writing diagrams as text using Mermaid helps keep diagrams updated and synchronized with the code.

## Further Reading

These are the sources used to check this lesson:

- [Mermaid: Class diagrams](https://mermaid.js.org/syntax/classDiagram.html): the text syntax used by the lab's diagram.
- [Mermaid: Sequence diagrams](https://mermaid.js.org/syntax/sequenceDiagram.html): writing sequence diagrams as text.
- [Visual Paradigm: UML class diagram tutorial](https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-class-diagram-tutorial/): relationships and multiplicity.
- [TypeScript Handbook: Classes](https://www.typescriptlang.org/docs/handbook/2/classes.html): classes, interfaces and parameter properties.

Next: **Creational Patterns**. You will learn better ways to create objects than writing `new` everywhere.
