1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! `mako-engine` — event-sourced process runtime for German energy market
//! communication (MaKo).
//!
//! # Architecture
//!
//! ```text
//! Raw EDIFACT bytes (AS4 transport)
//! │
//! ▼
//! [edi-energy] parse · validate
//! │
//! ▼ Command (typed, validated)
//! EngineContext::spawn / ::resume → Process::execute / ::execute_with / ::execute_with_retry
//! │
//! ├─ load events → reconstruct state (Workflow::apply + upcast)
//! ├─ handle command (Workflow::handle — pure, deterministic)
//! └─ append EventEnvelope batch (optimistic concurrency)
//!
//! EventStore ──► ProjectionRunner ──► Read models
//! SnapshotStore ──► Process::state_with_snapshot (O(k) replay)
//! OutboxStore ──► delivery worker ──► AS4 endpoint
//! DeadlineStore ──► scheduler ──► TimeoutDeadline command
//! ProcessRegistry ──► inbound message routing ──► Process
//! ```
//!
//! # Quick start
//!
//! ```rust,ignore
//! use mako_engine::{
//! builder::EngineBuilder,
//! ids::TenantId,
//! version::WorkflowId,
//! event_store::InMemoryEventStore,
//! };
//!
//! let ctx = EngineBuilder::new()
//! .with_event_store(InMemoryEventStore::new())
//! .build();
//!
//! // Spawn a new process.
//! let process = ctx.spawn::<MyWorkflow>(TenantId::new(), WorkflowId::new("…", "FV2024-10-01"));
//! let envelopes = process.execute(my_command).await?;
//!
//! // Reconstruct typed state by replaying all events.
//! let state = process.state().await?;
//!
//! // Persist routing information and resume on the next message.
//! ctx.registry().register(tenant, &conv_id.to_string(), process.identity()).await?;
//! let identity = ctx.registry().lookup(tenant, &conv_id.to_string()).await?.unwrap();
//! let resumed = ctx.resume::<MyWorkflow>(identity);
//! ```
//!
//! # Crate modules
//!
//! | Module | Contents |
//! |--------|----------|
//! | [`ids`] | Typed identifier newtypes (`EventId`, `StreamId`, `ProcessId`, `ProcessIdentity`, `DeadlineId`, …) |
//! | [`types`] | Semantic domain identifiers (`MaLo`, `MeLo`, `MarktpartnerCode`, `MessageRef`, `DeviceId`, `BkvId`, `UenbId`, `BillingPeriod`) |
//! | [`version`] | `FormatVersion`, `WorkflowId`, and `WorkflowVersionPolicy` |
//! | [`envelope`] | `EventEnvelope` and `NewEvent` |
//! | [`error`] | `EngineError`, `WorkflowError` |
//! | [`event_store`] | `EventStore` trait (with `stream_version`) + `InMemoryEventStore` |
//! | [`workflow`] | `Workflow` trait, `EventPayload`, `CommandContext` |
//! | [`message_adapter`] | `MessageAdapter` trait, `AdapterRegistry`, `FnAdapter` — cross-FV command translation |
//! | [`process`] | `Process<W,S>` — ergonomic typed process handle |
//! | [`projection`] | `Projection` trait + `ProjectionRunner` (single-stream and multi-stream) + `GlobalProjectionCheckpoint` |
//! | [`snapshot`] | `Snapshot`, `SnapshotStore` + `InMemorySnapshotStore` / `NoopSnapshotStore` |
//! | [`outbox`] | `OutboxMessage`, `OutboxStore` + `InMemoryOutboxStore` / `NoopOutboxStore` |
//! | [`inbox`] | `InboxStore` trait + `InMemoryInboxStore` for AS4 retry deduplication |
//! | [`deadline`] | `Deadline`, `DeadlineStore` + `InMemoryDeadlineStore` / `NoopDeadlineStore` |
//! | [`registry`] | `ProcessRegistry` + `InMemoryProcessRegistry` / `NoopProcessRegistry` |
//! | [`pid_router`] | `PidRouter` — maps `Prüfidentifikator` values to workflow names |
//! | [`fristen`] | Regulatory deadline helpers: `add_hours` (GPKE 24h), `add_werktage` (WiM/GeLi/MABIS) |
//! | [`dead_letter`] | `DeadLetterSink` trait + `LogDeadLetterSink` / `NoopDeadLetterSink` |
//! | [`erp`] | `ErpAdapter`, `ErpCommandSource`, `ErpEvent` — ERP/backend integration contract (BO4E) |
//! | [`builder`] | `EngineModule` trait, `EngineBuilder`, `EngineContext` |
// BDEW domain terms (MaKo, GPKE, WiM, GeLi) and product names (PostgreSQL,
// SlateDB) are not code identifiers — suppress doc_markdown for the crate.