State and Workflow Patterns
Command Dispatch Case
A discriminated command type keeps each action branch visible.
command dispatch
Dispatching by command kind is a traceable way to apply several updates in order.
Command Dispatch Case
commands.ts
Replay: real traced execution (multi-file project)
type Command =
| { kind: "add"; amount: number }
| { kind: "subtract"; amount: number }
| { kind: "reset" };
function applyCommand(total: number, command: Command): number {
if (command.kind === "add") {
return total + command.amount;
}
if (command.kind === "subtract") {
return total - command.amount;
}
return 0;
}
const useReset: boolean = false;
const commands: Command[] = [
{ kind: "add", amount: 5 },
useReset ? { kind: "reset" } : { kind: "subtract", amount: 2 },
{ kind: "add", amount: 3 }
];
let total: number = 0;
for (const command of commands) {
total = applyCommand(total, command);
}
console.log(`total=${total}`);
type Command =
| { kind: "add"; amount: number }
| { kind: "subtract"; amount: number }
| { kind: "reset" };
function applyCommand(total: number, command: Command): number {
if (command.kind === "add") {
return total + command.amount;
}
if (command.kind === "subtract") {
return total - command.amount;
}
return 0;
}
const useReset: boolean = true;
const commands: Command[] = [
{ kind: "add", amount: 5 },
useReset ? { kind: "reset" } : { kind: "subtract", amount: 2 },
{ kind: "add", amount: 3 }
];
let total: number = 0;
for (const command of commands) {
total = applyCommand(total, command);
}
console.log(`total=${total}`);
useReset ← false, commands ← [object Object],[object Object],[object Object]
15 return 0;16}1718const useReset→ false: boolean = false; //@useReset=true19const commands→ [object Object],[object Object],[object Object]: Command[] = [20 { kind: "add", amount: 5 },21 useReset ? { kind: "reset" } : { kind: "subtract", amount: 2 },22 { kind: "add", amount: 3 }23];2425let total→ 0: number = 0;26for (const command of commands) {27 total = applyCommand(total0, command[object Object]);28}function applyCommand(total: number, command: Command): number
pass 1 of 33 | { kind: "subtract"; amount: number }4 | { kind: "reset" };56function applyCommand(total0: number, command[object Object]: Command): number {7 if (command.kind === "add") {All 3 passes — pass 1 is the card above pass totalcommand.kindcommand.amount1 0 add 5 2 5 subtract 2 3 3 add 3 if (command.kind === "add")
pass 1 of 26function applyCommand(total: number, command: Command): number {7 if (command.kindadd === "add") {8 return total + command.amount5;9 }total ← 5
25let total: number = 0;26for (const command of commands) {27 total = applyCommand(total5, command[object Object]);28}if (command.kind === "subtract")
8 return total + command.amount;9}1011if (command.kindsubtract === "subtract") {12 return total - command.amount2;13}total ← 3
25let total: number = 0;26for (const command of commands) {27 total = applyCommand(total3, command[object Object]);28}if (command.kind === "add")
pass 2 of 26function applyCommand(total: number, command: Command): number {7 if (command.kindadd === "add") {8 return total + command.amount3;9 }total ← 6
25let total: number = 0;26for (const command of commands) {27 total = applyCommand(total3, command[object Object]);28}2930console.log(`total=${total6}`);outputtotal=6
useReset ← true, commands ← [object Object],[object Object],[object Object]
15 return 0;16}1718const useReset→ true: boolean = true;19const commands→ [object Object],[object Object],[object Object]: Command[] = [20 { kind: "add", amount: 5 },21 useReset ? { kind: "reset" } : { kind: "subtract", amount: 2 },22 { kind: "add", amount: 3 }23];2425let total→ 0: number = 0;26for (const command of commands) {27 total = applyCommand(total0, command[object Object]);28}function applyCommand(total: number, command: Command): number
pass 1 of 33 | { kind: "subtract"; amount: number }4 | { kind: "reset" };56function applyCommand(total0: number, command[object Object]: Command): number {7 if (command.kind === "add") {All 3 passes — pass 1 is the card above pass totalcommand.kindcommand.amount1 0 add 5 2 5 — — 3 0 add 3 if (command.kind === "add")
pass 1 of 26function applyCommand(total: number, command: Command): number {7 if (command.kindadd === "add") {8 return total + command.amount5;9 }total ← 5
25let total: number = 0;26for (const command of commands) {27 total = applyCommand(total5, command[object Object]);28}total ← 0
25let total: number = 0;26for (const command of commands) {27 total = applyCommand(total0, command[object Object]);28}if (command.kind === "add")
pass 2 of 26function applyCommand(total: number, command: Command): number {7 if (command.kindadd === "add") {8 return total + command.amount3;9 }total ← 3
25let total: number = 0;26for (const command of commands) {27 total = applyCommand(total0, command[object Object]);28}2930console.log(`total=${total3}`);outputtotal=3