Skip to content

Commit 8f1a9df

Browse files
docs: render architecture and example flows as mermaid diagrams (#152)
Add an Architecture section to the root README describing the source -> state -> sink spine, with a mermaid overview plus a small diagram per core module. Convert the ASCII diagrams in the fooddelivery and sinkflow examples and the statemachine handler-step README to mermaid, and expand the fooddelivery narrative to spell out the parallel Fulfillment/Watchdog regions and the compensation saga. Documentation only; no code or API changes. Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
1 parent c5199e0 commit 8f1a9df

4 files changed

Lines changed: 94 additions & 21 deletions

File tree

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,57 @@ in the documentation site:
4040

4141
### **[stablekernel.github.io/crucible](https://stablekernel.github.io/crucible/)**
4242

43+
## Architecture
44+
45+
Three core modules form the **ingest → drive → emit** spine: `source` brings
46+
events in, `state` decides what happens, and `sink` fans the resulting effects
47+
out. Each is a thin seam you can adopt on its own, and none imports another.
48+
49+
```mermaid
50+
flowchart LR
51+
streams[(external streams)] -->|source| engine[state engine]
52+
engine -->|sink| destinations[(destinations)]
53+
```
54+
55+
### `state` — the statechart engine
56+
57+
A stdlib-only statechart engine with no injected IO. Machines are pure: a `Fire`
58+
folds an event into a new instance and emits effects as plain data, leaving
59+
persistence and dispatch to the host.
60+
61+
```mermaid
62+
stateDiagram-v2
63+
[*] --> Idle
64+
Idle --> Working: Start [guard]
65+
Working --> Working: Progress / emit effect
66+
Working --> Done: Finish
67+
Working --> Idle: Reset
68+
Done --> [*]
69+
```
70+
71+
### `source` — the ingress seam
72+
73+
Consumes external streams (Kafka, JetStream, Redis, CDC, and more) and drives a
74+
machine, with the ack tied to a durable transition so redelivery is safe.
75+
76+
```mermaid
77+
flowchart LR
78+
stream[(stream)] --> decode[decode / codec] --> route["route to (key, event)"] --> fire["Fire on instance"] --> commit[durable commit] --> ack[ack]
79+
```
80+
81+
### `sink` — the egress seam
82+
83+
Fans emitted effects out to many destinations through a `Manifold`,
84+
fire-and-forget; one outlet's failure never stops the rest.
85+
86+
```mermaid
87+
flowchart LR
88+
effect[emitted effect] --> manifold[Manifold]
89+
manifold --> a[destination A]
90+
manifold --> b[destination B]
91+
manifold --> c[destination C]
92+
```
93+
4394
## Modules
4495

4596
Each module is independently versioned (per-module SemVer) and carries its own

examples/fooddelivery/README.md

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,40 @@ into your own domain.
1414

1515
An `Order` moves through these stages:
1616

17+
```mermaid
18+
stateDiagram-v2
19+
[*] --> Placed
20+
Placed --> Authorizing: Submit
21+
Authorizing --> Rejected: decline
22+
Authorizing --> Active: success [admit guard]
23+
24+
state Active {
25+
[*] --> Cooking
26+
Cooking --> AwaitingCourier
27+
AwaitingCourier --> EnRoute
28+
--
29+
[*] --> OnTime
30+
OnTime --> Overdue: after(SLA)
31+
}
32+
33+
Active --> Settling: DroppedOff
34+
Active --> Refunding: Cancel
35+
Settling --> Delivered: always
36+
Refunding --> Canceled: refund result
37+
38+
Rejected --> [*]
39+
Delivered --> [*]
40+
Canceled --> [*]
1741
```
18-
Placed --Submit--> Authorizing
19-
Authorizing invokes the payment "authorize" service.
20-
on decline --> Rejected (terminal)
21-
on success [admit guard] --> Active
22-
Active (parallel superstate)
23-
├─ Fulfillment region: Cooking --> AwaitingCourier --> EnRoute
24-
│ Cooking supervises the kitchen actor (it plates the meal).
25-
│ EnRoute supervises the courier actor (it delivers).
26-
└─ Watchdog region: OnTime --after(SLA)--> Overdue (records a breach)
27-
Active --DroppedOff--> Settling (courier completion exits the parallel state)
28-
Active --Cancel-----> Refunding (the compensation saga)
29-
Settling --always--> Delivered (terminal) (captures the payment hold)
30-
Refunding invokes "refund"; on its result --> Canceled (terminal)
31-
```
42+
43+
`Authorizing` invokes the payment `authorize` service. `Active` is a parallel
44+
superstate with two concurrent regions: a **Fulfillment** region
45+
(`Cooking → AwaitingCourier → EnRoute`, where `Cooking` supervises the kitchen
46+
actor and `EnRoute` the courier actor) and a **Watchdog** region (`OnTime`, which
47+
trips to `Overdue` and records a breach `after(SLA)`). `DroppedOff` exits the
48+
parallel state into `Settling`, which captures the payment hold and always lands
49+
in `Delivered`. `Cancel` routes into `Refunding`, the compensation saga, which
50+
invokes `refund` and ends in `Canceled`.
3251

3352
Run it through the host `Rig` (the example's wiring of the Scheduler, ServiceRunner,
3453
and ActorSystem); see `rig.go`, or embed the pieces directly.

examples/sinkflow/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ one `log/slog` logger. It is the proof that `state`, `sink`, and `telemetry`
77
compose through the [`sink/bridge`](../../sink/bridge) seam without any core
88
importing another.
99

10-
```
11-
order machine ──(bridge.Middleware)──▶ Manifold ──┬─▶ analytics (delivered)
12-
Fire(Prepare) ├─▶ audit (delivered)
13-
Fire(Dispatch) ── warehouse rejects ──▶ └─▶ warehouse (fails on Dispatch)
14-
Fire(Deliver)
10+
```mermaid
11+
flowchart LR
12+
M["order machine<br/>Fire(Prepare / Dispatch / Deliver)"] -->|bridge.Middleware| MAN[Manifold]
13+
MAN --> A["analytics<br/>(delivered)"]
14+
MAN --> AU["audit<br/>(delivered)"]
15+
MAN --> W["warehouse<br/>(rejects Dispatch)"]
1516
```
1617

1718
What the test asserts (`sinkflow_test.go`), all hermetic and deterministic:

source/statemachine/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ sub.Receive(ctx, h) // each message: route → load → Fire → emit → persis
2323

2424
The handler runs one declared step:
2525

26-
decode → route to (key, event) → load instance → Fire(event) →
27-
emit effects → persist new state → ack
26+
```mermaid
27+
flowchart LR
28+
decode --> route["route to (key, event)"] --> load["load instance"] --> fire["Fire(event)"] --> emit["emit effects"] --> persist["persist new state"] --> ack
29+
```
2830

2931
The ack comes only after a successful durable `Store.Save`
3032
(ack-after-durable-commit).

0 commit comments

Comments
 (0)