arc42 and ArchiMate examples: nine models, three per framework; 1.2

The six models from extension 0.11.0 (arc42 by section, ArchiMate by
layer) next to the three C4 ones; model:scaffold --example offers them
and the tests validate each with 0 problems.
This commit is contained in:
root-at-skic committed 2026-11-08 22:57:00 +02:00
1 parent 21770c241d
commit d9777fb90e
74 files changed
+2768 -7

No files matched your search

+3 -3
View File
@@ -15,7 +15,7 @@ license: MIT
compatibility: Requires Node.js 18+, bash and go-task (task). No network access; Archify is bundled under scripts/vendor.
metadata:
author: root-at-skic
version: "1.1"
version: "1.2"
origin-repository: https://gitea.lego-cloud.eu/vscode-extensions-skills/archify-vscode-ext.git
targets: Archify Diagram Viewer (root-at-skic.archify-vscode-ext)
---
@@ -41,7 +41,7 @@ task --list-all
| `task model:list -- --dir="<model>"` | What the model page shows: metadata, diagrams by folder, registry elements with "used in N diagrams", decisions with status and the diagrams that reference them. |
| `task model:graph -- --dir="<model>" [--query="uses:api"] [--links]` | The model as one graph, as the extension's **Graph** page shows it: every element, every connection once, plus a node per decision and per registry file. `--query` narrows it with the page's language: `database`, `type:backend`, `diagram:0200-*`, `decision:<id>`, `uses:api`, `near:api,2`, `from:web to:db` (shortest path), `unused`, `shared`, `-shared`; terms AND together, a leading `-` excludes. Use it to answer "what talks to X", "how does A reach B", "what does this decision touch", "which registry elements are unused". |
| `task model:scaffold -- --dir="<folder>" --id="<id>" --name="<name>" [--description="…"] [--c4]` | Creates `main.archify`; with `--c4` also `registry/` and the `0100-context`, `0200-containers`, `0300-components` folders with starter diagrams. Never overwrites. |
| `task model:scaffold -- --dir="<folder>" --example="<name>" [--id="<id>"] [--name="<name>"] [--description="…"]` | Copies a complete example model: `c4-standard-web-app`, `c4-e-shop` or `c4-event-based-integration` (the same three the extension offers under *Archify \| New Model…*). Id, name and description default to the example's own. Never overwrites. |
| `task model:scaffold -- --dir="<folder>" --example="<name>" [--id="<id>"] [--name="<name>"] [--description="…"]` | Copies a complete example model: C4 `c4-standard-web-app`, `c4-e-shop`, `c4-event-based-integration`; arc42 `arc42-cinema-tickets`, `arc42-iot-telemetry`, `arc42-team-wiki`; ArchiMate `archimate-insurance-claims`, `archimate-hr-onboarding`, `archimate-logistics-tracking` (the same nine the extension offers under *Archify \| New Model…*). Id, name and description default to the example's own. Never overwrites. |
| `task model:resolve -- --file="<diagram>" [--out="<file.json>"]` | The Archify JSON the extension hands to Archify, for the plain Archify CLI or CI. |
| `task diagram:validate -- --file="<diagram>"` | One diagram through Archify (refs resolved when inside a model). |
| `task diagram:render -- --file="<diagram>" --out="<file.html>"` | Standalone HTML page for people without VS Code. |
@@ -187,4 +187,4 @@ Diagrams next to `main.archify` are listed first, ungrouped; deeper ones by fold
- Read `references/authoring.md` when writing or repairing a diagram: kinds, fields, grid, labels, boundaries, brands, validator codes and fixes.
- Read `references/formats.md` when writing `main.archify`, a registry, a `ref` or a link, or when unsure what the tasks strip before Archify runs.
- Read `references/folders.md` when laying out a model folder or explaining how the extension finds the root and groups diagrams.
- `examples/c4-standard-web-app/`, `examples/c4-e-shop/` and `examples/c4-event-based-integration/` are complete C4-style models that validate with 0 problems; copy their shape, or copy one with `model:scaffold --example`.
- `examples/` holds nine complete models that validate with 0 problems; copy their shape, or copy one with `model:scaffold --example`. Three C4 (`c4-*`: context, containers, components). Three arc42 (`arc42-*`: the overview is section 3 business context with goals, constraints, quality, risks and glossary as `cards`; `0300-context`, `0500-building-blocks`, `0600-runtime` sequence diagrams, `0700-deployment`; decisions are section 9). Three ArchiMate (`archimate-*`: the overview is a layered view in three region boundaries; `0100-business`, `0200-application`, `0300-technology`; one registry file per layer; each element's ArchiMate type as its `tag` and `sublabel`, since Archify shows tags only in the focus panel).
@@ -0,0 +1,30 @@
schema_version: 1
diagram_type: architecture
meta:
title: Technical context
output: technical-context.html
components:
- ref: customer
pos: [40, 60]
size: [130, 60]
- ref: usher
pos: [40, 260]
size: [130, 60]
- ref: tickets
pos: [290, 160]
size: [150, 60]
- ref: psp
pos: [560, 60]
size: [150, 60]
- ref: mail
pos: [560, 160]
size: [150, 60]
- ref: pos
pos: [560, 260]
size: [150, 60]
connections:
- { from: customer, to: tickets, label: HTTPS JSON, variant: emphasis }
- { from: usher, to: tickets, label: HTTPS app }
- { from: tickets, to: psp, label: REST webhook, variant: security }
- { from: tickets, to: mail, label: SMTP TLS }
- { from: tickets, to: pos, label: REST VPN }
@@ -0,0 +1,49 @@
schema_version: 1
diagram_type: architecture
meta:
title: Booking API - level 2
output: booking.html
decisions: [seat-holds-with-ttl]
components:
- ref: web
pos: [40, 160]
size: [130, 60]
- id: programme
type: backend
label: Programme
sublabel: shows, halls
pos: [250, 60]
size: [130, 60]
- id: seats
type: backend
label: Seat Holds
sublabel: lock, release
pos: [250, 160]
size: [130, 60]
- id: checkout
type: backend
label: Checkout
sublabel: orders, webhook
pos: [250, 260]
size: [130, 60]
- ref: seatlock
pos: [460, 160]
size: [130, 60]
decisions: [seat-holds-with-ttl]
- ref: db
pos: [460, 60]
size: [130, 60]
- ref: psp
pos: [460, 360]
size: [150, 60]
boundaries:
- kind: region
label: Booking API
wraps: [programme, seats, checkout]
connections:
- { from: web, to: programme, label: browse }
- { from: web, to: seats, label: hold, variant: emphasis }
- { from: web, to: checkout, label: pay }
- { from: programme, to: db, label: SQL }
- { from: seats, to: seatlock, label: SET NX, decisions: [seat-holds-with-ttl] }
- { from: checkout, to: psp, label: session, variant: security }
@@ -0,0 +1,64 @@
schema_version: 1
diagram_type: architecture
meta:
title: Building blocks - level 1
output: level-1.html
decisions: [booking-owns-seat-inventory]
components:
- ref: web
pos: [40, 110]
size: [130, 60]
- ref: mobile
pos: [40, 210]
size: [130, 60]
- ref: scanner
pos: [40, 360]
size: [130, 60]
- ref: booking
pos: [250, 160]
size: [130, 60]
- ref: seatlock
pos: [460, 60]
size: [130, 60]
- ref: db
pos: [460, 260]
size: [130, 60]
- ref: ticketing
pos: [250, 360]
size: [130, 60]
- ref: possync
pos: [670, 260]
size: [130, 60]
- ref: psp
pos: [900, 160]
size: [150, 60]
- ref: pos
pos: [900, 260]
size: [150, 60]
- ref: mail
pos: [900, 460]
size: [150, 60]
boundaries:
- kind: region
label: Cinema Tickets
wraps: [web, mobile, scanner, booking, seatlock, db, ticketing, possync]
connections:
- { from: web, to: booking, label: JSON, variant: emphasis }
- { from: mobile, to: booking, label: JSON }
- { from: booking, to: seatlock, label: hold }
- { from: booking, to: db, label: SQL }
- { from: booking, to: psp, label: checkout, variant: security }
- { from: booking, to: ticketing, label: issue, labelAt: [315, 234] }
- { from: scanner, to: ticketing, label: check-in }
- { from: ticketing, to: db, label: SQL }
- { from: ticketing, to: mail, label: QR mail }
- { from: possync, to: db, label: SQL }
- { from: possync, to: pos, label: sales, decisions: [booking-owns-seat-inventory] }
cards:
- dot: amber
title: 8 Crosscutting concepts
items:
- "Times in UTC; shown in cinema local time"
- "Every write carries an idempotency key"
- "QR payload signed with an Ed25519 key"
- "Structured logs with order id as trace key"
@@ -0,0 +1,27 @@
schema_version: 1
diagram_type: sequence
meta:
title: Runtime - book seats
output: book-seats.html
viewBox: [1060, 680]
column_fit: spread
decisions: [seat-holds-with-ttl]
participants:
- { ref: customer }
- { ref: web }
- { ref: booking }
- { ref: seatlock }
- { ref: psp }
- { ref: ticketing }
- { ref: mail }
messages:
- { from: customer, to: web, y: 170, label: pick seats, variant: emphasis }
- { from: web, to: booking, y: 214, label: POST /holds }
- { from: booking, to: seatlock, y: 258, label: SET NX TTL 8m, decisions: [seat-holds-with-ttl] }
- { from: seatlock, to: booking, y: 302, label: OK, variant: return }
- { from: booking, to: web, y: 346, label: hold id, variant: return }
- { from: web, to: psp, y: 390, label: hosted checkout, variant: security }
- { from: psp, to: booking, y: 434, label: webhook paid, variant: security }
- { from: booking, to: ticketing, y: 478, label: issue tickets }
- { from: ticketing, to: mail, y: 522, label: QR e-mail }
- { from: mail, to: customer, y: 566, label: tickets, variant: return }
@@ -0,0 +1,19 @@
schema_version: 1
diagram_type: sequence
meta:
title: Runtime - check-in at the door
output: check-in.html
viewBox: [900, 480]
column_fit: spread
decisions: [signed-qr-codes]
participants:
- { ref: usher }
- { ref: scanner }
- { ref: ticketing }
- { ref: db }
messages:
- { from: usher, to: scanner, y: 180, label: scan QR, variant: emphasis, note: signature checked offline, decisions: [signed-qr-codes] }
- { from: scanner, to: ticketing, y: 230, label: POST /checkins }
- { from: ticketing, to: db, y: 280, label: first scan wins }
- { from: ticketing, to: scanner, y: 330, label: valid seat 7F, variant: return }
- { from: scanner, to: usher, y: 380, label: green light, variant: return }
@@ -0,0 +1,58 @@
schema_version: 1
diagram_type: architecture
meta:
title: Deployment - production
output: production.html
components:
- ref: customer
pos: [40, 160]
size: [130, 60]
- id: ingress
type: security
label: Ingress
sublabel: TLS, WAF
pos: [250, 160]
size: [130, 60]
- ref: web
pos: [460, 60]
size: [130, 60]
- ref: booking
pos: [460, 160]
size: [130, 60]
- ref: ticketing
pos: [460, 260]
size: [130, 60]
- ref: possync
pos: [460, 360]
size: [130, 60]
- ref: seatlock
pos: [710, 110]
size: [130, 60]
- ref: db
pos: [710, 260]
size: [130, 60]
- ref: pos
pos: [250, 460]
size: [150, 60]
- ref: scanner
pos: [40, 460]
size: [130, 60]
boundaries:
- kind: region
label: EU cloud - k8s cluster
wraps: [ingress, web, booking, ticketing, possync]
- kind: security-group
label: Managed data zone
wraps: [seatlock, db]
- kind: region
label: Cinema site
wraps: [pos, scanner]
connections:
- { from: customer, to: ingress, label: HTTPS, variant: emphasis }
- { from: ingress, to: web, label: route }
- { from: ingress, to: booking, label: route }
- { from: booking, to: seatlock, label: TLS }
- { from: booking, to: db, label: TLS }
- { from: ticketing, to: db, label: TLS }
- { from: possync, to: pos, label: VPN, variant: security }
- { from: scanner, to: ingress, label: HTTPS }
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: seat-holds-with-ttl
title: Seat holds as Redis keys with a TTL
status: accepted
date: 2026-09-08
elements: [booking, seatlock]
diagrams: [0500-building-blocks/booking.archify, 0600-runtime/book-seats.archify]
context: >
A customer may spend minutes at the payment page. Holding seats
as rows in the database left orphans when a browser closed, and
a nightly job freed them far too late for Friday shows.
decision: >
A hold is one Redis key per seat, set with SET NX and an
8-minute TTL. The payment webhook turns holds into an order in
PostgreSQL; an expired key frees the seat without any job.
consequences: >
Holds survive no Redis restart; at worst a customer re-picks
seats. The webhook must check the hold still exists before it
confirms the order, otherwise it refunds.
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: booking-owns-seat-inventory
title: The Booking API owns the seat inventory
status: accepted
date: 2026-09-22
elements: [booking, db, possync, pos]
diagrams: [0500-building-blocks/level-1.archify]
context: >
The box-office POS and the web shop both sold seats from their
own copy of the seat map. Twice a month a seat was sold at the
counter and online.
decision: >
The Tickets DB is the only seat inventory. The POS asks POS Sync
for a seat before it prints a ticket; POS Sync writes the sale
through the same rules as online orders.
consequences: >
The counter needs the network to sell; a fallback block of
reserved seats per show covers outages. The POS vendor adapter
is ours to maintain.
@@ -0,0 +1,19 @@
schema_version: 1
decision:
id: signed-qr-codes
title: Signed QR codes checked offline at the door
status: accepted
date: 2026-10-06
elements: [ticketing, scanner]
diagrams: [0600-runtime/check-in.archify]
context: >
Cinema Wi-Fi drops when a full hall arrives. A scanner that
needs the server for every ticket stops the queue.
decision: >
The Ticket Service signs each QR payload (ticket id, show,
seat) with a private key. The scanner app verifies it with the
public key and queues check-ins until it is online again.
consequences: >
A forwarded ticket scans twice while offline; the second scan
is flagged when the queue syncs. Key rotation needs an app
update window.
@@ -0,0 +1,72 @@
schema_version: 1
model:
id: cinema-tickets
name: Cinema Tickets
description: >
An arc42 example: online and mobile seat booking for a small
cinema chain. This overview is section 3 (business context) with
cards for sections 1, 2, 10, 11 and 12. 0300-context holds the
technical context (3), 0500-building-blocks the building block
view (5), 0600-runtime the runtime view (6), 0700-deployment the
deployment view (7), decisions/ the architecture decisions (9).
Crosscutting concepts (8) are a card on the level-1 diagram.
diagram_type: architecture
meta:
title: Cinema Tickets - business context
output: main.html
components:
- ref: customer
pos: [40, 60]
size: [130, 60]
- ref: usher
pos: [40, 260]
size: [130, 60]
- ref: tickets
pos: [280, 160]
size: [150, 60]
- ref: psp
pos: [540, 60]
size: [150, 60]
- ref: mail
pos: [540, 160]
size: [150, 60]
- ref: pos
pos: [540, 260]
size: [150, 60]
connections:
- { from: customer, to: tickets, label: books seats, variant: emphasis }
- { from: usher, to: tickets, label: checks in }
- { from: tickets, to: psp, label: payments, variant: security }
- { from: tickets, to: mail, label: tickets }
- { from: tickets, to: pos, label: seat sales }
cards:
- dot: cyan
title: 1 Quality goals
items:
- "No seat is ever sold twice"
- "Checkout under 2 s at Friday peak"
- "Door check-in works offline"
- dot: slate
title: 2 Constraints
items:
- "Existing box-office POS stays"
- "Card data only at the provider"
- "Team of four; hosted in the EU"
- dot: emerald
title: 10 Quality scenarios
items:
- "500 holds on one show in 1 min: no double sale"
- "Wi-Fi down at the door: scans go on, sync later"
- "Payment fails: seats free again within 8 min"
- dot: rose
title: 11 Risks
items:
- "POS vendor API is slow and undocumented"
- "Forwarded QR e-mails: first scan wins"
- dot: violet
title: 12 Glossary
items:
- "Show: one screening of a film in a hall"
- "Hold: a seat locked for one customer"
- "Check-in: first valid scan of a ticket"
- "POS: point-of-sale till at the box office"
@@ -0,0 +1,57 @@
schema_version: 1
elements:
web:
type: frontend
label: Web Shop
sublabel: React
brand: react
description: >
Programme, seat map and checkout in the browser.
mobile:
type: frontend
label: Mobile App
sublabel: iOS and Android
description: >
The same booking flow as the web shop, plus a ticket wallet.
scanner:
type: frontend
label: Scanner App
sublabel: door phones
description: >
Reads QR codes at the door; checks signatures offline.
booking:
type: backend
label: Booking API
sublabel: Node.js
brand: node-js
description: >
Shows, seat maps, seat holds and orders; the only writer of
the seat inventory.
diagram: 0500-building-blocks/booking.archify
ticketing:
type: backend
label: Ticket Service
sublabel: QR, check-in
description: >
Issues signed QR tickets, sends them by e-mail and records
check-ins at the door.
possync:
type: backend
label: POS Sync
sublabel: adapter
description: >
Exchanges seat sales with the box-office POS in both directions.
seatlock:
type: database
label: Seat Locks
sublabel: Redis
brand: redis
description: >
Short-lived seat holds with a TTL while the customer pays.
db:
type: database
label: Tickets DB
sublabel: PostgreSQL
brand: postgresql
description: >
Shows, seats, orders and tickets; the source of truth.
@@ -0,0 +1,14 @@
schema_version: 1
elements:
customer:
type: external
label: Moviegoer
sublabel: web and mobile
description: >
Picks a show, holds seats and pays; gets the tickets by e-mail.
usher:
type: external
label: Usher
sublabel: door staff
description: >
Scans tickets at the auditorium door with the scanner app.
@@ -0,0 +1,30 @@
schema_version: 1
elements:
tickets:
type: cloud
label: Cinema Tickets
sublabel: the system
description: >
Online booking, seat holds, e-mail tickets and door check-in
for the five cinemas of the fictional Lantern Cinemas chain.
diagram: 0500-building-blocks/level-1.archify
psp:
type: external
label: Payment provider
sublabel: hosted checkout
description: >
Takes card and wallet payments on its own page; we never see
card numbers.
pos:
type: external
label: Box-office POS
sublabel: in each cinema
description: >
Till system at the counter; sells walk-in seats from the same
seat map.
mail:
type: external
label: E-mail service
sublabel: transactional
description: >
Delivers ticket e-mails with QR codes and receipts.
@@ -0,0 +1,35 @@
schema_version: 1
diagram_type: architecture
meta:
title: Technical context
output: technical-context.html
decisions: [mqtt-with-mutual-tls]
components:
- ref: homeowner
pos: [40, 60]
size: [130, 60]
- ref: ops-engineer
pos: [40, 160]
size: [130, 60]
- ref: installer
pos: [40, 260]
size: [130, 60]
- ref: telemetry-platform
pos: [280, 160]
size: [150, 60]
- ref: heat-pump
pos: [540, 60]
size: [130, 60]
- ref: push-service
pos: [540, 160]
size: [130, 60]
- ref: service-desk
pos: [540, 260]
size: [130, 60]
connections:
- { from: homeowner, to: telemetry-platform, label: HTTPS }
- { from: ops-engineer, to: telemetry-platform, label: HTTPS, variant: security }
- { from: installer, to: telemetry-platform, label: HTTPS }
- { 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: service-desk, label: webhook }
@@ -0,0 +1,50 @@
schema_version: 1
diagram_type: architecture
meta:
title: Ingest, level 2
output: ingest.html
components:
- ref: mqtt-broker
pos: [40, 160]
size: [130, 60]
- id: decoder
type: backend
label: Decoder
sublabel: payload v1, v2
pos: [250, 160]
size: [130, 60]
- id: validator
type: backend
label: Validator
sublabel: ranges, clock
pos: [460, 160]
size: [130, 60]
- id: writer
type: backend
label: Batch writer
sublabel: 500 rows
pos: [670, 160]
size: [130, 60]
- id: dead-letter
type: database
label: Dead letters
sublabel: rejected readings
pos: [460, 310]
size: [130, 60]
- ref: tsdb
pos: [880, 160]
size: [130, 60]
- ref: rules
pos: [670, 310]
size: [130, 60]
boundaries:
- kind: region
label: Ingest
wraps: [decoder, validator, writer, dead-letter]
connections:
- { from: mqtt-broker, to: decoder, label: messages, variant: emphasis }
- { from: decoder, to: validator, label: readings, variant: emphasis }
- { from: validator, to: writer, label: valid, variant: emphasis }
- { from: validator, to: dead-letter, label: invalid, variant: dashed, labelAt: [525, 234] }
- { from: writer, to: tsdb, label: COPY, variant: emphasis }
- { from: writer, to: rules, label: stream, labelAt: [735, 234] }
@@ -0,0 +1,77 @@
schema_version: 1
diagram_type: architecture
meta:
title: Building blocks, level 1
output: level-1.html
decisions: [timescaledb-for-telemetry]
components:
- ref: heat-pump
pos: [40, 160]
size: [130, 60]
- ref: installer
pos: [40, 360]
size: [130, 60]
- ref: mqtt-broker
pos: [250, 160]
size: [130, 60]
decisions: [mqtt-with-mutual-tls]
- ref: provisioning
pos: [250, 360]
size: [130, 60]
- ref: rules
pos: [460, 60]
size: [130, 60]
- ref: ingest
pos: [460, 160]
size: [130, 60]
- ref: device-registry
pos: [460, 360]
size: [130, 60]
- ref: tsdb
pos: [670, 160]
size: [130, 60]
- ref: device-api
pos: [670, 360]
size: [130, 60]
- ref: ops-dashboard
pos: [880, 160]
size: [130, 60]
- ref: customer-app
pos: [880, 360]
size: [130, 60]
- ref: push-service
pos: [1090, 60]
size: [130, 60]
- ref: ops-engineer
pos: [1090, 160]
size: [130, 60]
- ref: homeowner
pos: [1090, 360]
size: [130, 60]
boundaries:
- kind: region
label: IoT Telemetry
wraps: [mqtt-broker, provisioning, rules, ingest, device-registry, tsdb, device-api, ops-dashboard, customer-app]
connections:
- { from: heat-pump, to: mqtt-broker, label: publish, variant: emphasis, decisions: [mqtt-with-mutual-tls] }
- { from: mqtt-broker, to: ingest, label: subscribe, variant: emphasis }
- { from: ingest, to: tsdb, label: write, variant: emphasis }
- { from: ingest, to: rules, label: stream }
- { from: ingest, to: device-registry, label: lookup, labelAt: [525, 234] }
- { from: rules, to: push-service, label: alert }
- { from: installer, to: provisioning, label: register }
- { from: provisioning, to: mqtt-broker, label: certs, variant: security }
- { from: provisioning, to: device-registry, label: store }
- { from: device-api, to: tsdb, label: read }
- { from: device-api, to: device-registry, label: read }
- { from: tsdb, to: ops-dashboard, label: query }
- { from: customer-app, to: device-api, label: REST }
- { from: ops-engineer, to: ops-dashboard, label: views }
- { from: homeowner, to: customer-app, label: uses }
cards:
- dot: amber
title: 8 Crosscutting concepts
items:
- Device identity, one X.509 certificate per device
- Idempotent writes, keyed by device id and timestamp
- Observability, metrics and traces on every service
@@ -0,0 +1,20 @@
schema_version: 1
diagram_type: sequence
meta:
title: Device provisioning
output: device-provisioning.html
decisions: [mqtt-with-mutual-tls]
participants:
- { ref: installer }
- { ref: provisioning }
- { ref: device-registry, label: Devices }
- { ref: heat-pump }
- { ref: mqtt-broker }
messages:
- { from: installer, to: provisioning, y: 160, label: register serial, variant: emphasis }
- { from: provisioning, to: device-registry, y: 210, label: store device }
- { from: provisioning, to: installer, y: 260, label: certificate, variant: return }
- { from: installer, to: heat-pump, y: 310, label: load certificate }
- { from: heat-pump, to: mqtt-broker, y: 360, label: connect mTLS, variant: emphasis }
- { from: mqtt-broker, to: provisioning, y: 410, label: check revocation }
- { from: mqtt-broker, to: heat-pump, y: 460, label: CONNACK, variant: return }
@@ -0,0 +1,20 @@
schema_version: 1
diagram_type: sequence
meta:
title: Fault reading to alert
output: telemetry-to-alert.html
participants:
- { ref: heat-pump }
- { ref: mqtt-broker }
- { ref: ingest }
- { ref: tsdb, label: TSDB }
- { ref: rules }
- { ref: push-service }
messages:
- { from: heat-pump, to: mqtt-broker, y: 160, label: publish fault, variant: emphasis }
- { from: mqtt-broker, to: ingest, y: 210, label: deliver QoS 1 }
- { from: ingest, to: tsdb, y: 260, label: write batch }
- { from: tsdb, to: ingest, y: 310, label: committed, variant: return }
- { from: ingest, to: mqtt-broker, y: 360, label: ack, variant: return }
- { from: ingest, to: rules, y: 410, label: reading }
- { from: rules, to: push-service, y: 460, label: send alert, variant: emphasis }
@@ -0,0 +1,44 @@
schema_version: 1
diagram_type: architecture
meta:
title: Deployment, production
output: production.html
decisions: [timescaledb-for-telemetry]
components:
- ref: heat-pump
pos: [40, 160]
size: [130, 60]
- ref: mqtt-broker
pos: [280, 160]
size: [130, 60]
- ref: ingest
pos: [490, 160]
size: [130, 60]
- ref: rules
pos: [490, 60]
size: [130, 60]
- ref: provisioning
pos: [280, 260]
size: [130, 60]
- ref: device-api
pos: [490, 260]
size: [130, 60]
- ref: tsdb
pos: [760, 160]
size: [130, 60]
- ref: device-registry
pos: [760, 260]
size: [130, 60]
boundaries:
- kind: region
label: Kubernetes cluster, eu-central
wraps: [mqtt-broker, ingest, rules, provisioning, device-api]
- kind: security-group
label: Managed DB zone
wraps: [tsdb, device-registry]
connections:
- { from: heat-pump, to: mqtt-broker, label: "8883", variant: emphasis }
- { from: mqtt-broker, to: ingest, label: in-cluster }
- { from: ingest, to: tsdb, label: "5432", variant: emphasis }
- { from: ingest, to: rules, label: gRPC }
- { from: device-api, to: device-registry, label: "5432" }
@@ -0,0 +1,21 @@
schema_version: 1
decision:
id: mqtt-with-mutual-tls
title: MQTT with mutual TLS for device traffic
status: accepted
date: 2026-08-12
elements: [heat-pump, mqtt-broker, provisioning]
diagrams: [0300-context/technical-context.archify, 0500-building-blocks/level-1.archify]
context: >
Heat pumps sit behind home routers on slow and flaky links.
The first prototype polled an HTTPS endpoint and drained
mobile data plans; shared API keys leaked from one device.
decision: >
Devices publish over MQTT 3.1.1 on port 8883. Each device
has its own client certificate issued by Provisioning;
the broker rejects any connection without one.
consequences: >
Small messages and one long-lived connection per device.
Certificates must be rotated and revoked, so Provisioning
is a critical building block. Installers need a step to
register the device before it can connect.
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: timescaledb-for-telemetry
title: TimescaleDB for telemetry
status: accepted
date: 2026-09-03
elements: [tsdb, ingest, ops-dashboard]
diagrams: [0500-building-blocks/level-1.archify, 0700-deployment/production.archify]
context: >
The fleet sends about 4,000 readings a second. A plain
PostgreSQL table grew too fast and dashboards slowed down
after a few weeks of data.
decision: >
Store telemetry in TimescaleDB hypertables with hourly
continuous aggregates. Raw data is kept 90 days, rollups
10 years. The team already runs PostgreSQL.
consequences: >
SQL stays the query language for the API and the
dashboard. Retention jobs must be monitored. Very high
device counts may need sharding later (see risk R1).
+76
View File
@@ -0,0 +1,76 @@
schema_version: 1
model:
id: iot-telemetry
name: IoT Telemetry
description: >
An arc42 example: a fictional heat pump maker collects device telemetry
over MQTT, stores it, raises alerts and shows it to operations and customers.
main.archify holds section 3 business context with cards for sections
1 goals, 2 constraints, 10 quality, 11 risks and 12 glossary;
0300-context the technical context; 0500-building-blocks section 5;
0600-runtime section 6; 0700-deployment section 7; decisions/ section 9;
the crosscutting concepts of section 8 are a card on level-1.
diagram_type: architecture
meta:
title: Business context
output: main.html
components:
- ref: homeowner
pos: [40, 60]
size: [130, 60]
- ref: ops-engineer
pos: [40, 160]
size: [130, 60]
- ref: installer
pos: [40, 260]
size: [130, 60]
- ref: telemetry-platform
pos: [280, 160]
size: [150, 60]
- ref: heat-pump
pos: [540, 60]
size: [130, 60]
- ref: push-service
pos: [540, 160]
size: [130, 60]
- ref: service-desk
pos: [540, 260]
size: [130, 60]
connections:
- { from: homeowner, to: telemetry-platform, label: checks }
- { from: ops-engineer, to: telemetry-platform, label: monitors }
- { from: installer, to: telemetry-platform, label: registers }
- { from: heat-pump, to: telemetry-platform, label: telemetry, variant: emphasis }
- { from: telemetry-platform, to: push-service, label: alerts }
- { from: telemetry-platform, to: service-desk, label: tickets }
cards:
- dot: cyan
title: 1 Quality goals
items:
- Reliability, no telemetry lost once accepted
- Timeliness, alert within 60 s of a fault
- Security, every device authenticated
- dot: slate
title: 2 Constraints
items:
- Devices speak MQTT only; firmware by a partner
- Data stays in one EU region
- Team of six; PostgreSQL skills in house
- dot: emerald
title: 10 Quality scenarios
items:
- A fault code reaches the homeowner within 60 s
- Broker restart, devices reconnect, no data lost
- A stolen certificate is revoked within 15 min
- dot: rose
title: 11 Risks
items:
- R1 fleet growth beyond one TSDB node
- R2 partner firmware delays certificate rotation
- dot: violet
title: 12 Glossary
items:
- Telemetry, readings a device sends
- Rollup, hourly aggregate of telemetry
- Provisioning, giving a device its identity
- Fault code, error number from the device
@@ -0,0 +1,61 @@
schema_version: 1
elements:
mqtt-broker:
type: messagebus
label: MQTT broker
sublabel: mutual TLS
description: >
Terminates device connections; one topic per device.
ingest:
type: backend
label: Ingest
sublabel: Go
brand: go
description: >
Decodes, validates and stores telemetry; forwards it to the rules.
diagram: 0500-building-blocks/ingest.archify
tsdb:
type: database
label: Time series DB
sublabel: TimescaleDB
brand: postgresql
description: >
Raw telemetry for 90 days and hourly rollups for 10 years.
rules:
type: backend
label: Rules engine
sublabel: alerting
description: >
Evaluates thresholds and fault codes; raises alerts.
provisioning:
type: security
label: Provisioning
sublabel: device certs
description: >
Registers devices and issues their client certificates.
device-registry:
type: database
label: Device registry
sublabel: PostgreSQL
brand: postgresql
description: >
Devices, owners, firmware versions and certificate state.
device-api:
type: backend
label: Device API
sublabel: REST
description: >
Serves telemetry and device data to the customer app.
ops-dashboard:
type: frontend
label: Ops dashboard
sublabel: Grafana
brand: grafana
description: >
Fleet view for the operations team, read only on the TSDB.
customer-app:
type: frontend
label: Customer app
sublabel: mobile
description: >
Shows the homeowner their heat pump and its alerts.
@@ -0,0 +1,20 @@
schema_version: 1
elements:
homeowner:
type: external
label: Homeowner
sublabel: customer app
description: >
Owns a heat pump; checks temperatures and gets fault alerts.
ops-engineer:
type: external
label: Ops engineer
sublabel: operations team
description: >
Watches the fleet, handles alerts and plans service visits.
installer:
type: external
label: Installer
sublabel: field service
description: >
Mounts heat pumps on site and registers them with the platform.
@@ -0,0 +1,27 @@
schema_version: 1
elements:
telemetry-platform:
type: cloud
label: IoT Telemetry
sublabel: the system
description: >
Everything we build and run; broker, ingest, stores, rules and apps.
diagram: 0500-building-blocks/level-1.archify
heat-pump:
type: external
label: Heat pump
sublabel: field device
description: >
Sends telemetry every 30 seconds over MQTT; built by a partner.
push-service:
type: external
label: Push service
sublabel: mobile push
description: >
Delivers push notifications to the customer app.
service-desk:
type: external
label: Service desk
sublabel: ticketing
description: >
Opens service tickets for faults that need a visit.
@@ -0,0 +1,26 @@
schema_version: 1
diagram_type: architecture
meta:
title: Technical context
output: technical-context.html
components:
- ref: engineer
pos: [40, 60]
size: [130, 60]
- ref: wiki-admin
pos: [40, 260]
size: [130, 60]
- ref: wiki
pos: [280, 160]
size: [150, 60]
- ref: idp
pos: [520, 60]
size: [150, 60]
- ref: object-store
pos: [520, 260]
size: [150, 60]
connections:
- { from: engineer, to: wiki, label: HTTPS, variant: emphasis }
- { from: wiki-admin, to: wiki, label: kubectl }
- { from: wiki, to: idp, label: OIDC, variant: security }
- { from: wiki, to: object-store, label: S3 API }
@@ -0,0 +1,47 @@
schema_version: 1
diagram_type: architecture
meta:
title: Building blocks - level 1
output: level-1.html
decisions: [postgres-full-text-search]
components:
- ref: engineer
pos: [40, 160]
size: [130, 60]
- ref: web
pos: [250, 160]
size: [130, 60]
- ref: api
pos: [460, 160]
size: [130, 60]
- ref: db
pos: [460, 360]
size: [130, 60]
- ref: backup-job
pos: [670, 360]
size: [130, 60]
- ref: idp
pos: [670, 60]
size: [130, 60]
- ref: object-store
pos: [880, 160]
size: [130, 60]
boundaries:
- kind: region
label: Team Wiki
wraps: [web, api, db, backup-job]
connections:
- { from: engineer, to: web, label: HTTPS, variant: emphasis }
- { from: web, to: api, label: JSON, variant: emphasis }
- { from: api, to: db, label: SQL, labelAt: [525, 234], decisions: [postgres-full-text-search] }
- { from: api, to: idp, label: OIDC, variant: security }
- { from: api, to: object-store, label: files }
- { from: backup-job, to: db, label: dump }
- { from: backup-job, to: object-store, label: upload }
cards:
- dot: amber
title: 8 Crosscutting concepts
items:
- Access - every request checked against space groups
- Logs - JSON to stdout, collected by the cluster
- Config - environment variables, secrets from the vault
@@ -0,0 +1,55 @@
schema_version: 1
diagram_type: architecture
meta:
title: Building blocks - level 2, Wiki API
output: wiki-api.html
components:
- ref: web
pos: [40, 160]
size: [130, 60]
- id: auth
type: security
label: Auth
sublabel: OIDC session
pos: [250, 60]
size: [130, 60]
- id: pages
type: backend
label: Pages
sublabel: revisions
pos: [250, 160]
size: [130, 60]
- id: search
type: backend
label: Search
sublabel: full-text
pos: [250, 260]
size: [130, 60]
decisions: [postgres-full-text-search]
- id: attachments
type: backend
label: Attachments
pos: [250, 360]
size: [130, 60]
- ref: idp
pos: [460, 60]
size: [130, 60]
- ref: db
pos: [460, 210]
size: [130, 60]
- ref: object-store
pos: [460, 360]
size: [130, 60]
boundaries:
- kind: region
label: Wiki API
wraps: [auth, pages, search, attachments]
connections:
- { from: web, to: auth, label: sign in, variant: security }
- { from: web, to: pages, label: edit, labelAt: [222, 182] }
- { from: web, to: search, label: query }
- { from: web, to: attachments, label: upload }
- { from: auth, to: idp, label: OIDC, variant: security }
- { from: pages, to: db, label: SQL }
- { from: search, to: db, label: SQL }
- { from: attachments, to: object-store, label: S3 }
@@ -0,0 +1,20 @@
schema_version: 1
diagram_type: sequence
meta:
title: Runtime - search and open a page
output: search-page.html
participants:
- ref: engineer
- ref: web
- ref: api
- { ref: db, label: DB }
- { ref: object-store, label: Object store }
messages:
- { from: engineer, to: web, y: 160, label: type query, variant: emphasis }
- { from: web, to: api, y: 210, label: GET /search }
- { from: api, to: db, y: 260, label: full-text query }
- { from: db, to: api, y: 310, label: ranked pages, variant: return }
- { from: api, to: web, y: 360, label: results, variant: return }
- { from: web, to: api, y: 410, label: GET /pages/id }
- { from: api, to: object-store, y: 460, label: signed URL }
- { from: api, to: web, y: 510, label: page, variant: return }
@@ -0,0 +1,19 @@
schema_version: 1
diagram_type: sequence
meta:
title: Runtime - sign in
output: sign-in.html
decisions: [sso-only-via-company-idp]
participants:
- ref: engineer
- ref: web
- ref: api
- { ref: idp, label: Company IdP }
messages:
- { from: engineer, to: web, y: 160, label: open wiki, variant: emphasis }
- { from: web, to: api, y: 210, label: GET /session }
- { from: api, to: web, y: 260, label: redirect, variant: return }
- { from: web, to: idp, y: 310, label: OIDC login, variant: security }
- { from: idp, to: api, y: 360, label: code, variant: return }
- { from: api, to: idp, y: 410, label: token exchange, variant: security }
- { from: api, to: web, y: 460, label: session cookie, variant: return }
@@ -0,0 +1,48 @@
schema_version: 1
diagram_type: architecture
meta:
title: Deployment - production
output: production.html
components:
- ref: engineer
pos: [40, 160]
size: [130, 60]
- id: ingress
type: security
label: Ingress
sublabel: TLS
pos: [250, 160]
size: [130, 60]
- ref: web
pos: [460, 60]
size: [130, 60]
- ref: api
pos: [460, 160]
size: [130, 60]
- ref: db
pos: [670, 160]
size: [130, 60]
- ref: backup-job
pos: [670, 360]
size: [130, 60]
- ref: idp
pos: [460, 460]
size: [130, 60]
- ref: object-store
pos: [880, 460]
size: [130, 60]
boundaries:
- kind: region
label: Company cluster
wraps: [ingress, web, api, db, backup-job]
- kind: security-group
label: Namespace team-wiki
wraps: [web, api, db, backup-job]
connections:
- { from: engineer, to: ingress, label: HTTPS, variant: emphasis }
- { from: ingress, to: web, label: static }
- { from: ingress, to: api, label: /api, variant: emphasis }
- { from: api, to: db, label: SQL }
- { from: api, to: idp, label: OIDC, variant: security, labelAt: [525, 234] }
- { from: backup-job, to: db, label: dump }
- { from: backup-job, to: object-store, label: S3 }
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: postgres-full-text-search
title: PostgreSQL full-text search, no search engine
status: accepted
date: 2026-10-05
elements: [api, db]
diagrams: [0500-building-blocks/level-1.archify, 0500-building-blocks/wiki-api.archify]
context: >
The wiki holds a few thousand pages for one team. A
separate search engine would be one more service to run,
back up and keep in sync.
decision: >
Search uses PostgreSQL full-text search on a generated
column of each page. The API queries it in the same
transaction as the page data.
consequences: >
Results are never stale and backups cover search too.
Ranking is simpler than a search engine. Revisit when
the wiki grows past 100000 pages.
@@ -0,0 +1,19 @@
schema_version: 1
decision:
id: sso-only-via-company-idp
title: Sign-in only through the company identity provider
status: accepted
date: 2026-10-12
elements: [idp, api, web]
diagrams: [0300-context/technical-context.archify, 0600-runtime/sign-in.archify]
context: >
Leavers kept access to the old wiki because it had its
own passwords. Nobody owned the user list.
decision: >
The wiki has no local accounts. Users sign in with OIDC
at the company identity provider; team groups map to
wiki spaces.
consequences: >
Offboarding removes wiki access at once. The wiki cannot
be used when the identity provider is down. The admin
needs a break-glass account kept in the vault.
+66
View File
@@ -0,0 +1,66 @@
schema_version: 1
model:
id: team-wiki
name: Team Wiki
description: >
An arc42 example: a small self-hosted wiki for an engineering
team. Sections 1 goals, 2 constraints, 3 business context,
10 quality, 11 risks and 12 glossary are on this overview;
0300-context holds 3 technical context, 0500-building-blocks
5 building blocks, 0600-runtime 6 runtime, 0700-deployment
7 deployment and decisions 9 architecture decisions.
diagram_type: architecture
meta:
title: Team Wiki - business context
output: main.html
decisions: [sso-only-via-company-idp]
components:
- ref: engineer
pos: [40, 60]
size: [130, 60]
- ref: wiki-admin
pos: [40, 260]
size: [130, 60]
- ref: wiki
pos: [280, 160]
size: [150, 60]
- ref: idp
pos: [520, 60]
size: [150, 60]
- ref: object-store
pos: [520, 260]
size: [150, 60]
connections:
- { from: engineer, to: wiki, label: reads, variant: emphasis }
- { from: wiki-admin, to: wiki, label: runs }
- { from: wiki, to: idp, label: sign-in, variant: security }
- { from: wiki, to: object-store, label: files }
cards:
- dot: cyan
title: 1 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: slate
title: 2 Constraints
items:
- Runs on the company Kubernetes cluster
- Sign-in only via the company identity provider
- dot: emerald
title: 10 Quality scenarios
items:
- A search over 5000 pages answers in under 1 s
- A leaver loses access within 5 minutes
- The admin restores last night's state in 30 minutes
- dot: rose
title: 11 Risks
items:
- Identity provider outage blocks all sign-ins
- Backups untested until a restore drill is run
- dot: violet
title: 12 Glossary
items:
- Space - pages of one team, mapped to a group
- Revision - one saved version of a page
- Attachment - a file stored beside a page
@@ -0,0 +1,33 @@
schema_version: 1
elements:
web:
type: frontend
label: Web App
sublabel: React
brand: react
description: >
Page editor, viewer and search box in the browser; talks
only to the API.
api:
type: backend
label: Wiki API
sublabel: REST
description: >
Pages, search, attachments and access checks; the only
writer of the database.
diagram: 0500-building-blocks/wiki-api.archify
db:
type: database
label: Database
sublabel: PostgreSQL
brand: postgresql
description: >
Pages, revisions, users and the full-text index; the
source of truth.
backup-job:
type: backend
label: Backup job
sublabel: nightly
description: >
Dumps the database at 02:00 and writes it to the backup
bucket; keeps 30 days.
@@ -0,0 +1,15 @@
schema_version: 1
elements:
engineer:
type: external
label: Engineer
sublabel: team member
description: >
Reads and edits pages, searches, attaches files; signs in
with the company account.
wiki-admin:
type: external
label: Wiki admin
sublabel: platform team
description: >
Runs the wiki, restores backups and manages spaces.
@@ -0,0 +1,24 @@
schema_version: 1
elements:
wiki:
type: cloud
label: Team Wiki
sublabel: the system
description: >
Everything we build and run; web app, API, database and
the backup job.
diagram: 0500-building-blocks/level-1.archify
idp:
type: security
label: Identity provider
sublabel: company SSO
description: >
The company identity provider; signs users in over OIDC
and owns team membership.
object-store:
type: external
label: Object storage
sublabel: S3-compatible
description: >
Company object storage; holds attachments and nightly
backups in separate buckets.
@@ -0,0 +1,36 @@
schema_version: 1
diagram_type: architecture
meta:
title: Business actors and services
output: business-services.html
components:
- ref: hr
pos: [40, 60]
size: [130, 60]
- ref: manager
pos: [40, 160]
size: [130, 60]
- ref: service-desk
pos: [40, 260]
size: [130, 60]
- ref: onboard
pos: [250, 160]
size: [150, 60]
diagram: 0100-business/onboard-employee.archify
- ref: onboarding-svc
pos: [480, 60]
size: [130, 60]
- ref: workplace-svc
pos: [480, 260]
size: [130, 60]
- ref: new-hire
pos: [690, 160]
size: [130, 60]
connections:
- { from: hr, to: onboard, label: assigned }
- { from: manager, to: onboard, label: assigned }
- { from: service-desk, to: onboard, label: assigned }
- { from: onboard, to: onboarding-svc, label: realizes }
- { from: onboard, to: workplace-svc, label: realizes }
- { from: onboarding-svc, to: new-hire, label: serves, variant: emphasis }
- { from: workplace-svc, to: new-hire, label: serves }
@@ -0,0 +1,25 @@
schema_version: 2
diagram_type: workflow
meta:
title: Onboard Employee
output: onboard-employee.html
viewBox: [1040, 660]
lanes:
- { id: hr-lane, label: HR }
- { id: manager-lane, label: Hiring manager }
- { id: desk-lane, label: IT service desk }
- { id: hire-lane, label: New hire }
mainPath: [contract-signed, record, request, create-accounts, issue-laptop, first-day]
nodes:
- { ref: contract-signed, lane: hr-lane, col: 0, width: 120 }
- { id: record, lane: hr-lane, col: 1, type: backend, label: Create record, tag: Business Process }
- { id: request, lane: manager-lane, col: 2, type: frontend, label: Request access, tag: Business Process }
- { ref: create-accounts, lane: desk-lane, col: 3, width: 120, decisions: [hr-system-drives-identity] }
- { ref: issue-laptop, lane: desk-lane, col: 4, decisions: [zero-touch-laptops] }
- { ref: first-day, lane: hire-lane, col: 5 }
edges:
- { from: contract-signed, to: record, label: triggers }
- { from: record, to: request, label: notifies }
- { from: request, to: create-accounts, label: ticket }
- { from: create-accounts, to: issue-laptop, label: then }
- { from: issue-laptop, to: first-day, label: hand over }
@@ -0,0 +1,44 @@
schema_version: 1
diagram_type: architecture
meta:
title: Application components, services and data
output: application-components.html
components:
- ref: provisioning-svc
pos: [250, 60]
size: [130, 60]
- ref: portal
pos: [460, 60]
size: [130, 60]
- ref: device-svc
pos: [670, 60]
size: [130, 60]
- ref: hr-system
pos: [40, 160]
size: [130, 60]
- ref: iam
pos: [250, 160]
size: [130, 60]
- ref: itsm
pos: [460, 160]
size: [130, 60]
- ref: mdm-app
pos: [670, 160]
size: [130, 60]
- ref: employee-record
pos: [40, 260]
size: [130, 60]
- ref: account
pos: [250, 260]
size: [130, 60]
- ref: asset
pos: [460, 260]
size: [130, 60]
connections:
- { from: iam, to: provisioning-svc, label: realizes }
- { from: itsm, to: portal, label: realizes }
- { from: mdm-app, to: device-svc, label: realizes }
- { from: hr-system, to: employee-record, label: writes, labelAt: [105, 234] }
- { from: iam, to: account, label: writes, labelAt: [315, 234] }
- { from: itsm, to: asset, label: writes, labelAt: [525, 234] }
- { from: mdm-app, to: asset, label: reads }
@@ -0,0 +1,29 @@
schema_version: 1
diagram_type: architecture
meta:
title: Application cooperation
output: application-cooperation.html
decisions: [hr-system-drives-identity]
components:
- ref: hr-system
pos: [40, 160]
size: [130, 60]
- ref: iam
pos: [250, 60]
size: [130, 60]
diagram: 0200-application/application-components.archify
- ref: itsm
pos: [250, 260]
size: [130, 60]
- ref: collab
pos: [460, 60]
size: [130, 60]
- ref: mdm-app
pos: [460, 260]
size: [130, 60]
connections:
- { from: hr-system, to: iam, label: joiner event, variant: emphasis, decisions: [hr-system-drives-identity] }
- { from: hr-system, to: itsm, label: laptop request }
- { from: iam, to: collab, label: provisions }
- { from: iam, to: mdm-app, label: users }
- { from: itsm, to: mdm-app, label: assigns }
@@ -0,0 +1,42 @@
schema_version: 1
diagram_type: architecture
meta:
title: Technology layer
output: infrastructure.html
decisions: [zero-touch-laptops]
components:
- ref: hr-saas
pos: [40, 60]
size: [130, 60]
- ref: sso
pos: [250, 60]
size: [130, 60]
- ref: directory
pos: [460, 60]
size: [130, 60]
- ref: itsm-saas
pos: [40, 160]
size: [130, 60]
- ref: mdm-saas
pos: [460, 160]
size: [130, 60]
- ref: laptop
pos: [460, 360]
size: [130, 60]
- ref: office-net
pos: [250, 360]
size: [130, 60]
boundaries:
- kind: region
label: SaaS tenants
wraps: [hr-saas, sso, directory, itsm-saas, mdm-saas]
- kind: security-group
label: Office
wraps: [laptop, office-net]
connections:
- { from: directory, to: sso, label: realizes }
- { from: sso, to: hr-saas, label: serves }
- { from: sso, to: itsm-saas, label: serves }
- { from: directory, to: mdm-saas, label: devices, labelAt: [525, 134] }
- { from: mdm-saas, to: laptop, label: manages, labelAt: [525, 234], variant: security, decisions: [zero-touch-laptops] }
- { from: laptop, to: office-net, label: connects }
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: hr-system-drives-identity
title: The HR system drives identity
status: accepted
date: 2026-09-08
elements: [hr-system, iam, account, employee-record]
diagrams: [0200-application/application-cooperation.archify, 0100-business/onboard-employee.archify]
context: >
Accounts were created by hand from emails sent by HR.
New hires waited days for access, and leavers kept
accounts for weeks after their last day.
decision: >
The HR system is the source of truth for joiners and
leavers. Identity management reads its events and
creates, changes and disables accounts automatically.
consequences: >
No account without an employee record. HR data quality
now decides access, so start dates must be right. The
service desk only handles exceptions.
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: zero-touch-laptops
title: Zero-touch enrollment for laptops
status: accepted
date: 2026-10-05
elements: [mdm-app, mdm-saas, laptop, device-svc, asset]
diagrams: [0300-technology/infrastructure.archify, 0100-business/onboard-employee.archify]
context: >
Every laptop was imaged by hand at the service desk,
which took half a day per device and left settings that
differed from one laptop to the next.
decision: >
Laptops are registered in device management at purchase.
They configure themselves at first sign-in over any
internet connection; the office network needs no VPN.
consequences: >
Laptops can be shipped straight to the new hire. A lost
laptop is wiped remotely. Setup depends on the MDM
tenant being reachable on the first day.
@@ -0,0 +1,59 @@
schema_version: 1
model:
id: hr-onboarding
name: HR Onboarding
description: >
An ArchiMate example: how a new employee is onboarded, from signed
contract to first day. Business, application and technology layers
map to folders; each element's ArchiMate type is shown as its tag.
diagram_type: architecture
meta:
title: HR Onboarding - layered view
output: main.html
components:
- ref: new-hire
pos: [40, 70]
size: [170, 60]
- ref: onboarding-svc
pos: [270, 70]
size: [170, 60]
- ref: onboard
pos: [500, 70]
size: [170, 60]
- ref: portal
pos: [40, 270]
size: [170, 60]
- ref: iam
pos: [270, 270]
size: [170, 60]
- ref: provisioning-svc
pos: [500, 270]
size: [170, 60]
- ref: office-net
pos: [40, 470]
size: [170, 60]
- ref: sso
pos: [270, 470]
size: [170, 60]
- ref: directory
pos: [500, 470]
size: [170, 60]
boundaries:
- kind: region
label: Business layer
wraps: [new-hire, onboarding-svc, onboard]
- kind: region
label: Application layer
wraps: [portal, iam, provisioning-svc]
- kind: region
label: Technology layer
wraps: [office-net, sso, directory]
connections:
- { from: onboarding-svc, to: new-hire, label: serves, variant: emphasis }
- { from: onboard, to: onboarding-svc, label: realizes }
- { from: portal, to: new-hire, label: serves }
- { from: provisioning-svc, to: onboard, label: serves }
- { from: iam, to: provisioning-svc, label: realizes }
- { from: office-net, to: portal, label: serves }
- { from: sso, to: iam, label: serves }
- { from: directory, to: sso, label: realizes }
@@ -0,0 +1,81 @@
schema_version: 1
elements:
hr-system:
type: backend
label: HR system
sublabel: App Component
tag: App Component
description: >
Holds employee records and contracts; the source of truth for joiners.
diagram: 0200-application/application-cooperation.archify
iam:
type: security
label: Identity mgmt
sublabel: App Component
tag: App Component
description: >
Creates and removes accounts from HR events; single sign-on for all apps.
diagram: 0200-application/application-components.archify
itsm:
type: backend
label: IT service mgmt
sublabel: App Component
tag: App Component
description: >
Tickets, requests and the asset inventory of the IT service desk.
mdm-app:
type: backend
label: Device mgmt
sublabel: App Component
tag: App Component
description: >
Enrolls laptops, pushes policies and apps, wipes lost devices.
collab:
type: backend
label: Mail and chat
sublabel: App Component
tag: App Component
description: >
Mailbox, calendar and chat; accounts provisioned by identity management.
provisioning-svc:
type: cloud
label: Provisioning
sublabel: App Service
tag: App Service
description: >
Account create, change and disable, triggered by the HR system.
device-svc:
type: cloud
label: Enrollment
sublabel: App Service
tag: App Service
description: >
Zero-touch enrollment; the laptop configures itself at first sign-in.
portal:
type: frontend
label: Service portal
sublabel: App Interface
tag: App Interface
description: >
Where managers request equipment and access for a new hire.
employee-record:
type: database
label: Employee record
sublabel: Data Object
tag: Data Object
description: >
Name, role, department, manager and start date of an employee.
account:
type: database
label: Account
sublabel: Data Object
tag: Data Object
description: >
User id, groups and licences of one person.
asset:
type: database
label: Asset
sublabel: Data Object
tag: Data Object
description: >
A laptop with serial number, owner and enrollment state.
@@ -0,0 +1,81 @@
schema_version: 1
elements:
new-hire:
type: external
label: New hire
sublabel: Business Actor
tag: Business Actor
description: >
The person joining the company; signs the contract and starts on day one.
hr:
type: external
label: HR officer
sublabel: Business Role
tag: Business Role
description: >
Prepares the contract and the employee record; owns the onboarding process.
manager:
type: external
label: Hiring manager
sublabel: Business Role
tag: Business Role
description: >
Requests access and equipment for the new hire and plans the first day.
service-desk:
type: external
label: IT service desk
sublabel: Business Role
tag: Business Role
description: >
Creates accounts, prepares the laptop and hands it over.
onboard:
type: backend
label: Onboard Employee
sublabel: Business Process
tag: Business Process
description: >
From signed contract to a productive first day.
diagram: 0100-business/onboard-employee.archify
onboarding-svc:
type: cloud
label: Onboarding
sublabel: Business Service
tag: Business Service
description: >
What the new hire gets from HR; contract, record, welcome and first day plan.
diagram: 0100-business/business-services.archify
workplace-svc:
type: cloud
label: Workplace setup
sublabel: Business Service
tag: Business Service
description: >
Accounts and a ready laptop, delivered by the IT service desk.
contract-signed:
type: messagebus
label: Contract signed
sublabel: Business Event
tag: Business Event
description: >
Starts the process; HR records the start date.
create-accounts:
type: backend
label: Create accounts
sublabel: Business Process
tag: Business Process
description: >
Service desk confirms the accounts the identity system provisioned.
issue-laptop:
type: backend
label: Issue laptop
sublabel: Business Process
tag: Business Process
description: >
An enrolled laptop is assigned and handed over.
first-day:
type: messagebus
label: First day
sublabel: Business Event
tag: Business Event
description: >
The new hire signs in for the first time; the process ends.
@@ -0,0 +1,53 @@
schema_version: 1
elements:
hr-saas:
type: cloud
label: HR tenant
sublabel: Node
tag: Node
description: >
The SaaS tenant that runs the HR system.
diagram: 0300-technology/infrastructure.archify
itsm-saas:
type: cloud
label: ITSM tenant
sublabel: Node
tag: Node
description: >
The SaaS tenant that runs IT service management.
mdm-saas:
type: cloud
label: MDM tenant
sublabel: Node
tag: Node
description: >
The SaaS tenant that runs device management.
directory:
type: backend
label: Directory
sublabel: System Software
tag: System Software
description: >
Cloud directory service; users, groups and devices.
diagram: 0300-technology/infrastructure.archify
sso:
type: security
label: Single sign-on
sublabel: Tech Service
tag: Tech Service
description: >
SAML and OIDC sign-in for every SaaS tenant, backed by the directory.
laptop:
type: frontend
label: Laptop
sublabel: Device
tag: Device
description: >
Company laptop, enrolled before it is handed over.
office-net:
type: messagebus
label: Office network
sublabel: Network
tag: Network
description: >
Wired and wireless network of the office; internet only, no VPN.
@@ -0,0 +1,31 @@
schema_version: 1
diagram_type: architecture
meta:
title: Business layer
output: business-layer.html
components:
- ref: customer
pos: [40, 160]
size: [130, 60]
- ref: insurant
pos: [250, 160]
size: [130, 60]
- ref: claim-registration
pos: [460, 60]
size: [150, 60]
- ref: claim-payment
pos: [460, 260]
size: [150, 60]
- ref: handle-claim
pos: [690, 160]
size: [150, 60]
- ref: claims-handler
pos: [920, 160]
size: [130, 60]
connections:
- { from: customer, to: insurant, label: assigned to }
- { from: claim-registration, to: insurant, label: serves, variant: emphasis }
- { from: claim-payment, to: insurant, label: serves }
- { from: handle-claim, to: claim-registration, label: realizes }
- { from: handle-claim, to: claim-payment, label: realizes }
- { from: claims-handler, to: handle-claim, label: assigned to }
@@ -0,0 +1,29 @@
schema_version: 2
diagram_type: workflow
meta:
title: Handle Claim
output: handle-claim.html
viewBox: [840, 534]
lanes:
- { id: insurant-lane, label: Insurant }
- { id: handler-lane, label: Claims handler }
- { id: systems-lane, label: Applications }
mainPath: [damage, register, accept, valuate, pay]
nodes:
- { id: damage, lane: insurant-lane, col: 0, type: external, label: Damage occurs, tag: Business Event }
- { ref: insurant, lane: insurant-lane, col: 5 }
- { id: register, lane: handler-lane, col: 1, type: backend, label: Register, tag: Business Process }
- { id: accept, lane: handler-lane, col: 2, type: security, label: Accept, sublabel: check coverage, tag: Business Process }
- { id: valuate, lane: handler-lane, col: 3, type: backend, label: Valuate, tag: Business Process }
- { id: pay, lane: handler-lane, col: 4, type: backend, label: Pay, tag: Business Process }
- { ref: claims-app, lane: systems-lane, col: 2 }
- { ref: payment, lane: systems-lane, col: 4 }
edges:
- { from: damage, to: register, label: report }
- { from: register, to: accept }
- { from: accept, to: valuate, label: covered }
- { from: valuate, to: pay, label: approved }
- { from: pay, to: insurant, label: paid }
- { from: accept, to: insurant, label: rejected, role: branch, variant: dashed }
- { from: claims-app, to: accept, label: serves, role: async }
- { from: payment, to: pay, label: serves, role: async }
@@ -0,0 +1,32 @@
schema_version: 1
diagram_type: architecture
meta:
title: Application cooperation
output: application-cooperation.html
components:
- ref: portal
pos: [40, 260]
size: [130, 60]
- ref: claims-app
pos: [250, 260]
size: [130, 60]
- ref: crm
pos: [250, 60]
size: [130, 60]
- ref: policy-admin
pos: [460, 160]
size: [130, 60]
decisions: [policies-stay-on-mainframe]
- ref: doc-mgmt
pos: [460, 360]
size: [130, 60]
decisions: [documents-in-object-storage]
- ref: payment
pos: [250, 460]
size: [130, 60]
connections:
- { from: portal, to: claims-app, label: claims, variant: emphasis }
- { from: crm, to: claims-app, label: customer, labelAt: [315, 134] }
- { from: policy-admin, to: claims-app, label: coverage, decisions: [policies-stay-on-mainframe] }
- { from: claims-app, to: doc-mgmt, label: documents }
- { from: claims-app, to: payment, label: payments, labelAt: [315, 334] }
@@ -0,0 +1,36 @@
schema_version: 1
diagram_type: architecture
meta:
title: Application structure
output: application-structure.html
components:
- ref: portal
pos: [40, 160]
size: [130, 60]
- ref: claims-admin
pos: [250, 160]
size: [130, 60]
- ref: claims-app
pos: [460, 160]
size: [130, 60]
- ref: claim
pos: [460, 360]
size: [130, 60]
- ref: crm
pos: [670, 60]
size: [130, 60]
- ref: customer-file
pos: [880, 60]
size: [130, 60]
- ref: policy-admin
pos: [670, 260]
size: [130, 60]
- ref: policy
pos: [880, 260]
size: [130, 60]
connections:
- { from: claims-admin, to: portal, label: serves, variant: emphasis }
- { from: claims-app, to: claims-admin, label: realizes }
- { from: claims-app, to: claim, label: accesses, labelAt: [525, 234] }
- { from: crm, to: customer-file, label: accesses }
- { from: policy-admin, to: policy, label: accesses }
@@ -0,0 +1,45 @@
schema_version: 1
diagram_type: architecture
meta:
title: Technology layer
output: infrastructure.html
components:
- ref: dmz
pos: [40, 160]
size: [130, 60]
- ref: doc-store
pos: [250, 60]
size: [130, 60]
decisions: [documents-in-object-storage]
- ref: app-servers
pos: [250, 160]
size: [130, 60]
- ref: db-service
pos: [250, 260]
size: [130, 60]
- ref: db-cluster
pos: [250, 360]
size: [130, 60]
- ref: mq
pos: [460, 160]
size: [130, 60]
- ref: mainframe
pos: [670, 160]
size: [130, 60]
decisions: [policies-stay-on-mainframe]
- ref: dc-lan
pos: [670, 360]
size: [130, 60]
boundaries:
- kind: region
label: Data centre
wraps: [doc-store, app-servers, db-service, db-cluster, mq, mainframe, dc-lan]
connections:
- { from: dmz, to: app-servers, label: HTTPS, variant: security }
- { from: app-servers, to: doc-store, label: S3 API }
- { from: db-service, to: app-servers, label: serves }
- { from: db-cluster, to: db-service, label: realizes }
- { from: app-servers, to: mq, label: MQ }
- { from: mq, to: mainframe, label: bridge }
- { from: db-cluster, to: dc-lan, label: LAN }
- { from: mainframe, to: dc-lan, label: LAN, labelAt: [735, 234] }
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: policies-stay-on-mainframe
title: Policies stay on the mainframe
status: accepted
date: 2026-09-08
elements: [policy-admin, mainframe, mq, claims-app]
diagrams: [0200-application/application-cooperation.archify, 0300-technology/infrastructure.archify]
context: >
The new claims application needs coverage data for every
claim. Policy administration has run on the mainframe for
twenty years and a migration was estimated at three years.
decision: >
Policy administration stays on the mainframe. The claims
application reads coverage through the message queue bridge
and never copies policies into its own database.
consequences: >
Accepting a claim depends on the mainframe being up during
office hours. The queue bridge must be kept and tested.
A later migration needs its own decision.
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: documents-in-object-storage
title: Claim documents in object storage
status: accepted
date: 2026-10-14
elements: [doc-mgmt, doc-store, claims-app]
diagrams: [0200-application/application-cooperation.archify, 0300-technology/infrastructure.archify]
context: >
Photos and expert reports were stored as blobs in the claims
database. The database grew by forty gigabytes a month and
backups no longer fitted the nightly window.
decision: >
Document management stores every file in the document store
and keeps only a reference in the claim. The claims
application never writes files itself.
consequences: >
Database backups shrink and stay fast. Documents get their own
retention rules. A claim and its files are restored from two
places after an incident.
@@ -0,0 +1,59 @@
schema_version: 1
model:
id: insurance-claims
name: Insurance Claims
description: >
ArchiMate example: how Halbrook Mutual handles claims. The business,
application and technology layers are mapped to folders, and each
element's ArchiMate type is shown as its tag.
diagram_type: architecture
meta:
title: Insurance Claims - layered view
output: main.html
components:
- ref: insurant
pos: [40, 70]
size: [170, 60]
- ref: claim-registration
pos: [270, 70]
size: [170, 60]
- ref: handle-claim
pos: [500, 70]
size: [170, 60]
- ref: portal
pos: [40, 270]
size: [170, 60]
- ref: claims-app
pos: [270, 270]
size: [170, 60]
- ref: claims-admin
pos: [500, 270]
size: [170, 60]
- ref: dmz
pos: [40, 470]
size: [170, 60]
- ref: app-servers
pos: [270, 470]
size: [170, 60]
- ref: db-cluster
pos: [500, 470]
size: [170, 60]
boundaries:
- kind: region
label: Business layer
wraps: [insurant, claim-registration, handle-claim]
- kind: region
label: Application layer
wraps: [portal, claims-app, claims-admin]
- kind: region
label: Technology layer
wraps: [dmz, app-servers, db-cluster]
connections:
- { from: claim-registration, to: insurant, label: serves, variant: emphasis }
- { from: handle-claim, to: claim-registration, label: realizes }
- { from: portal, to: insurant, label: serves }
- { from: claims-admin, to: handle-claim, label: serves }
- { from: claims-app, to: claims-admin, label: realizes }
- { from: dmz, to: portal, label: serves }
- { from: app-servers, to: claims-app, label: serves }
- { from: db-cluster, to: app-servers, label: serves }
@@ -0,0 +1,76 @@
schema_version: 1
elements:
portal:
type: frontend
label: Customer Portal
sublabel: App Interface
tag: App Interface
description: >
Web and mobile front end where customers report claims and
follow their status.
claims-admin:
type: cloud
label: Claims Admin
sublabel: App Service
tag: App Service
description: >
Register, look up and update claims; used by the portal and
by claims handlers.
claims-app:
type: backend
label: Claims App
sublabel: App Component
tag: App Component
description: >
Owns claims from registration to payment; the system of
record for claims.
diagram: 0200-application/application-structure.archify
crm:
type: backend
label: CRM
sublabel: App Component
tag: App Component
description: >
Customer contacts, addresses and the customer file.
policy-admin:
type: backend
label: Policy Admin
sublabel: App Component
tag: App Component
description: >
Policies, coverage and premiums; runs on the mainframe.
doc-mgmt:
type: backend
label: Document Mgmt
sublabel: App Component
tag: App Component
description: >
Scanned letters, photos and expert reports attached to claims.
payment:
type: backend
label: Payment
sublabel: App Component
tag: App Component
description: >
Sends approved claim payments to the bank in daily batches.
claim:
type: database
label: Claim
sublabel: Data Object
tag: Data Object
description: >
One reported damage, its valuation and its payment status.
policy:
type: database
label: Policy
sublabel: Data Object
tag: Data Object
description: >
Coverage, deductible and term of an insurance contract.
customer-file:
type: database
label: Customer File
sublabel: Data Object
tag: Data Object
description: >
Everything known about a customer; contacts, policies, claims.
@@ -0,0 +1,50 @@
schema_version: 1
elements:
customer:
type: external
label: Customer
sublabel: Business Actor
tag: Business Actor
description: >
A person or small business holding one or more policies
with Halbrook Mutual.
insurant:
type: external
label: Insurant
sublabel: Business Role
tag: Business Role
description: >
The role a customer plays while a claim is open; reports
damage and receives the payment.
claims-handler:
type: external
label: Claims handler
sublabel: Business Role
tag: Business Role
description: >
Employee of the claims department who accepts, valuates
and approves claims.
handle-claim:
type: backend
label: Handle Claim
sublabel: Business Process
tag: Business Process
description: >
Register, accept, valuate and pay a claim; the core process
of the claims department.
diagram: 0100-business/handle-claim.archify
claim-registration:
type: frontend
label: Claim Registration
sublabel: Business Service
tag: Business Service
description: >
Lets an insurant report damage by phone, mail or the
customer portal.
claim-payment:
type: frontend
label: Claim Payment
sublabel: Business Service
tag: Business Service
description: >
Pays the valuated amount to the insurant or a repairer.
@@ -0,0 +1,62 @@
schema_version: 1
elements:
app-servers:
type: backend
label: App Servers
sublabel: Node
tag: Node
description: >
Two virtual machine hosts running the claims app, CRM and the
portal back end.
diagram: 0300-technology/infrastructure.archify
db-cluster:
type: database
label: DB Cluster
sublabel: Node
brand: postgresql
tag: Node
description: >
Primary and standby database servers for claims and CRM data.
doc-store:
type: database
label: Document Store
sublabel: System Software
tag: System Software
description: >
Object storage for scans and photos; written by document
management only.
mainframe:
type: backend
label: Mainframe
sublabel: Node
tag: Node
description: >
Hosts policy administration and the nightly payment batch.
mq:
type: messagebus
label: Message Queue
sublabel: System Software
tag: System Software
description: >
Queue manager between the app servers and the mainframe.
db-service:
type: cloud
label: DB Service
sublabel: Tech Service
tag: Tech Service
description: >
Managed SQL access with backups, offered to all applications.
dc-lan:
type: messagebus
label: Data Centre LAN
sublabel: Network
tag: Network
description: >
Internal network linking app servers, databases and mainframe.
dmz:
type: security
label: DMZ Gateway
sublabel: Network
tag: Network
description: >
Reverse proxy and firewall between the internet and the LAN.
@@ -0,0 +1,34 @@
schema_version: 1
diagram_type: architecture
meta:
title: Business actors and services
output: business-services.html
components:
- ref: shipper
pos: [40, 60]
size: [130, 60]
- ref: recipient
pos: [40, 260]
size: [130, 60]
- ref: delivery-service
pos: [250, 60]
size: [130, 60]
- ref: tracking-service
pos: [250, 260]
size: [130, 60]
- ref: deliver-shipment
pos: [460, 160]
size: [130, 60]
- ref: driver
pos: [670, 60]
size: [130, 60]
- ref: customer-service
pos: [670, 260]
size: [130, 60]
connections:
- { from: delivery-service, to: shipper, label: serves, variant: emphasis }
- { from: tracking-service, to: recipient, label: serves }
- { from: deliver-shipment, to: delivery-service, label: realizes }
- { from: deliver-shipment, to: tracking-service, label: realizes }
- { from: driver, to: deliver-shipment, label: assigned to }
- { from: customer-service, to: tracking-service, label: assigned to }
@@ -0,0 +1,25 @@
schema_version: 2
diagram_type: workflow
meta:
title: Deliver Shipment
output: deliver-shipment.html
viewBox: [840, 534]
lanes:
- { id: customer-lane, label: Shipper and recipient }
- { id: driver-lane, label: Driver }
- { id: hub-lane, label: Hub }
mainPath: [book, pick-up, sort, line-haul, last-mile, pod-step]
nodes:
- { id: book, lane: customer-lane, col: 0, type: external, label: Book shipment, tag: Business Event }
- { id: pick-up, lane: driver-lane, col: 1, type: backend, label: Pick up, tag: Business Process }
- { id: sort, lane: hub-lane, col: 2, type: backend, label: Sort at hub, tag: Business Process }
- { id: line-haul, lane: hub-lane, col: 3, type: backend, label: Line haul, tag: Business Process }
- { id: last-mile, lane: driver-lane, col: 4, type: backend, label: Last mile, tag: Business Process }
- { id: pod-step, lane: customer-lane, col: 5, type: security, label: Delivered, sublabel: proof of delivery, tag: Business Process }
edges:
- { from: book, to: pick-up, label: triggers }
- { from: pick-up, to: sort, label: parcel }
- { from: sort, to: line-haul, label: trailer }
- { from: line-haul, to: last-mile, label: route }
- { from: last-mile, to: pod-step, label: hand over }
- { from: last-mile, to: sort, label: not home, role: return, variant: dashed }
@@ -0,0 +1,32 @@
schema_version: 1
diagram_type: architecture
meta:
title: Application cooperation
output: application-cooperation.html
decisions: [edi-gateway-in-front-of-tms]
components:
- ref: shipper
pos: [40, 260]
size: [130, 60]
- ref: edi-gateway
pos: [250, 260]
size: [130, 60]
decisions: [edi-gateway-in-front-of-tms]
- ref: tms
pos: [460, 260]
size: [130, 60]
- ref: driver-app
pos: [460, 60]
size: [130, 60]
- ref: telematics
pos: [460, 460]
size: [130, 60]
- ref: tracking-portal
pos: [670, 260]
size: [130, 60]
connections:
- { from: shipper, to: edi-gateway, label: bookings, variant: emphasis }
- { from: edi-gateway, to: tms, label: shipments, variant: emphasis }
- { from: driver-app, to: tms, label: scans, labelAt: [525, 134], decisions: [broker-for-tracking-events] }
- { from: telematics, to: tms, label: positions, decisions: [broker-for-tracking-events] }
- { from: tms, to: tracking-portal, label: status }
@@ -0,0 +1,34 @@
schema_version: 1
diagram_type: architecture
meta:
title: Application services and data
output: application-services.html
components:
- ref: tracking-portal
pos: [40, 160]
size: [130, 60]
- ref: tracking-svc
pos: [250, 160]
size: [130, 60]
- ref: tms
pos: [460, 160]
size: [130, 60]
- ref: driver-app
pos: [670, 160]
size: [130, 60]
- ref: shipment
pos: [250, 360]
size: [130, 60]
- ref: tracking-event
pos: [460, 360]
size: [130, 60]
- ref: pod
pos: [670, 360]
size: [130, 60]
connections:
- { from: tracking-svc, to: tracking-portal, label: serves, variant: emphasis }
- { from: tms, to: tracking-svc, label: realizes }
- { from: driver-app, to: tms, label: uses }
- { from: tms, to: shipment, label: accesses }
- { from: tms, to: tracking-event, label: accesses, labelAt: [525, 234] }
- { from: driver-app, to: pod, label: accesses, labelAt: [735, 234] }
@@ -0,0 +1,44 @@
schema_version: 1
diagram_type: architecture
meta:
title: Technology infrastructure
output: infrastructure.html
decisions: [broker-for-tracking-events]
components:
- ref: handhelds
pos: [40, 160]
size: [130, 60]
- ref: telematics-units
pos: [40, 360]
size: [130, 60]
- ref: mobile-network
pos: [250, 260]
size: [130, 60]
- ref: cloud-cluster
pos: [460, 160]
size: [130, 60]
- ref: message-broker
pos: [460, 360]
size: [130, 60]
decisions: [broker-for-tracking-events]
- ref: db-server
pos: [670, 160]
size: [130, 60]
- ref: event-streaming
pos: [670, 360]
size: [130, 60]
boundaries:
- kind: region
label: Field devices
wraps: [handhelds, telematics-units]
- kind: security-group
label: Cloud platform
wraps: [cloud-cluster, message-broker, db-server, event-streaming]
connections:
- { from: handhelds, to: mobile-network, label: LTE }
- { from: telematics-units, to: mobile-network, label: LTE }
- { from: mobile-network, to: cloud-cluster, label: HTTPS, variant: security }
- { from: mobile-network, to: message-broker, label: MQTT, variant: security }
- { from: cloud-cluster, to: message-broker, label: hosts, labelAt: [525, 234] }
- { from: cloud-cluster, to: db-server, label: SQL }
- { from: message-broker, to: event-streaming, label: realizes }
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: broker-for-tracking-events
title: A message broker for tracking events
status: accepted
date: 2026-09-08
elements: [message-broker, telematics, driver-app, tms, tracking-event]
diagrams: [0300-technology/infrastructure.archify, 0200-application/application-cooperation.archify]
context: >
Handhelds and telematics units posted every scan and position
straight to the TMS. At evening peaks the TMS fell behind and
dropped events when a device retried over a weak signal.
decision: >
Devices publish tracking events to a Kafka message broker on
the cloud cluster. The TMS and telematics read the topics at
their own pace; events are kept for seven days for replay.
consequences: >
Spikes no longer reach the TMS. Status on the tracking portal
can lag by seconds. Consumers must handle duplicate events,
and the broker is one more platform service to run.
@@ -0,0 +1,20 @@
schema_version: 1
decision:
id: edi-gateway-in-front-of-tms
title: An EDI gateway in front of the TMS
status: accepted
date: 2026-09-22
elements: [edi-gateway, tms, shipper, shipment]
diagrams: [0200-application/application-cooperation.archify]
context: >
Large shippers send bookings as EDI files in three dialects.
Each dialect was parsed inside the TMS, so a new shipper meant
a TMS release.
decision: >
A separate EDI gateway receives, validates and translates
every booking into one shipment message for the TMS. It also
sends status messages back in the shipper's dialect.
consequences: >
New shippers are onboarded in the gateway alone. The TMS has
one booking interface. A rejected booking now fails at the
gateway, which customer service must be able to see.
@@ -0,0 +1,17 @@
schema_version: 1
decision:
id: photo-proof-of-delivery
title: Photo as proof of delivery
status: proposed
date: 2026-10-27
elements: [pod, driver-app, handhelds]
diagrams: [0200-application/application-services.archify, 0100-business/deliver-shipment.archify]
context: >
Most parcels are left at the door when nobody signs. Disputes
about such parcels take customer service days to settle.
decision: >
The driver app takes a photo of every parcel left without a
signature and stores it with the proof of delivery.
consequences: >
Disputes are settled from the photo. Photos are personal data
and are deleted after 90 days. Uploads add mobile data cost.
@@ -0,0 +1,59 @@
schema_version: 1
model:
id: logistics-tracking
name: Logistics Tracking
description: >
An ArchiMate example: a parcel carrier lets shippers and recipients
track shipments. Business, application and technology layers are
mapped to folders; each element's ArchiMate type is shown as its tag.
diagram_type: architecture
meta:
title: Logistics Tracking - layered view
output: main.html
components:
- ref: recipient
pos: [40, 70]
size: [170, 60]
- ref: tracking-service
pos: [270, 70]
size: [170, 60]
- ref: deliver-shipment
pos: [500, 70]
size: [170, 60]
- ref: tracking-portal
pos: [40, 270]
size: [170, 60]
- ref: tms
pos: [270, 270]
size: [170, 60]
- ref: tracking-svc
pos: [500, 270]
size: [170, 60]
- ref: mobile-network
pos: [40, 470]
size: [170, 60]
- ref: cloud-cluster
pos: [270, 470]
size: [170, 60]
- ref: event-streaming
pos: [500, 470]
size: [170, 60]
boundaries:
- kind: region
label: Business layer
wraps: [recipient, tracking-service, deliver-shipment]
- kind: region
label: Application layer
wraps: [tracking-portal, tms, tracking-svc]
- kind: region
label: Technology layer
wraps: [mobile-network, cloud-cluster, event-streaming]
connections:
- { from: tracking-service, to: recipient, label: serves, variant: emphasis }
- { from: deliver-shipment, to: tracking-service, label: realizes }
- { from: tracking-portal, to: recipient, label: serves }
- { from: tracking-svc, to: deliver-shipment, label: serves }
- { from: tms, to: tracking-svc, label: realizes }
- { from: mobile-network, to: tracking-portal, label: serves }
- { from: cloud-cluster, to: tms, label: serves }
- { from: event-streaming, to: tracking-svc, label: serves }
@@ -0,0 +1,67 @@
schema_version: 1
elements:
tms:
type: backend
label: TMS
sublabel: App Component
tag: App Component
description: >
Owns shipments, routes and hub plans; the source of truth for status.
diagram: 0200-application/application-cooperation.archify
tracking-portal:
type: frontend
label: Tracking portal
sublabel: App Component
tag: App Component
description: >
Public site where shippers and recipients follow a shipment.
edi-gateway:
type: security
label: EDI gateway
sublabel: App Component
tag: App Component
description: >
Receives booking messages from large shippers and returns status.
driver-app:
type: frontend
label: Driver app
sublabel: App Component
tag: App Component
description: >
Scans parcels at every hand-over and captures proof of delivery.
telematics:
type: backend
label: Telematics
sublabel: App Component
tag: App Component
description: >
Collects vehicle positions and turns them into arrival estimates.
tracking-svc:
type: backend
label: Tracking service
sublabel: App Service
tag: App Service
description: >
Status and arrival estimate of one shipment, offered by the TMS.
diagram: 0200-application/application-services.archify
shipment:
type: database
label: Shipment
sublabel: Data Object
tag: Data Object
description: >
One booked parcel with sender, recipient, route and status.
tracking-event:
type: database
label: Tracking event
sublabel: Data Object
tag: Data Object
description: >
A scan or position report; the history of a shipment.
pod:
type: database
label: Proof of delivery
sublabel: Data Object
tag: Data Object
description: >
Signature or photo taken at the door; closes the shipment.
@@ -0,0 +1,54 @@
schema_version: 1
elements:
shipper:
type: external
label: Shipper
sublabel: Business Actor
tag: Business Actor
description: >
Books shipments, hands parcels over at pick-up and follows
them until delivery.
recipient:
type: external
label: Recipient
sublabel: Business Actor
tag: Business Actor
description: >
Receives the parcel; tracks it and may reschedule delivery.
driver:
type: external
label: Driver
sublabel: Business Role
tag: Business Role
description: >
Collects parcels, drives the routes and captures proof of delivery.
customer-service:
type: external
label: Customer service
sublabel: Business Role
tag: Business Role
description: >
Answers shipper and recipient questions about late or lost parcels.
deliver-shipment:
type: backend
label: Deliver Shipment
sublabel: Business Process
tag: Business Process
description: >
Pick up, sort at hub, line haul, last mile and proof of delivery.
diagram: 0100-business/deliver-shipment.archify
delivery-service:
type: backend
label: Parcel delivery
sublabel: Business Service
tag: Business Service
description: >
What the carrier sells; a parcel collected and delivered on time.
diagram: 0100-business/business-services.archify
tracking-service:
type: backend
label: Shipment tracking
sublabel: Business Service
tag: Business Service
description: >
Where a parcel is and when it arrives, for shippers and recipients.
@@ -0,0 +1,55 @@
schema_version: 1
elements:
cloud-cluster:
type: cloud
label: Cloud cluster
sublabel: Node
brand: kubernetes
tag: Node
description: >
Container cluster that runs the TMS, portal, EDI gateway and telematics.
diagram: 0300-technology/infrastructure.archify
message-broker:
type: messagebus
label: Message broker
sublabel: System Software
brand: apache-kafka
tag: System Software
description: >
Carries tracking events from devices and vehicles to the applications.
db-server:
type: database
label: Database server
sublabel: System Software
brand: postgresql
tag: System Software
description: >
Stores shipments, tracking events and proofs of delivery.
event-streaming:
type: backend
label: Event streaming
sublabel: Tech Service
tag: Tech Service
description: >
Durable, ordered topics that applications publish to and read from.
handhelds:
type: external
label: Handhelds
sublabel: Device
tag: Device
description: >
Scanners carried by drivers and hub staff; run the driver app.
telematics-units:
type: external
label: Telematics units
sublabel: Device
tag: Device
description: >
GPS boxes in vans and trucks; report position every minute.
mobile-network:
type: security
label: Mobile network
sublabel: Network
tag: Network
description: >
Carrier-grade mobile network that links devices to the cluster.
@@ -16,6 +16,12 @@ export const EXAMPLES = {
'c4-standard-web-app': 'C4 - Standard Web App: single-page app, API, database, cache, file storage, identity provider',
'c4-e-shop': 'C4 - E-Shop: web shop, API, worker, queue, payment and email providers',
'c4-event-based-integration': 'C4 - Event-Based Integration: event bus, router, dead-letter queue, event store, consumers',
'arc42-cinema-tickets': 'arc42 - Cinema Tickets: context, building blocks, runtime and deployment; seat booking with payment',
'arc42-iot-telemetry': 'arc42 - IoT Telemetry: devices over MQTT, ingest, time-series store, alerting, dashboards',
'arc42-team-wiki': 'arc42 - Team Wiki: a small self-hosted wiki, the arc42 sections at their smallest',
'archimate-insurance-claims': 'ArchiMate - Insurance Claims: business, application and technology layers of claim handling',
'archimate-hr-onboarding': 'ArchiMate - HR Onboarding: onboarding a new employee across HR, identity and IT services',
'archimate-logistics-tracking': 'ArchiMate - Logistics Tracking: shipment tracking from pick-up to proof of delivery',
};
export const help = `model-scaffold — create main.archify (and with --c4 the C4 folders), or copy an example
+6 -4
View File
@@ -206,17 +206,19 @@ try {
assert(r.status === 0, `exit ${r.status}`);
});
// The shipped examples: the same three the extension offers under "Archify | New Model…".
// The shipped examples: the same nine the extension offers under "Archify | New Model…".
const examplesDir = path.join(skill, 'examples');
const exampleNames = fs.readdirSync(examplesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
check('examples/ holds exactly the three C4 examples', [], () => {
assert(exampleNames.join(',') === 'c4-e-shop,c4-event-based-integration,c4-standard-web-app', `unexpected examples: ${exampleNames.join(',')}`);
check('examples/ holds the nine examples: three C4, three arc42, three ArchiMate', [], () => {
const expected = 'arc42-cinema-tickets,arc42-iot-telemetry,arc42-team-wiki,archimate-hr-onboarding,archimate-insurance-claims,archimate-logistics-tracking,c4-e-shop,c4-event-based-integration,c4-standard-web-app';
assert(exampleNames.join(',') === expected, `unexpected examples: ${exampleNames.join(',')}`);
});
for (const name of exampleNames) {
check(`example ${name} validates with 0 problems`, ['--action', 'model-validate', '--dir', path.join(examplesDir, name)], (r) => {
assert(r.status === 0, `exit ${r.status}`);
assert(yamlValue(r.stdout, ' errors') === '0' && yamlValue(r.stdout, ' warnings') === '0', 'example has problems');
assert(yamlValue(r.stdout, ' diagrams') === '4', 'expected 4 diagrams');
const diagrams = Number(yamlValue(r.stdout, ' diagrams'));
assert(diagrams >= 4 && yamlValue(r.stdout, ' diagramsOk') === String(diagrams), `expected at least 4 diagrams, all ok (${diagrams})`);
});
}
const fromExample = path.join(tmp, 'from-example');