3.4 KiB
3.4 KiB
| type | status | owner | date |
|---|---|---|---|
| Reference | ACTIVE | Lead Architect | 2026-04-21 |
Wizard‑DSL & Orchestrator – Referenz
Ziel
Deklarative Beschreibung von Wizard‑Flows als Graph (Steps, Guards, Transitions) mit klaren Side‑Effects und Offline‑Draft‑Unterstützung.
Kern‑Interfaces (Skizze)
interface StepId
data class WizardContext(
val origin: AppScreen,
val role: String?,
val isOnline: Boolean,
val stats: MasterdataStats?
)
data class WizardState<S: StepId, A>(
val current: S,
val history: List<S> = emptyList(),
val acc: A,
val errors: List<String> = emptyList()
)
typealias Guard<S, A> = (WizardContext, A) -> Boolean
data class Transition<S: StepId>(val target: S, val guard: Guard<S, *>? = null, val id: String)
interface StepEffects<S: StepId, A> {
suspend fun onEnter(ctx: WizardContext, state: WizardState<S, A>) {}
suspend fun onLeave(ctx: WizardContext, state: WizardState<S, A>) {}
suspend fun onComplete(ctx: WizardContext, state: WizardState<S, A>) {}
}
DSL (Skizze)
class FlowBuilder<S: StepId, A> {
fun step(id: S, block: StepBuilder<S, A>.() -> Unit) { /* … */ }
}
class StepBuilder<S: StepId, A> {
fun onEnter(block: suspend (WizardContext, WizardState<S, A>) -> Unit) { /* … */ }
fun whenGuard(id: String, g: Guard<S, A>, go: S) { /* … */ }
fun otherwise(go: S) { /* … */ }
}
fun <S: StepId, A> flow(start: S, build: FlowBuilder<S, A>.() -> Unit): WizardRuntime<S, A> { /* … */ }
Beispiel – Event‑Flow (Auszug)
sealed interface EventStep: StepId {
data object ZnsCheck: EventStep
data object VeranstalterSelection: EventStep
data object AnsprechpersonMapping: EventStep
data object MetaData: EventStep
data object TurnierAnlage: EventStep
data object Summary: EventStep
}
data class EventAcc(
val veranstalterId: Uuid? = null,
val veranstalterNr: String = "",
val veranstalterName: String = "",
val ansprechpersonSatznr: String = "",
val name: String = "",
val ort: String = "",
val start: LocalDate? = null,
val end: LocalDate? = null,
val turniere: List<TurnierEntry> = emptyList()
)
object EventGuards {
val hasZns: Guard<EventStep, EventAcc> = { ctx, _ -> (ctx.stats?.vereinCount ?: 0) > 0 }
val needsContactPerson: Guard<EventStep, EventAcc> = { _, acc -> acc.veranstalterId == null || acc.veranstalterNr.startsWith("ORG-") }
}
val EventFlow = flow<EventStep, EventAcc>(start = EventStep.ZnsCheck) {
step(EventStep.ZnsCheck) {
onEnter { ctx, _ -> /* prefetch stats */ }
whenGuard("hasZns", EventGuards.hasZns, go = EventStep.VeranstalterSelection)
otherwise(go = EventStep.VeranstalterSelection)
}
step(EventStep.VeranstalterSelection) {
whenGuard("needsContact", EventGuards.needsContactPerson, go = EventStep.AnsprechpersonMapping)
otherwise(go = EventStep.MetaData)
}
}
DevTools
- Strukturierte Logs je Transition (from, to, guard-id, result, duration).
- Graph‑Export (DOT/PlantUML) aus der DSL für Doku & Reviews.
Tests (Empfehlungen)
- Unit: Guards (100% Branch‑Abdeckung), Runtime‑History.
- Property: Resume‑Determinismus (Draft → korrekter Step).
- Snapshot: Compose‑Panels mit Beispielkontexten.
Verweise
- ADR‑0025 Orchestrator · ADR‑0026 Validation‑Policy · ADR‑0027 Draft‑Domain & Delta‑Sync
- Roadmap:
docs/01_Architecture/MASTER_ROADMAP.md#3-initiative-wizard-orchestrator--offline-drafts-q2q3-2026