Worked example — an aggregate as a Mermaid class diagram.
Mermaid's classDiagram lets you draw the root + its internals + the by-id reference to another aggregate, all in one block:
classDiagram
class Order {
+OrderId id
+CustomerId customer_id
+addLine(product_id, qty)
+confirm()
}
class OrderLine {
+ProductId product_id
+int qty
+Money line_total
}
Order *-- "1..*" OrderLine : contains
Order ..> CustomerId : by id only
Reading it:
Order is the aggregate root — every operation that mutates an order enters through addLine() / confirm() so invariants stay enforceable.
*-- is composition: an OrderLine cannot exist without its Order. No repository for OrderLine.
..> is a dependency on the CustomerId value object — Order references the Customer aggregate by id only, never by holding the actual Customer object.
In the playground below, extend this skeleton with one more invariant of your choosing (e.g. a totalAmount() method on Order).