Software Design Diagrams
This document walks through the design of Harborlight, a small order-and-payment service. It is written as ordinary reading material: prose sections, then a diagram, then more prose. Each diagram is a standard Mermaid fenced block that NoteThink renders inline.
Authentication flow
When a customer signs in, Harborlight delegates identity to an external OAuth provider and exchanges the returned authorization code for tokens before establishing a session. The sequence below traces a single sign-in, including the path where the provider rejects the code.
sequenceDiagram
participant U as User
participant W as Web App
participant A as Auth Service
participant P as OAuth Provider
U->>W: Click Sign in
W->>P: Redirect to authorize endpoint
P-->>U: Present consent screen
U->>P: Approve and return code
P-->>W: Redirect with auth code
W->>A: Exchange code for tokens
activate A
A->>P: POST /token (code, client secret)
alt Code valid
P-->>A: Access and refresh tokens
A-->>W: Session established
W-->>U: Show dashboard
else Code expired or reused
P-->>A: 400 invalid_grant
A-->>W: Authentication failed
W-->>U: Show retry prompt
end
deactivate A
Note over A,P: Tokens are cached server-side. The browser never sees the refresh token.
Notice that the refresh token stays on the server. The browser only ever holds a short-lived session reference, which limits the blast radius if a client device is compromised.
Data model
Harborlight's persistence layer is a relational schema. A customer places orders; each order contains line items that reference catalogue products, and each order has at most one payment record. The entity-relationship diagram captures the cardinalities and the primary/foreign keys.
erDiagram
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ ADDRESS : has
ORDER ||--|{ LINE_ITEM : contains
ORDER ||--o| PAYMENT : settled_by
PRODUCT ||--o{ LINE_ITEM : "appears in"
CATEGORY ||--o{ PRODUCT : groups
CUSTOMER {
int id PK
string email
string display_name
datetime created_at
}
ADDRESS {
int id PK
int customer_id FK
string line1
string postcode
string country
}
ORDER {
int id PK
int customer_id FK
string status
decimal total
datetime placed_at
}
LINE_ITEM {
int id PK
int order_id FK
int product_id FK
int quantity
decimal unit_price
}
PRODUCT {
int id PK
int category_id FK
string sku
string name
decimal list_price
}
CATEGORY {
int id PK
string name
}
PAYMENT {
int id PK
int order_id FK
string provider_ref
string status
decimal amount
}
The LINE_ITEM table snapshots unit_price at purchase time rather than reading PRODUCT.list_price live, so historical orders remain accurate after price changes.
Domain classes
The service layer models the same domain in code. A base Account is specialised into Customer and Administrator. An Order is composed of LineItem objects and associates with a PaymentGateway that performs the actual charge.
classDiagram
class Account {
+int id
+string email
+login(password) bool
+logout() void
}
class Customer {
+string displayName
+placeOrder(cart) Order
}
class Administrator {
+string role
+refund(order) bool
}
class Order {
+int id
+string status
+decimal total
+addItem(item) void
+checkout() bool
}
class LineItem {
+int quantity
+decimal unitPrice
+subtotal() decimal
}
class PaymentGateway {
+charge(amount) string
+reverse(ref) bool
}
Account <|-- Customer
Account <|-- Administrator
Order *-- LineItem
Customer --> Order : places
Order --> PaymentGateway : settles via
This keeps authorization logic on Account, so both customer-facing and admin flows inherit a single, audited login path while differing only in their domain capabilities.
Demonstrates NoteThink's Mermaid support for software design - sequence, entity-relationship, and class diagrams rendered inline from your markdown.