Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa05195958 | ||
|
|
a4b1c55f26 | ||
|
|
e9485e4f9d | ||
|
|
d0ef5472e5 | ||
|
|
7fd34fcce0 | ||
|
|
f4663c089d | ||
|
|
2ab4287f02 |
No files matched your search
@@ -1,5 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.11.3 — 2026-11-09
|
||||||
|
|
||||||
|
- README: a link to the guides and walkthroughs on Medium.
|
||||||
|
|
||||||
|
## 0.11.2 — 2026-11-08
|
||||||
|
|
||||||
|
- The ArchiMate examples' layered views put the labels of the arrows between layers in the gap between the bands, no longer on the band titles.
|
||||||
|
|
||||||
|
## 0.11.1 — 2026-11-08
|
||||||
|
|
||||||
|
- **The arc42 examples cover all twelve sections.** Each overview carries cards for 1.1 Requirements, 1.2 Quality goals, 1.3 Stakeholders, 2 Constraints, 4 Solution strategy, 11 Risks and technical debt and 12 Glossary; the technical context lists the domain interfaces (3); every building block has a black-box description, responsibility and interfaces (5); the deployment view places the building blocks on their nodes with a mapping (7); level 1 carries the crosscutting concepts (8); a new `1000-quality` folder draws the quality tree with its scenarios (10). Each model's description says where each section lives.
|
||||||
|
|
||||||
## 0.11.0 — 2026-11-08
|
## 0.11.0 — 2026-11-08
|
||||||
|
|
||||||
- **arc42 and ArchiMate examples.** *Archify | New Model…* now offers nine examples, three per framework. **arc42** (Cinema Tickets, IoT Telemetry, Team Wiki): the overview is the business context with quality goals, constraints, quality scenarios, risks and a glossary as cards; then `0300-context`, `0500-building-blocks` (levels 1 and 2), `0600-runtime` (sequence diagrams), `0700-deployment`, and the decisions as section 9. **ArchiMate** (Insurance Claims, HR Onboarding, Logistics Tracking): the overview is a layered view of the business, application and technology layers; then `0100-business` (with the business process as a workflow), `0200-application` and `0300-technology`, one registry file per layer, each element's ArchiMate type under its name.
|
- **arc42 and ArchiMate examples.** *Archify | New Model…* now offers nine examples, three per framework. **arc42** (Cinema Tickets, IoT Telemetry, Team Wiki): the overview is the business context with quality goals, constraints, quality scenarios, risks and a glossary as cards; then `0300-context`, `0500-building-blocks` (levels 1 and 2), `0600-runtime` (sequence diagrams), `0700-deployment`, and the decisions as section 9. **ArchiMate** (Insurance Claims, HR Onboarding, Logistics Tracking): the overview is a layered view of the business, application and technology layers; then `0100-business` (with the business process as a workflow), `0200-application` and `0300-technology`, one registry file per layer, each element's ArchiMate type under its name.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ Create and view [Archify](https://github.com/tt-a1i/archify) diagrams inside VS
|
|||||||
|
|
||||||
This is an unofficial extension. It bundles the Archify renderer (MIT, © tt-a1i) and runs it on VS Code's own Node runtime, so nothing else needs to be installed.
|
This is an unofficial extension. It bundles the Archify renderer (MIT, © tt-a1i) and runs it on VS Code's own Node runtime, so nothing else needs to be installed.
|
||||||
|
|
||||||
|
Guides and walkthroughs, one per feature (models, decisions, graph, Source view, 3D, Sync, the arc42 and ArchiMate examples): [articles on Medium](https://medium.com/@root.at.skic).
|
||||||
|
|
||||||
## Supported files
|
## Supported files
|
||||||
|
|
||||||
| File | Format | What the extension does |
|
| File | Format | What the extension does |
|
||||||
@@ -126,7 +128,7 @@ online-shop/
|
|||||||
- **Decisions.** One `*.archify-decision` file per architecture decision: id, title, status, date, the registry elements and diagrams it is about, and prose for context, decision and consequences. Diagrams point at decisions with `decisions: [<id>]` on the diagram, a component or a connection. They show as a **Decisions** panel on the `main.archify` page, a **Decisions** rail panel under each diagram (hover lights up what the decision is attached to), on element cards and in the element dialog, and a decision opens as its own page. *Archify | New Decision…* creates one; links are checked in Problems.
|
- **Decisions.** One `*.archify-decision` file per architecture decision: id, title, status, date, the registry elements and diagrams it is about, and prose for context, decision and consequences. Diagrams point at decisions with `decisions: [<id>]` on the diagram, a component or a connection. They show as a **Decisions** panel on the `main.archify` page, a **Decisions** rail panel under each diagram (hover lights up what the decision is attached to), on element cards and in the element dialog, and a decision opens as its own page. *Archify | New Decision…* creates one; links are checked in Problems.
|
||||||
- **Registry view.** A `.archify-registry` file opens as its elements, as cards. Click one and a dialog shows its description, where it is defined and the diagrams that use it; click a diagram to open it. **View | Source** at the top right switches to the YAML, checked against the registry schema as you type.
|
- **Registry view.** A `.archify-registry` file opens as its elements, as cards. Click one and a dialog shows its description, where it is defined and the diagrams that use it; click a diagram to open it. **View | Source** at the top right switches to the YAML, checked against the registry schema as you type.
|
||||||
- **Elements and Connectors.** Under every diagram, Archify's node index is titled **Elements**, and a **Connectors** panel next to it lists every connection as "from → to" with its label. Hovering highlights the connector in the diagram; clicking keeps it highlighted and scrolls to it.
|
- **Elements and Connectors.** Under every diagram, Archify's node index is titled **Elements**, and a **Connectors** panel next to it lists every connection as "from → to" with its label. Hovering highlights the connector in the diagram; clicking keeps it highlighted and scrolls to it.
|
||||||
- **Starting one.** *Archify | New Model…* (right-click a folder) copies one of the shipped examples into a new folder, named as you choose: **C4 - Standard Web App** (single-page app, API, database, cache, storage, identity provider), **C4 - E-Shop** (web shop, API, worker, queue, payment and email providers) or **C4 - Event-Based Integration** (event bus, router, dead-letter queue, event store, consumers), each with context, container and component diagrams. **arc42 - Cinema Tickets**, **arc42 - IoT Telemetry** and **arc42 - Team Wiki** follow arc42's sections: the overview is the business context with goals, constraints, quality scenarios, risks and a glossary as cards, then `0300-context`, `0500-building-blocks`, `0600-runtime` (sequence diagrams) and `0700-deployment`, with section 9 as decisions. **ArchiMate - Insurance Claims**, **ArchiMate - HR Onboarding** and **ArchiMate - Logistics Tracking** are layered: the overview is a layered view, then `0100-business`, `0200-application` and `0300-technology`, one registry file per layer, each element's ArchiMate type as its tag. Every example has a registry and decisions. For an empty model, *Archify | New Diagram… → Model* creates just a `main.archify`.
|
- **Starting one.** *Archify | New Model…* (right-click a folder) copies one of the shipped examples into a new folder, named as you choose: **C4 - Standard Web App** (single-page app, API, database, cache, storage, identity provider), **C4 - E-Shop** (web shop, API, worker, queue, payment and email providers) or **C4 - Event-Based Integration** (event bus, router, dead-letter queue, event store, consumers), each with context, container and component diagrams. **arc42 - Cinema Tickets**, **arc42 - IoT Telemetry** and **arc42 - Team Wiki** cover all twelve arc42 sections: the overview is the business context (3) with requirements, quality goals, stakeholders, constraints, solution strategy, risks and technical debt, and a glossary as numbered cards; then `0300-context` (domain interfaces), `0500-building-blocks` (black boxes, crosscutting concepts), `0600-runtime` (sequence diagrams), `0700-deployment` (blocks mapped to nodes) and `1000-quality` (quality tree), with section 9 as decisions. **ArchiMate - Insurance Claims**, **ArchiMate - HR Onboarding** and **ArchiMate - Logistics Tracking** are layered: the overview is a layered view, then `0100-business`, `0200-application` and `0300-technology`, one registry file per layer, each element's ArchiMate type as its tag. Every example has a registry and decisions. For an empty model, *Archify | New Diagram… → Model* creates just a `main.archify`.
|
||||||
- **The graph.** The **Model | Graph | Source** switch at the top of the `main.archify` page (next to **Export** when the file has a diagram) replaces the page with the whole model as one graph: every element any diagram draws plus every registry element, every connection between them, each once. Registry elements have a solid border, inline ones a dashed border; colours are Archify's type colours and the legend chips at the bottom hide or show a type. **Decisions** are nodes too (tags coloured by status, dotted links to the elements they are about, dashed links to what they supersede), and so are **registries** (one node per file, dotted links to the elements it defines); their chips hide them when the picture gets busy. The layout is **Layered** by default, like Archify's stack: clients on the left, data on the right, registries in a row above, decisions in a row below, connections drawn orthogonally; click the button for a **Force** layout instead. Drag nodes, scroll to zoom, **Fit** (or <kbd>0</kbd>) frames what matches, **Labels** toggles connection labels, the dotted **Links** from decisions and registries to their elements are on by default and can be switched off, in which case only the selected element's show (hovering an element lights up its connections), the sun/moon button switches the page between its dark and light theme (remembered, shared with the diagram pages' theme choice), **Export PNG** saves the picture. On a diagram page the switch reads **Diagram | Graph | Source** and Graph opens the graph already narrowed to that diagram; **Source** on the graph page goes back to `main.archify` in its Source view. *Archify | Open Model Graph* does the same from the Command Palette or Explorer.
|
- **The graph.** The **Model | Graph | Source** switch at the top of the `main.archify` page (next to **Export** when the file has a diagram) replaces the page with the whole model as one graph: every element any diagram draws plus every registry element, every connection between them, each once. Registry elements have a solid border, inline ones a dashed border; colours are Archify's type colours and the legend chips at the bottom hide or show a type. **Decisions** are nodes too (tags coloured by status, dotted links to the elements they are about, dashed links to what they supersede), and so are **registries** (one node per file, dotted links to the elements it defines); their chips hide them when the picture gets busy. The layout is **Layered** by default, like Archify's stack: clients on the left, data on the right, registries in a row above, decisions in a row below, connections drawn orthogonally; click the button for a **Force** layout instead. Drag nodes, scroll to zoom, **Fit** (or <kbd>0</kbd>) frames what matches, **Labels** toggles connection labels, the dotted **Links** from decisions and registries to their elements are on by default and can be switched off, in which case only the selected element's show (hovering an element lights up its connections), the sun/moon button switches the page between its dark and light theme (remembered, shared with the diagram pages' theme choice), **Export PNG** saves the picture. On a diagram page the switch reads **Diagram | Graph | Source** and Graph opens the graph already narrowed to that diagram; **Source** on the graph page goes back to `main.archify` in its Source view. *Archify | Open Model Graph* does the same from the Command Palette or Explorer.
|
||||||
- **Queries.** Type a few words in the box (<kbd>/</kbd> focuses it); matches stay bright, the rest dims, the count shows "9 of 14 elements". Terms combine with AND and a leading `-` excludes: `database` (id, label, sublabel, brand or type contain it), `type:backend`, `diagram:0200-*` (what those diagrams draw), `decision:<id>` (what a decision is about or is attached to), `uses:api` (an element and its neighbours), `near:api,2` (within two steps), `from:web to:db` (the shortest path, following the arrows first), `unused` (registry elements no diagram draws), `shared` and `-shared` (registry or inline elements only), `type:decision` and `type:registry` for the decision and registry nodes. Decisions and registries follow the elements they are attached to into a result; `-type:decision` or `-type:registry` leaves them out. Walks and paths follow the diagrams' connections only, so a registry is never a shortcut between its elements. The **?** button lists them; clicking an example runs it.
|
- **Queries.** Type a few words in the box (<kbd>/</kbd> focuses it); matches stay bright, the rest dims, the count shows "9 of 14 elements". Terms combine with AND and a leading `-` excludes: `database` (id, label, sublabel, brand or type contain it), `type:backend`, `diagram:0200-*` (what those diagrams draw), `decision:<id>` (what a decision is about or is attached to), `uses:api` (an element and its neighbours), `near:api,2` (within two steps), `from:web to:db` (the shortest path, following the arrows first), `unused` (registry elements no diagram draws), `shared` and `-shared` (registry or inline elements only), `type:decision` and `type:registry` for the decision and registry nodes. Decisions and registries follow the elements they are attached to into a result; `-type:decision` or `-type:registry` leaves them out. Walks and paths follow the diagrams' connections only, so a registry is never a shortcut between its elements. The **?** button lists them; clicking an example runs it.
|
||||||
- **Side panel.** Click an element for its description, type, where it is defined (**Registry** opens the file at the line), the diagrams that draw it (click to open), its connections and its decisions (click to open). **Neighbours** runs `uses:` for it. Click a connection for its labels, the diagrams that draw it and its decisions. Click a decision node for its status, context and decision text, the elements and connections it is about, and **Open decision**; a registry node for the elements it defines, the ones no diagram draws yet, and **Open registry**.
|
- **Side panel.** Click an element for its description, type, where it is defined (**Registry** opens the file at the line), the diagrams that draw it (click to open), its connections and its decisions (click to open). **Neighbours** runs `uses:` for it. Click a connection for its labels, the diagrams that draw it and its decisions. Click a decision node for its status, context and decision text, the elements and connections it is about, and **Open decision**; a registry node for the elements it defines, the ones no diagram draws yet, and **Open registry**.
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "archify-vscode-ext",
|
"name": "archify-vscode-ext",
|
||||||
"version": "0.11.0",
|
"version": "0.11.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "archify-vscode-ext",
|
"name": "archify-vscode-ext",
|
||||||
"version": "0.11.0",
|
"version": "0.11.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.20.4",
|
"@codemirror/autocomplete": "^6.20.4",
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"name": "archify-vscode-ext",
|
"name": "archify-vscode-ext",
|
||||||
"displayName": "Archify Diagram Viewer",
|
"displayName": "Archify Diagram Viewer",
|
||||||
"description": "Archify diagrams in VS Code. Write diagrams as .archify files in YAML: they open as the interactive diagram, with live updates, validation, templates and completion. Also previews Archify's own JSON files (*.architecture.json, …) and opens rendered Archify HTML.",
|
"description": "Archify diagrams in VS Code. Write diagrams as .archify files in YAML: they open as the interactive diagram, with live updates, validation, templates and completion. Also previews Archify's own JSON files (*.architecture.json, …) and opens rendered Archify HTML.",
|
||||||
"version": "0.11.0",
|
"version": "0.11.3",
|
||||||
"publisher": "root-at-skic",
|
"publisher": "root-at-skic",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sponsor": {
|
"sponsor": {
|
||||||
|
|||||||
@@ -28,3 +28,12 @@ connections:
|
|||||||
- { from: tickets, to: psp, label: REST webhook, variant: security }
|
- { from: tickets, to: psp, label: REST webhook, variant: security }
|
||||||
- { from: tickets, to: mail, label: SMTP TLS }
|
- { from: tickets, to: mail, label: SMTP TLS }
|
||||||
- { from: tickets, to: pos, label: REST VPN }
|
- { from: tickets, to: pos, label: REST VPN }
|
||||||
|
cards:
|
||||||
|
- dot: cyan
|
||||||
|
title: 3 Domain interfaces
|
||||||
|
items:
|
||||||
|
- "Moviegoer: in shows, seats, payment start; out seat map, QR tickets"
|
||||||
|
- "Usher: in scanned QR codes; out valid or invalid, seat"
|
||||||
|
- "Payment provider: out checkout session; in paid or failed webhook"
|
||||||
|
- "E-mail service: out ticket e-mails with QR and receipt"
|
||||||
|
- "Box-office POS: in counter sales; out seat availability"
|
||||||
@@ -58,7 +58,9 @@ cards:
|
|||||||
- dot: amber
|
- dot: amber
|
||||||
title: 8 Crosscutting concepts
|
title: 8 Crosscutting concepts
|
||||||
items:
|
items:
|
||||||
|
- "Persistence: PostgreSQL is the truth; Redis only for holds"
|
||||||
|
- "Security: OIDC for staff apps; card data never stored"
|
||||||
|
- "Errors: idempotency keys; failed webhooks are retried"
|
||||||
- "Times in UTC; shown in cinema local time"
|
- "Times in UTC; shown in cinema local time"
|
||||||
- "Every write carries an idempotency key"
|
|
||||||
- "QR payload signed with an Ed25519 key"
|
- "QR payload signed with an Ed25519 key"
|
||||||
- "Structured logs with order id as trace key"
|
- "Structured logs with order id as trace key"
|
||||||
@@ -5,7 +5,10 @@ meta:
|
|||||||
output: production.html
|
output: production.html
|
||||||
components:
|
components:
|
||||||
- ref: customer
|
- ref: customer
|
||||||
pos: [40, 160]
|
pos: [40, 260]
|
||||||
|
size: [130, 60]
|
||||||
|
- ref: mobile
|
||||||
|
pos: [40, 60]
|
||||||
size: [130, 60]
|
size: [130, 60]
|
||||||
- id: ingress
|
- id: ingress
|
||||||
type: security
|
type: security
|
||||||
@@ -44,6 +47,9 @@ boundaries:
|
|||||||
- kind: security-group
|
- kind: security-group
|
||||||
label: Managed data zone
|
label: Managed data zone
|
||||||
wraps: [seatlock, db]
|
wraps: [seatlock, db]
|
||||||
|
- kind: region
|
||||||
|
label: Customer phones
|
||||||
|
wraps: [mobile]
|
||||||
- kind: region
|
- kind: region
|
||||||
label: Cinema site
|
label: Cinema site
|
||||||
wraps: [pos, scanner]
|
wraps: [pos, scanner]
|
||||||
@@ -56,3 +62,13 @@ connections:
|
|||||||
- { from: ticketing, to: db, label: TLS }
|
- { from: ticketing, to: db, label: TLS }
|
||||||
- { from: possync, to: pos, label: VPN, variant: security }
|
- { from: possync, to: pos, label: VPN, variant: security }
|
||||||
- { from: scanner, to: ingress, label: HTTPS }
|
- { from: scanner, to: ingress, label: HTTPS }
|
||||||
|
- { from: mobile, to: ingress, label: HTTPS }
|
||||||
|
cards:
|
||||||
|
- dot: slate
|
||||||
|
title: 7 Mapping
|
||||||
|
items:
|
||||||
|
- "Web Shop, Booking API, Ticket Service, POS Sync -> EU k8s cluster"
|
||||||
|
- "Seat Locks -> managed Redis; Tickets DB -> managed PostgreSQL"
|
||||||
|
- "Mobile App -> customer phones"
|
||||||
|
- "Scanner App -> door phones at the cinema site"
|
||||||
|
- "Box-office POS -> cinema site, VPN to POS Sync"
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
schema_version: 1
|
||||||
|
diagram_type: architecture
|
||||||
|
meta:
|
||||||
|
title: Quality tree
|
||||||
|
output: quality-tree.html
|
||||||
|
components:
|
||||||
|
- id: quality
|
||||||
|
type: cloud
|
||||||
|
label: Quality
|
||||||
|
pos: [40, 260]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: reliability
|
||||||
|
type: backend
|
||||||
|
label: Reliability
|
||||||
|
pos: [250, 60]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: performance
|
||||||
|
type: backend
|
||||||
|
label: Performance
|
||||||
|
pos: [250, 210]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: availability
|
||||||
|
type: backend
|
||||||
|
label: Availability
|
||||||
|
pos: [250, 360]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: security
|
||||||
|
type: security
|
||||||
|
label: Security
|
||||||
|
pos: [250, 460]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: s-nodouble
|
||||||
|
type: external
|
||||||
|
label: No double sale
|
||||||
|
pos: [460, 10]
|
||||||
|
size: [150, 60]
|
||||||
|
- id: s-payfail
|
||||||
|
type: external
|
||||||
|
label: Failed pay frees seats
|
||||||
|
pos: [460, 110]
|
||||||
|
size: [170, 60]
|
||||||
|
- id: s-peak
|
||||||
|
type: external
|
||||||
|
label: Checkout < 2 s at peak
|
||||||
|
pos: [460, 210]
|
||||||
|
size: [170, 60]
|
||||||
|
- id: s-offline
|
||||||
|
type: external
|
||||||
|
label: Offline door scans
|
||||||
|
pos: [460, 360]
|
||||||
|
size: [170, 60]
|
||||||
|
- id: s-forged
|
||||||
|
type: external
|
||||||
|
label: Forged QR rejected
|
||||||
|
pos: [460, 460]
|
||||||
|
size: [170, 60]
|
||||||
|
connections:
|
||||||
|
- { from: quality, to: reliability }
|
||||||
|
- { from: quality, to: performance }
|
||||||
|
- { from: quality, to: availability }
|
||||||
|
- { from: quality, to: security }
|
||||||
|
- { from: reliability, to: s-nodouble }
|
||||||
|
- { from: reliability, to: s-payfail }
|
||||||
|
- { from: performance, to: s-peak }
|
||||||
|
- { from: availability, to: s-offline }
|
||||||
|
- { from: security, to: s-forged }
|
||||||
|
cards:
|
||||||
|
- dot: emerald
|
||||||
|
title: 10 Quality scenarios
|
||||||
|
items:
|
||||||
|
- "500 holds on one show in 1 min: no seat sold twice"
|
||||||
|
- "Payment fails or is abandoned: seats free again within 8 min"
|
||||||
|
- "Friday 19:00 peak, 300 checkouts/min: p95 under 2 s"
|
||||||
|
- "Wi-Fi down at the door: scans go on, check-ins sync later"
|
||||||
|
- "Edited or forged QR code scanned: rejected at once"
|
||||||
@@ -3,13 +3,14 @@ model:
|
|||||||
id: cinema-tickets
|
id: cinema-tickets
|
||||||
name: Cinema Tickets
|
name: Cinema Tickets
|
||||||
description: >
|
description: >
|
||||||
An arc42 example: online and mobile seat booking for a small
|
An arc42 example: seat booking for the fictional Lantern Cinemas.
|
||||||
cinema chain. This overview is section 3 (business context) with
|
1 Requirements, quality goals, stakeholders; 2 Constraints;
|
||||||
cards for sections 1, 2, 10, 11 and 12. 0300-context holds the
|
4 Solution strategy; 11 Risks; 12 Glossary: cards on this overview.
|
||||||
technical context (3), 0500-building-blocks the building block
|
3 Context: this overview (business) and 0300-context (technical).
|
||||||
view (5), 0600-runtime the runtime view (6), 0700-deployment the
|
5 Building blocks: 0500-building-blocks, black boxes in registry/.
|
||||||
deployment view (7), decisions/ the architecture decisions (9).
|
6 Runtime: 0600-runtime. 7 Deployment: 0700-deployment.
|
||||||
Crosscutting concepts (8) are a card on the level-1 diagram.
|
8 Crosscutting concepts: card on 0500-building-blocks/level-1.
|
||||||
|
9 Decisions: decisions/. 10 Quality: 1000-quality.
|
||||||
diagram_type: architecture
|
diagram_type: architecture
|
||||||
meta:
|
meta:
|
||||||
title: Cinema Tickets - business context
|
title: Cinema Tickets - business context
|
||||||
@@ -40,12 +41,26 @@ connections:
|
|||||||
- { from: tickets, to: mail, label: tickets }
|
- { from: tickets, to: mail, label: tickets }
|
||||||
- { from: tickets, to: pos, label: seat sales }
|
- { from: tickets, to: pos, label: seat sales }
|
||||||
cards:
|
cards:
|
||||||
|
- dot: orange
|
||||||
|
title: 1.1 Requirements
|
||||||
|
items:
|
||||||
|
- "Browse shows and pick seats on a seat map"
|
||||||
|
- "Hold seats, pay online, get QR tickets by e-mail"
|
||||||
|
- "Ushers check tickets in at the door"
|
||||||
|
- "Box-office POS sells from the same seats"
|
||||||
- dot: cyan
|
- dot: cyan
|
||||||
title: 1 Quality goals
|
title: 1.2 Quality goals
|
||||||
items:
|
items:
|
||||||
- "No seat is ever sold twice"
|
- "No seat is ever sold twice"
|
||||||
- "Checkout under 2 s at Friday peak"
|
- "Checkout under 2 s at Friday peak"
|
||||||
- "Door check-in works offline"
|
- "Door check-in works offline"
|
||||||
|
- dot: amber
|
||||||
|
title: 1.3 Stakeholders
|
||||||
|
items:
|
||||||
|
- "Moviegoer: quick booking, tickets on the phone"
|
||||||
|
- "Usher: fast scans even without Wi-Fi"
|
||||||
|
- "Box-office staff: one seat map with online sales"
|
||||||
|
- "Lantern Cinemas management: fewer refunds, low cost"
|
||||||
- dot: slate
|
- dot: slate
|
||||||
title: 2 Constraints
|
title: 2 Constraints
|
||||||
items:
|
items:
|
||||||
@@ -53,16 +68,20 @@ cards:
|
|||||||
- "Card data only at the provider"
|
- "Card data only at the provider"
|
||||||
- "Team of four; hosted in the EU"
|
- "Team of four; hosted in the EU"
|
||||||
- dot: emerald
|
- dot: emerald
|
||||||
title: 10 Quality scenarios
|
title: 4 Solution strategy
|
||||||
items:
|
items:
|
||||||
- "500 holds on one show in 1 min: no double sale"
|
- "Node.js, PostgreSQL, Redis on managed EU k8s: fits a team of four"
|
||||||
- "Wi-Fi down at the door: scans go on, sync later"
|
- "Three services by job: booking, tickets, POS sync"
|
||||||
- "Payment fails: seats free again within 8 min"
|
- "No double sale: Booking API sole seat owner (booking-owns-seat-inventory)"
|
||||||
|
- "Checkout speed: Redis holds with TTL (seat-holds-with-ttl)"
|
||||||
|
- "Offline check-in: signed QR codes (signed-qr-codes)"
|
||||||
- dot: rose
|
- dot: rose
|
||||||
title: 11 Risks
|
title: 11 Risks and technical debt
|
||||||
items:
|
items:
|
||||||
- "POS vendor API is slow and undocumented"
|
- "Risk: POS vendor API is slow and undocumented"
|
||||||
- "Forwarded QR e-mails: first scan wins"
|
- "Risk: forwarded QR e-mails, first scan wins"
|
||||||
|
- "Debt: POS Sync adapter has no automated tests"
|
||||||
|
- "Debt: QR key rotation needs an app release"
|
||||||
- dot: violet
|
- dot: violet
|
||||||
title: 12 Glossary
|
title: 12 Glossary
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -6,52 +6,67 @@ elements:
|
|||||||
sublabel: React
|
sublabel: React
|
||||||
brand: react
|
brand: react
|
||||||
description: >
|
description: >
|
||||||
Programme, seat map and checkout in the browser.
|
Responsibility: programme, seat map and checkout in the browser.
|
||||||
|
Interfaces: provides the web UI; requires Booking API (JSON)
|
||||||
|
and the provider's hosted checkout page.
|
||||||
mobile:
|
mobile:
|
||||||
type: frontend
|
type: frontend
|
||||||
label: Mobile App
|
label: Mobile App
|
||||||
sublabel: iOS and Android
|
sublabel: iOS and Android
|
||||||
description: >
|
description: >
|
||||||
The same booking flow as the web shop, plus a ticket wallet.
|
Responsibility: the web shop's booking flow plus a ticket wallet.
|
||||||
|
Interfaces: provides the iOS/Android UI; requires Booking API
|
||||||
|
(JSON).
|
||||||
scanner:
|
scanner:
|
||||||
type: frontend
|
type: frontend
|
||||||
label: Scanner App
|
label: Scanner App
|
||||||
sublabel: door phones
|
sublabel: door phones
|
||||||
description: >
|
description: >
|
||||||
Reads QR codes at the door; checks signatures offline.
|
Responsibility: reads QR codes at the door, checks signatures
|
||||||
|
offline. Interfaces: provides the usher UI; requires Ticket
|
||||||
|
Service (POST /checkins), queued while offline.
|
||||||
booking:
|
booking:
|
||||||
type: backend
|
type: backend
|
||||||
label: Booking API
|
label: Booking API
|
||||||
sublabel: Node.js
|
sublabel: Node.js
|
||||||
brand: node-js
|
brand: node-js
|
||||||
description: >
|
description: >
|
||||||
Shows, seat maps, seat holds and orders; the only writer of
|
Responsibility: shows, seat maps, holds and orders; the only
|
||||||
the seat inventory.
|
writer of the seat inventory. Interfaces: provides REST JSON to
|
||||||
|
the apps and the payment webhook; requires Seat Locks, Tickets
|
||||||
|
DB, payment provider, Ticket Service.
|
||||||
diagram: 0500-building-blocks/booking.archify
|
diagram: 0500-building-blocks/booking.archify
|
||||||
ticketing:
|
ticketing:
|
||||||
type: backend
|
type: backend
|
||||||
label: Ticket Service
|
label: Ticket Service
|
||||||
sublabel: QR, check-in
|
sublabel: QR, check-in
|
||||||
description: >
|
description: >
|
||||||
Issues signed QR tickets, sends them by e-mail and records
|
Responsibility: issues signed QR tickets, mails them and records
|
||||||
check-ins at the door.
|
check-ins. Interfaces: provides issue and POST /checkins;
|
||||||
|
requires Tickets DB and the e-mail service (SMTP).
|
||||||
possync:
|
possync:
|
||||||
type: backend
|
type: backend
|
||||||
label: POS Sync
|
label: POS Sync
|
||||||
sublabel: adapter
|
sublabel: adapter
|
||||||
description: >
|
description: >
|
||||||
Exchanges seat sales with the box-office POS in both directions.
|
Responsibility: exchanges seat sales with the box-office POS.
|
||||||
|
Interfaces: provides a REST adapter to the POS over VPN;
|
||||||
|
requires Tickets DB (SQL).
|
||||||
seatlock:
|
seatlock:
|
||||||
type: database
|
type: database
|
||||||
label: Seat Locks
|
label: Seat Locks
|
||||||
sublabel: Redis
|
sublabel: Redis
|
||||||
brand: redis
|
brand: redis
|
||||||
description: >
|
description: >
|
||||||
Short-lived seat holds with a TTL while the customer pays.
|
Responsibility: short-lived seat holds with a TTL while the
|
||||||
|
customer pays. Interfaces: provides Redis SET NX/GET/DEL to the
|
||||||
|
Booking API; requires nothing.
|
||||||
db:
|
db:
|
||||||
type: database
|
type: database
|
||||||
label: Tickets DB
|
label: Tickets DB
|
||||||
sublabel: PostgreSQL
|
sublabel: PostgreSQL
|
||||||
brand: postgresql
|
brand: postgresql
|
||||||
description: >
|
description: >
|
||||||
Shows, seats, orders and tickets; the source of truth.
|
Responsibility: shows, seats, orders and tickets; the source of
|
||||||
|
truth. Interfaces: provides SQL to Booking API, Ticket Service
|
||||||
|
and POS Sync; requires nothing.
|
||||||
@@ -33,3 +33,13 @@ connections:
|
|||||||
- { from: heat-pump, to: telemetry-platform, label: MQTT 8883, variant: emphasis, decisions: [mqtt-with-mutual-tls] }
|
- { from: heat-pump, to: telemetry-platform, label: MQTT 8883, variant: emphasis, decisions: [mqtt-with-mutual-tls] }
|
||||||
- { from: telemetry-platform, to: push-service, label: HTTP/2 }
|
- { from: telemetry-platform, to: push-service, label: HTTP/2 }
|
||||||
- { from: telemetry-platform, to: service-desk, label: webhook }
|
- { from: telemetry-platform, to: service-desk, label: webhook }
|
||||||
|
cards:
|
||||||
|
- dot: cyan
|
||||||
|
title: 3 Domain interfaces
|
||||||
|
items:
|
||||||
|
- "Heat pump: in telemetry and fault codes; out none"
|
||||||
|
- "Homeowner: in app requests; out status, history, alerts"
|
||||||
|
- "Ops engineer: in queries; out fleet dashboards"
|
||||||
|
- "Installer: in device serial; out device certificate"
|
||||||
|
- "Push service: out alert notifications"
|
||||||
|
- "Service desk: out tickets for faults needing a visit"
|
||||||
@@ -73,5 +73,8 @@ cards:
|
|||||||
title: 8 Crosscutting concepts
|
title: 8 Crosscutting concepts
|
||||||
items:
|
items:
|
||||||
- Device identity, one X.509 certificate per device
|
- Device identity, one X.509 certificate per device
|
||||||
|
- Security, OIDC login for people, mTLS for devices
|
||||||
|
- Persistence, PostgreSQL dialect for all stores
|
||||||
- Idempotent writes, keyed by device id and timestamp
|
- Idempotent writes, keyed by device id and timestamp
|
||||||
|
- Error handling, invalid readings to dead letters
|
||||||
- Observability, metrics and traces on every service
|
- Observability, metrics and traces on every service
|
||||||
@@ -29,6 +29,12 @@ components:
|
|||||||
- ref: device-registry
|
- ref: device-registry
|
||||||
pos: [760, 260]
|
pos: [760, 260]
|
||||||
size: [130, 60]
|
size: [130, 60]
|
||||||
|
- ref: ops-dashboard
|
||||||
|
pos: [1030, 160]
|
||||||
|
size: [130, 60]
|
||||||
|
- ref: customer-app
|
||||||
|
pos: [490, 440]
|
||||||
|
size: [130, 60]
|
||||||
boundaries:
|
boundaries:
|
||||||
- kind: region
|
- kind: region
|
||||||
label: Kubernetes cluster, eu-central
|
label: Kubernetes cluster, eu-central
|
||||||
@@ -36,9 +42,26 @@ boundaries:
|
|||||||
- kind: security-group
|
- kind: security-group
|
||||||
label: Managed DB zone
|
label: Managed DB zone
|
||||||
wraps: [tsdb, device-registry]
|
wraps: [tsdb, device-registry]
|
||||||
|
- kind: region
|
||||||
|
label: Ops network, managed Grafana
|
||||||
|
wraps: [ops-dashboard]
|
||||||
|
- kind: region
|
||||||
|
label: Homeowner phones
|
||||||
|
wraps: [customer-app]
|
||||||
connections:
|
connections:
|
||||||
- { from: heat-pump, to: mqtt-broker, label: "8883", variant: emphasis }
|
- { from: heat-pump, to: mqtt-broker, label: "8883", variant: emphasis }
|
||||||
- { from: mqtt-broker, to: ingest, label: in-cluster }
|
- { from: mqtt-broker, to: ingest, label: in-cluster }
|
||||||
- { from: ingest, to: tsdb, label: "5432", variant: emphasis }
|
- { from: ingest, to: tsdb, label: "5432", variant: emphasis }
|
||||||
- { from: ingest, to: rules, label: gRPC }
|
- { from: ingest, to: rules, label: gRPC }
|
||||||
- { from: device-api, to: device-registry, label: "5432" }
|
- { from: device-api, to: device-registry, label: "5432" }
|
||||||
|
- { from: tsdb, to: ops-dashboard, label: read-only }
|
||||||
|
- { from: customer-app, to: device-api, label: HTTPS }
|
||||||
|
cards:
|
||||||
|
- dot: slate
|
||||||
|
title: 7 Mapping
|
||||||
|
items:
|
||||||
|
- "mqtt-broker, ingest, rules -> Kubernetes cluster, eu-central"
|
||||||
|
- "provisioning, device-api -> Kubernetes cluster, eu-central"
|
||||||
|
- "tsdb, device-registry -> managed DB zone, eu-central"
|
||||||
|
- "ops-dashboard -> ops network, managed Grafana"
|
||||||
|
- "customer-app -> homeowner phones, app stores"
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
schema_version: 1
|
||||||
|
diagram_type: architecture
|
||||||
|
meta:
|
||||||
|
title: Quality tree
|
||||||
|
output: quality-tree.html
|
||||||
|
components:
|
||||||
|
- id: quality
|
||||||
|
type: cloud
|
||||||
|
label: Quality
|
||||||
|
pos: [40, 240]
|
||||||
|
size: [130, 100]
|
||||||
|
- id: reliability
|
||||||
|
type: backend
|
||||||
|
label: Reliability
|
||||||
|
pos: [250, 60]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: performance
|
||||||
|
type: backend
|
||||||
|
label: Performance
|
||||||
|
pos: [250, 210]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: security
|
||||||
|
type: security
|
||||||
|
label: Security
|
||||||
|
pos: [250, 360]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: operability
|
||||||
|
type: backend
|
||||||
|
label: Operability
|
||||||
|
pos: [250, 460]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: q-no-loss
|
||||||
|
type: frontend
|
||||||
|
label: Broker restart, no loss
|
||||||
|
pos: [460, 60]
|
||||||
|
size: [180, 60]
|
||||||
|
- id: q-alert-60s
|
||||||
|
type: frontend
|
||||||
|
label: Fault alert in 60 s
|
||||||
|
pos: [460, 160]
|
||||||
|
size: [180, 60]
|
||||||
|
- id: q-peak-load
|
||||||
|
type: frontend
|
||||||
|
label: 4,000 readings/s
|
||||||
|
pos: [460, 260]
|
||||||
|
size: [180, 60]
|
||||||
|
- id: q-revoke
|
||||||
|
type: frontend
|
||||||
|
label: Revoke cert in 15 min
|
||||||
|
pos: [460, 360]
|
||||||
|
size: [180, 60]
|
||||||
|
- id: q-new-device
|
||||||
|
type: frontend
|
||||||
|
label: New device in 10 min
|
||||||
|
pos: [460, 460]
|
||||||
|
size: [180, 60]
|
||||||
|
connections:
|
||||||
|
- { from: quality, to: reliability }
|
||||||
|
- { from: quality, to: performance }
|
||||||
|
- { from: quality, to: security }
|
||||||
|
- { from: quality, to: operability }
|
||||||
|
- { from: reliability, to: q-no-loss }
|
||||||
|
- { from: performance, to: q-alert-60s }
|
||||||
|
- { from: performance, to: q-peak-load }
|
||||||
|
- { from: security, to: q-revoke }
|
||||||
|
- { from: operability, to: q-new-device }
|
||||||
|
cards:
|
||||||
|
- dot: emerald
|
||||||
|
title: 10 Quality scenarios
|
||||||
|
items:
|
||||||
|
- "Broker restarts under load: devices reconnect, no accepted reading lost"
|
||||||
|
- "Heat pump sends a fault code: homeowner gets a push within 60 s"
|
||||||
|
- "Fleet peaks at 4,000 readings/s: p95 ingest lag stays under 5 s"
|
||||||
|
- "A device certificate is stolen: ops revoke it, broker refuses it within 15 min"
|
||||||
|
- "Installer registers a new heat pump: data visible in the app within 10 min"
|
||||||
@@ -5,11 +5,14 @@ model:
|
|||||||
description: >
|
description: >
|
||||||
An arc42 example: a fictional heat pump maker collects device telemetry
|
An arc42 example: a fictional heat pump maker collects device telemetry
|
||||||
over MQTT, stores it, raises alerts and shows it to operations and customers.
|
over MQTT, stores it, raises alerts and shows it to operations and customers.
|
||||||
main.archify holds section 3 business context with cards for sections
|
Where the 12 arc42 sections live:
|
||||||
1 goals, 2 constraints, 10 quality, 11 risks and 12 glossary;
|
1 introduction and goals, 2 constraints, 4 solution strategy,
|
||||||
0300-context the technical context; 0500-building-blocks section 5;
|
11 risks and 12 glossary are cards on this overview;
|
||||||
0600-runtime section 6; 0700-deployment section 7; decisions/ section 9;
|
3 business context is this overview, technical context in 0300-context;
|
||||||
the crosscutting concepts of section 8 are a card on level-1.
|
5 building blocks in 0500-building-blocks (black boxes in the registry);
|
||||||
|
6 runtime in 0600-runtime; 7 deployment in 0700-deployment;
|
||||||
|
8 crosscutting concepts a card on 0500-building-blocks/level-1;
|
||||||
|
9 decisions in decisions/; 10 quality tree in 1000-quality.
|
||||||
diagram_type: architecture
|
diagram_type: architecture
|
||||||
meta:
|
meta:
|
||||||
title: Business context
|
title: Business context
|
||||||
@@ -44,29 +47,47 @@ connections:
|
|||||||
- { from: telemetry-platform, to: push-service, label: alerts }
|
- { from: telemetry-platform, to: push-service, label: alerts }
|
||||||
- { from: telemetry-platform, to: service-desk, label: tickets }
|
- { from: telemetry-platform, to: service-desk, label: tickets }
|
||||||
cards:
|
cards:
|
||||||
|
- dot: orange
|
||||||
|
title: 1.1 Requirements
|
||||||
|
items:
|
||||||
|
- Collect telemetry from every heat pump every 30 s
|
||||||
|
- Alert homeowner and ops on fault codes
|
||||||
|
- Show live and historic data in app and dashboard
|
||||||
|
- Register and provision new devices on site
|
||||||
- dot: cyan
|
- dot: cyan
|
||||||
title: 1 Quality goals
|
title: 1.2 Quality goals
|
||||||
items:
|
items:
|
||||||
- Reliability, no telemetry lost once accepted
|
- Reliability, no telemetry lost once accepted
|
||||||
- Timeliness, alert within 60 s of a fault
|
- Timeliness, alert within 60 s of a fault
|
||||||
- Security, every device authenticated
|
- Security, every device authenticated
|
||||||
|
- dot: emerald
|
||||||
|
title: 1.3 Stakeholders
|
||||||
|
items:
|
||||||
|
- "Homeowner: sees status, gets alerts quickly"
|
||||||
|
- "Ops engineer: fleet view, few false alarms"
|
||||||
|
- "Installer: register a device in minutes"
|
||||||
|
- "Firmware partner: stable MQTT contract"
|
||||||
- dot: slate
|
- dot: slate
|
||||||
title: 2 Constraints
|
title: 2 Constraints
|
||||||
items:
|
items:
|
||||||
- Devices speak MQTT only; firmware by a partner
|
- Devices speak MQTT only; firmware by a partner
|
||||||
- Data stays in one EU region
|
- Data stays in one EU region
|
||||||
- Team of six; PostgreSQL skills in house
|
- Team of six; PostgreSQL skills in house
|
||||||
- dot: emerald
|
- dot: amber
|
||||||
title: 10 Quality scenarios
|
title: 4 Solution strategy
|
||||||
items:
|
items:
|
||||||
- A fault code reaches the homeowner within 60 s
|
- MQTT with mutual TLS for devices (security, ADR 0001)
|
||||||
- Broker restart, devices reconnect, no data lost
|
- TimescaleDB for telemetry, SQL everywhere (ADR 0002)
|
||||||
- A stolen certificate is revoked within 15 min
|
- Ingest acks only after commit (reliability)
|
||||||
|
- Rules engine on the ingest stream (timeliness)
|
||||||
|
- Split by flow, ingest vs read vs provisioning
|
||||||
- dot: rose
|
- dot: rose
|
||||||
title: 11 Risks
|
title: 11 Risks and technical debt
|
||||||
items:
|
items:
|
||||||
- R1 fleet growth beyond one TSDB node
|
- "Risk: fleet growth beyond one TSDB node"
|
||||||
- R2 partner firmware delays certificate rotation
|
- "Risk: partner firmware delays cert rotation"
|
||||||
|
- "Debt: payload v1 decoder kept for old firmware"
|
||||||
|
- "Debt: rules configured in code, no UI"
|
||||||
- dot: violet
|
- dot: violet
|
||||||
title: 12 Glossary
|
title: 12 Glossary
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -5,14 +5,18 @@ elements:
|
|||||||
label: MQTT broker
|
label: MQTT broker
|
||||||
sublabel: mutual TLS
|
sublabel: mutual TLS
|
||||||
description: >
|
description: >
|
||||||
Terminates device connections; one topic per device.
|
Responsibility: terminates device connections, one topic per device.
|
||||||
|
Interfaces: provides MQTT 8883 with mTLS; requires certs and
|
||||||
|
revocation checks from Provisioning.
|
||||||
ingest:
|
ingest:
|
||||||
type: backend
|
type: backend
|
||||||
label: Ingest
|
label: Ingest
|
||||||
sublabel: Go
|
sublabel: Go
|
||||||
brand: go
|
brand: go
|
||||||
description: >
|
description: >
|
||||||
Decodes, validates and stores telemetry; forwards it to the rules.
|
Responsibility: decodes, validates and stores telemetry.
|
||||||
|
Interfaces: requires MQTT subscription, TSDB, device registry;
|
||||||
|
provides the reading stream to the rules engine.
|
||||||
diagram: 0500-building-blocks/ingest.archify
|
diagram: 0500-building-blocks/ingest.archify
|
||||||
tsdb:
|
tsdb:
|
||||||
type: database
|
type: database
|
||||||
@@ -20,42 +24,52 @@ elements:
|
|||||||
sublabel: TimescaleDB
|
sublabel: TimescaleDB
|
||||||
brand: postgresql
|
brand: postgresql
|
||||||
description: >
|
description: >
|
||||||
Raw telemetry for 90 days and hourly rollups for 10 years.
|
Responsibility: raw telemetry 90 days, hourly rollups 10 years.
|
||||||
|
Interfaces: provides SQL on 5432 to Device API and dashboard;
|
||||||
|
requires batched writes from Ingest.
|
||||||
rules:
|
rules:
|
||||||
type: backend
|
type: backend
|
||||||
label: Rules engine
|
label: Rules engine
|
||||||
sublabel: alerting
|
sublabel: alerting
|
||||||
description: >
|
description: >
|
||||||
Evaluates thresholds and fault codes; raises alerts.
|
Responsibility: evaluates thresholds and fault codes, raises alerts.
|
||||||
|
Interfaces: requires the reading stream from Ingest;
|
||||||
|
provides alerts to the push service and service desk.
|
||||||
provisioning:
|
provisioning:
|
||||||
type: security
|
type: security
|
||||||
label: Provisioning
|
label: Provisioning
|
||||||
sublabel: device certs
|
sublabel: device certs
|
||||||
description: >
|
description: >
|
||||||
Registers devices and issues their client certificates.
|
Responsibility: registers devices and issues client certificates.
|
||||||
|
Interfaces: provides a REST register call for installers and
|
||||||
|
revocation checks for the broker; requires the device registry.
|
||||||
device-registry:
|
device-registry:
|
||||||
type: database
|
type: database
|
||||||
label: Device registry
|
label: Device registry
|
||||||
sublabel: PostgreSQL
|
sublabel: PostgreSQL
|
||||||
brand: postgresql
|
brand: postgresql
|
||||||
description: >
|
description: >
|
||||||
Devices, owners, firmware versions and certificate state.
|
Responsibility: devices, owners, firmware and certificate state.
|
||||||
|
Interfaces: provides SQL on 5432 to Ingest, Provisioning, Device API.
|
||||||
device-api:
|
device-api:
|
||||||
type: backend
|
type: backend
|
||||||
label: Device API
|
label: Device API
|
||||||
sublabel: REST
|
sublabel: REST
|
||||||
description: >
|
description: >
|
||||||
Serves telemetry and device data to the customer app.
|
Responsibility: serves telemetry and device data to the customer app.
|
||||||
|
Interfaces: provides REST over HTTPS; requires TSDB and device registry.
|
||||||
ops-dashboard:
|
ops-dashboard:
|
||||||
type: frontend
|
type: frontend
|
||||||
label: Ops dashboard
|
label: Ops dashboard
|
||||||
sublabel: Grafana
|
sublabel: Grafana
|
||||||
brand: grafana
|
brand: grafana
|
||||||
description: >
|
description: >
|
||||||
Fleet view for the operations team, read only on the TSDB.
|
Responsibility: fleet view for the operations team.
|
||||||
|
Interfaces: provides web UI over HTTPS; requires read-only SQL on TSDB.
|
||||||
customer-app:
|
customer-app:
|
||||||
type: frontend
|
type: frontend
|
||||||
label: Customer app
|
label: Customer app
|
||||||
sublabel: mobile
|
sublabel: mobile
|
||||||
description: >
|
description: >
|
||||||
Shows the homeowner their heat pump and its alerts.
|
Responsibility: shows the homeowner their heat pump and alerts.
|
||||||
|
Interfaces: provides the mobile UI; requires Device API and push.
|
||||||
@@ -24,3 +24,11 @@ connections:
|
|||||||
- { from: wiki-admin, to: wiki, label: kubectl }
|
- { from: wiki-admin, to: wiki, label: kubectl }
|
||||||
- { from: wiki, to: idp, label: OIDC, variant: security }
|
- { from: wiki, to: idp, label: OIDC, variant: security }
|
||||||
- { from: wiki, to: object-store, label: S3 API }
|
- { from: wiki, to: object-store, label: S3 API }
|
||||||
|
cards:
|
||||||
|
- dot: cyan
|
||||||
|
title: 3 Domain interfaces
|
||||||
|
items:
|
||||||
|
- "Engineer: in edits, queries, files; out pages, results"
|
||||||
|
- "Wiki admin: in deploys, restore commands; out status, logs"
|
||||||
|
- "Identity provider: out sign-in code, groups; in token requests"
|
||||||
|
- "Object storage: out attachments, backups; in signed URL reads"
|
||||||
@@ -42,6 +42,9 @@ cards:
|
|||||||
- dot: amber
|
- dot: amber
|
||||||
title: 8 Crosscutting concepts
|
title: 8 Crosscutting concepts
|
||||||
items:
|
items:
|
||||||
- Access - every request checked against space groups
|
- "Security: OIDC session, every request checked against space groups"
|
||||||
- Logs - JSON to stdout, collected by the cluster
|
- "Persistence: PostgreSQL is the only source of truth"
|
||||||
- Config - environment variables, secrets from the vault
|
- "Errors: API returns problem JSON; the web app shows a retry"
|
||||||
|
- "Logging: JSON to stdout, collected by the cluster"
|
||||||
|
- "Backups: nightly dump to object storage, 30 days"
|
||||||
|
- "Config: environment variables, secrets from the vault"
|
||||||
@@ -36,8 +36,11 @@ boundaries:
|
|||||||
label: Company cluster
|
label: Company cluster
|
||||||
wraps: [ingress, web, api, db, backup-job]
|
wraps: [ingress, web, api, db, backup-job]
|
||||||
- kind: security-group
|
- kind: security-group
|
||||||
label: Namespace team-wiki
|
label: App node pool
|
||||||
wraps: [web, api, db, backup-job]
|
wraps: [web, api]
|
||||||
|
- kind: security-group
|
||||||
|
label: Data node
|
||||||
|
wraps: [db, backup-job]
|
||||||
connections:
|
connections:
|
||||||
- { from: engineer, to: ingress, label: HTTPS, variant: emphasis }
|
- { from: engineer, to: ingress, label: HTTPS, variant: emphasis }
|
||||||
- { from: ingress, to: web, label: static }
|
- { from: ingress, to: web, label: static }
|
||||||
@@ -46,3 +49,12 @@ connections:
|
|||||||
- { from: api, to: idp, label: OIDC, variant: security, labelAt: [525, 234] }
|
- { from: api, to: idp, label: OIDC, variant: security, labelAt: [525, 234] }
|
||||||
- { from: backup-job, to: db, label: dump }
|
- { from: backup-job, to: db, label: dump }
|
||||||
- { from: backup-job, to: object-store, label: S3 }
|
- { from: backup-job, to: object-store, label: S3 }
|
||||||
|
cards:
|
||||||
|
- dot: slate
|
||||||
|
title: 7 Mapping
|
||||||
|
items:
|
||||||
|
- "Web App -> app node pool, nginx pod, namespace team-wiki"
|
||||||
|
- "Wiki API -> app node pool, 2 pods behind the ingress"
|
||||||
|
- "Database -> data node, PostgreSQL stateful set with volume"
|
||||||
|
- "Backup job -> data node, Kubernetes CronJob"
|
||||||
|
- "Attachments, backups -> company object storage buckets"
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
schema_version: 1
|
||||||
|
diagram_type: architecture
|
||||||
|
meta:
|
||||||
|
title: Quality tree
|
||||||
|
output: quality-tree.html
|
||||||
|
legend:
|
||||||
|
mode: hidden
|
||||||
|
components:
|
||||||
|
- id: quality
|
||||||
|
type: cloud
|
||||||
|
label: Quality
|
||||||
|
pos: [40, 200]
|
||||||
|
size: [130, 120]
|
||||||
|
- id: usability
|
||||||
|
type: frontend
|
||||||
|
label: Usability
|
||||||
|
pos: [250, 60]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: security
|
||||||
|
type: security
|
||||||
|
label: Security
|
||||||
|
pos: [250, 160]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: reliability
|
||||||
|
type: backend
|
||||||
|
label: Reliability
|
||||||
|
pos: [250, 310]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: maintainability
|
||||||
|
type: backend
|
||||||
|
label: Maintainability
|
||||||
|
pos: [250, 460]
|
||||||
|
size: [130, 60]
|
||||||
|
- id: q-search
|
||||||
|
type: external
|
||||||
|
label: Search under 1 s
|
||||||
|
pos: [460, 60]
|
||||||
|
size: [170, 60]
|
||||||
|
- id: q-leaver
|
||||||
|
type: external
|
||||||
|
label: Leaver out in 5 min
|
||||||
|
pos: [460, 160]
|
||||||
|
size: [170, 60]
|
||||||
|
- id: q-restore
|
||||||
|
type: external
|
||||||
|
label: Restore in 30 min
|
||||||
|
pos: [460, 260]
|
||||||
|
size: [170, 60]
|
||||||
|
- id: q-pod
|
||||||
|
type: external
|
||||||
|
label: Pod back in 1 min
|
||||||
|
pos: [460, 360]
|
||||||
|
size: [170, 60]
|
||||||
|
- id: q-upgrade
|
||||||
|
type: external
|
||||||
|
label: Upgrade in 1 hour
|
||||||
|
pos: [460, 460]
|
||||||
|
size: [170, 60]
|
||||||
|
connections:
|
||||||
|
- { from: quality, to: usability }
|
||||||
|
- { from: quality, to: security }
|
||||||
|
- { from: quality, to: reliability }
|
||||||
|
- { from: quality, to: maintainability }
|
||||||
|
- { from: usability, to: q-search }
|
||||||
|
- { from: security, to: q-leaver }
|
||||||
|
- { from: reliability, to: q-restore }
|
||||||
|
- { from: reliability, to: q-pod }
|
||||||
|
- { from: maintainability, to: q-upgrade }
|
||||||
|
cards:
|
||||||
|
- dot: emerald
|
||||||
|
title: 10 Quality scenarios
|
||||||
|
items:
|
||||||
|
- "Search: an engineer searches 5000 pages; results in under 1 s"
|
||||||
|
- "Leaver: HR disables an account; wiki access ends within 5 minutes"
|
||||||
|
- "Restore: the database is lost; the admin restores last night in 30 minutes"
|
||||||
|
- "Crash: an API pod dies; the cluster restarts it, no data lost, back in 1 minute"
|
||||||
|
- "Upgrade: a new wiki version ships; one admin rolls it out in 1 hour"
|
||||||
@@ -3,12 +3,14 @@ model:
|
|||||||
id: team-wiki
|
id: team-wiki
|
||||||
name: Team Wiki
|
name: Team Wiki
|
||||||
description: >
|
description: >
|
||||||
An arc42 example: a small self-hosted wiki for an engineering
|
An arc42 example: a small self-hosted wiki for an engineering team.
|
||||||
team. Sections 1 goals, 2 constraints, 3 business context,
|
1 Requirements, quality goals, stakeholders; 2 Constraints;
|
||||||
10 quality, 11 risks and 12 glossary are on this overview;
|
4 Solution strategy; 11 Risks; 12 Glossary: cards on this overview.
|
||||||
0300-context holds 3 technical context, 0500-building-blocks
|
3 Context: this overview (business) and 0300-context (technical).
|
||||||
5 building blocks, 0600-runtime 6 runtime, 0700-deployment
|
5 Building blocks: 0500-building-blocks, black boxes in registry/.
|
||||||
7 deployment and decisions 9 architecture decisions.
|
6 Runtime: 0600-runtime. 7 Deployment: 0700-deployment.
|
||||||
|
8 Crosscutting concepts: card on 0500-building-blocks/level-1.
|
||||||
|
9 Decisions: decisions/. 10 Quality: 1000-quality.
|
||||||
diagram_type: architecture
|
diagram_type: architecture
|
||||||
meta:
|
meta:
|
||||||
title: Team Wiki - business context
|
title: Team Wiki - business context
|
||||||
@@ -36,31 +38,48 @@ connections:
|
|||||||
- { from: wiki, to: idp, label: sign-in, variant: security }
|
- { from: wiki, to: idp, label: sign-in, variant: security }
|
||||||
- { from: wiki, to: object-store, label: files }
|
- { from: wiki, to: object-store, label: files }
|
||||||
cards:
|
cards:
|
||||||
- dot: cyan
|
- dot: orange
|
||||||
title: 1 Quality goals
|
title: 1.1 Requirements
|
||||||
items:
|
items:
|
||||||
- Findability - any page found in two searches
|
- "Create, edit and version pages in team spaces"
|
||||||
- Security - only current staff can read
|
- "Full-text search over every page a user may read"
|
||||||
- Operability - one person runs it in an hour a week
|
- "Attach files to pages"
|
||||||
|
- "Admin restores the wiki from a nightly backup"
|
||||||
|
- dot: cyan
|
||||||
|
title: 1.2 Quality goals
|
||||||
|
items:
|
||||||
|
- "Findability: any page found in two searches"
|
||||||
|
- "Security: only current staff can read"
|
||||||
|
- "Operability: one person runs it in an hour a week"
|
||||||
|
- dot: amber
|
||||||
|
title: 1.3 Stakeholders
|
||||||
|
items:
|
||||||
|
- "Engineer: fast search, simple editor"
|
||||||
|
- "Wiki admin: little routine work, easy restore"
|
||||||
|
- "Security officer: no access after leaving"
|
||||||
|
- "Team lead: one place for team knowledge"
|
||||||
- dot: slate
|
- dot: slate
|
||||||
title: 2 Constraints
|
title: 2 Constraints
|
||||||
items:
|
items:
|
||||||
- Runs on the company Kubernetes cluster
|
- "Runs on the company Kubernetes cluster"
|
||||||
- Sign-in only via the company identity provider
|
- "Sign-in only via the company identity provider"
|
||||||
- dot: emerald
|
- dot: emerald
|
||||||
title: 10 Quality scenarios
|
title: 4 Solution strategy
|
||||||
items:
|
items:
|
||||||
- A search over 5000 pages answers in under 1 s
|
- "React, Node.js API, PostgreSQL: one stack the team knows"
|
||||||
- A leaver loses access within 5 minutes
|
- "Three blocks plus a backup job: operability"
|
||||||
- The admin restores last night's state in 30 minutes
|
- "Findability: PostgreSQL full-text search (postgres-full-text-search)"
|
||||||
|
- "Security: OIDC at the company IdP only (sso-only-via-company-idp)"
|
||||||
|
- "Operability: files and backups in company object storage"
|
||||||
- dot: rose
|
- dot: rose
|
||||||
title: 11 Risks
|
title: 11 Risks and technical debt
|
||||||
items:
|
items:
|
||||||
- Identity provider outage blocks all sign-ins
|
- "Risk: identity provider outage blocks all sign-ins"
|
||||||
- Backups untested until a restore drill is run
|
- "Risk: backups untested until a restore drill is run"
|
||||||
|
- "Debt: attachments are not virus-scanned yet"
|
||||||
- dot: violet
|
- dot: violet
|
||||||
title: 12 Glossary
|
title: 12 Glossary
|
||||||
items:
|
items:
|
||||||
- Space - pages of one team, mapped to a group
|
- "Space: pages of one team, mapped to a group"
|
||||||
- Revision - one saved version of a page
|
- "Revision: one saved version of a page"
|
||||||
- Attachment - a file stored beside a page
|
- "Attachment: a file stored beside a page"
|
||||||
@@ -6,15 +6,17 @@ elements:
|
|||||||
sublabel: React
|
sublabel: React
|
||||||
brand: react
|
brand: react
|
||||||
description: >
|
description: >
|
||||||
Page editor, viewer and search box in the browser; talks
|
Responsibility: page editor, viewer and search box in the
|
||||||
only to the API.
|
browser. Interfaces: provides the UI over HTTPS; requires
|
||||||
|
the Wiki API (JSON).
|
||||||
api:
|
api:
|
||||||
type: backend
|
type: backend
|
||||||
label: Wiki API
|
label: Wiki API
|
||||||
sublabel: REST
|
sublabel: REST
|
||||||
description: >
|
description: >
|
||||||
Pages, search, attachments and access checks; the only
|
Responsibility: pages, search, attachments and access checks;
|
||||||
writer of the database.
|
the only writer of the database. Interfaces: provides REST
|
||||||
|
JSON; requires the IdP (OIDC), the database (SQL), object storage (S3).
|
||||||
diagram: 0500-building-blocks/wiki-api.archify
|
diagram: 0500-building-blocks/wiki-api.archify
|
||||||
db:
|
db:
|
||||||
type: database
|
type: database
|
||||||
@@ -22,12 +24,14 @@ elements:
|
|||||||
sublabel: PostgreSQL
|
sublabel: PostgreSQL
|
||||||
brand: postgresql
|
brand: postgresql
|
||||||
description: >
|
description: >
|
||||||
Pages, revisions, users and the full-text index; the
|
Responsibility: pages, revisions, users and the full-text
|
||||||
source of truth.
|
index; the source of truth. Interfaces: provides SQL to the
|
||||||
|
API and dumps to the backup job.
|
||||||
backup-job:
|
backup-job:
|
||||||
type: backend
|
type: backend
|
||||||
label: Backup job
|
label: Backup job
|
||||||
sublabel: nightly
|
sublabel: nightly
|
||||||
description: >
|
description: >
|
||||||
Dumps the database at 02:00 and writes it to the backup
|
Responsibility: dumps the database at 02:00, keeps 30 days.
|
||||||
bucket; keeps 30 days.
|
Interfaces: requires the database (pg_dump) and the backup
|
||||||
|
bucket (S3); provides none.
|
||||||
@@ -51,9 +51,9 @@ boundaries:
|
|||||||
connections:
|
connections:
|
||||||
- { from: onboarding-svc, to: new-hire, label: serves, variant: emphasis }
|
- { from: onboarding-svc, to: new-hire, label: serves, variant: emphasis }
|
||||||
- { from: onboard, to: onboarding-svc, label: realizes }
|
- { from: onboard, to: onboarding-svc, label: realizes }
|
||||||
- { from: portal, to: new-hire, label: serves }
|
- { from: portal, to: new-hire, label: serves, labelAt: [125, 197] }
|
||||||
- { from: provisioning-svc, to: onboard, label: serves }
|
- { from: provisioning-svc, to: onboard, label: serves, labelAt: [585, 197] }
|
||||||
- { from: iam, to: provisioning-svc, label: realizes }
|
- { from: iam, to: provisioning-svc, label: realizes }
|
||||||
- { from: office-net, to: portal, label: serves }
|
- { from: office-net, to: portal, label: serves, labelAt: [125, 397] }
|
||||||
- { from: sso, to: iam, label: serves }
|
- { from: sso, to: iam, label: serves, labelAt: [355, 397] }
|
||||||
- { from: directory, to: sso, label: realizes }
|
- { from: directory, to: sso, label: realizes }
|
||||||
@@ -51,9 +51,9 @@ boundaries:
|
|||||||
connections:
|
connections:
|
||||||
- { from: claim-registration, to: insurant, label: serves, variant: emphasis }
|
- { from: claim-registration, to: insurant, label: serves, variant: emphasis }
|
||||||
- { from: handle-claim, to: claim-registration, label: realizes }
|
- { from: handle-claim, to: claim-registration, label: realizes }
|
||||||
- { from: portal, to: insurant, label: serves }
|
- { from: portal, to: insurant, label: serves, labelAt: [125, 197] }
|
||||||
- { from: claims-admin, to: handle-claim, label: serves }
|
- { from: claims-admin, to: handle-claim, label: serves, labelAt: [585, 197] }
|
||||||
- { from: claims-app, to: claims-admin, label: realizes }
|
- { from: claims-app, to: claims-admin, label: realizes }
|
||||||
- { from: dmz, to: portal, label: serves }
|
- { from: dmz, to: portal, label: serves, labelAt: [125, 397] }
|
||||||
- { from: app-servers, to: claims-app, label: serves }
|
- { from: app-servers, to: claims-app, label: serves, labelAt: [355, 397] }
|
||||||
- { from: db-cluster, to: app-servers, label: serves }
|
- { from: db-cluster, to: app-servers, label: serves }
|
||||||
@@ -51,9 +51,9 @@ boundaries:
|
|||||||
connections:
|
connections:
|
||||||
- { from: tracking-service, to: recipient, label: serves, variant: emphasis }
|
- { from: tracking-service, to: recipient, label: serves, variant: emphasis }
|
||||||
- { from: deliver-shipment, to: tracking-service, label: realizes }
|
- { from: deliver-shipment, to: tracking-service, label: realizes }
|
||||||
- { from: tracking-portal, to: recipient, label: serves }
|
- { from: tracking-portal, to: recipient, label: serves, labelAt: [125, 197] }
|
||||||
- { from: tracking-svc, to: deliver-shipment, label: serves }
|
- { from: tracking-svc, to: deliver-shipment, label: serves, labelAt: [585, 197] }
|
||||||
- { from: tms, to: tracking-svc, label: realizes }
|
- { from: tms, to: tracking-svc, label: realizes }
|
||||||
- { from: mobile-network, to: tracking-portal, label: serves }
|
- { from: mobile-network, to: tracking-portal, label: serves, labelAt: [125, 397] }
|
||||||
- { from: cloud-cluster, to: tms, label: serves }
|
- { from: cloud-cluster, to: tms, label: serves, labelAt: [355, 397] }
|
||||||
- { from: event-streaming, to: tracking-svc, label: serves }
|
- { from: event-streaming, to: tracking-svc, label: serves, labelAt: [585, 397] }
|
||||||
+12
-1
@@ -29,7 +29,7 @@ function walk(dir: string, rel = ''): string[] {
|
|||||||
// The folders each framework's examples must have, besides main.archify, registry/ and decisions/.
|
// The folders each framework's examples must have, besides main.archify, registry/ and decisions/.
|
||||||
const FRAMEWORK_FOLDERS: Record<string, string[]> = {
|
const FRAMEWORK_FOLDERS: Record<string, string[]> = {
|
||||||
c4: ['0100-context', '0200-containers', '0300-components'],
|
c4: ['0100-context', '0200-containers', '0300-components'],
|
||||||
arc42: ['0300-context', '0500-building-blocks', '0600-runtime', '0700-deployment'],
|
arc42: ['0300-context', '0500-building-blocks', '0600-runtime', '0700-deployment', '1000-quality'],
|
||||||
archimate: ['0100-business', '0200-application', '0300-technology'],
|
archimate: ['0100-business', '0200-application', '0300-technology'],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,6 +95,17 @@ for (const example of MODEL_EXAMPLES) {
|
|||||||
}
|
}
|
||||||
// Every registry element is used by some diagram.
|
// Every registry element is used by some diagram.
|
||||||
for (const id of registry.elements.keys()) assert.ok(used.has(id), `element ${id} is never used`);
|
for (const id of registry.elements.keys()) assert.ok(used.has(id), `element ${id} is never used`);
|
||||||
|
|
||||||
|
// arc42: every text section has a card somewhere, titled with its number.
|
||||||
|
if (example.folder.startsWith('arc42-')) {
|
||||||
|
const titles = diagrams.flatMap((file) => {
|
||||||
|
const cards = parse(read(file))?.cards;
|
||||||
|
return Array.isArray(cards) ? cards.map((card: { title?: string }) => String(card?.title ?? '')) : [];
|
||||||
|
});
|
||||||
|
for (const section of ['1.1', '1.2', '1.3', '2', '3', '4', '7', '8', '10', '11', '12']) {
|
||||||
|
assert.ok(titles.some((title) => title.startsWith(`${section} `)), `${example.folder}: no card for arc42 section ${section}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user