Skip to content

Commit 1d1ef5b

Browse files
committed
Add README for command runners
1 parent 0d673ce commit 1d1ef5b

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

  • FunctionalStateMachine.CommandRunner
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# FunctionalStateMachine.CommandRunner
2+
3+
`FunctionalStateMachine.CommandRunner` is an optional DI layer for executing commands produced by a state machine. It discovers and registers `ICommandRunner<TCommand>`/`IAsyncCommandRunner<TCommand>` implementations and provides dispatchers for running commands.
4+
5+
## Quick start
6+
7+
### 1) Define your command hierarchy
8+
9+
```csharp
10+
public abstract record UserCommand
11+
{
12+
public sealed record SendWelcomeEmail(Guid UserId) : UserCommand;
13+
}
14+
```
15+
16+
### 2) Implement runners
17+
18+
```csharp
19+
public sealed class SendWelcomeEmailRunner : ICommandRunner<UserCommand.SendWelcomeEmail>
20+
{
21+
public void Run(UserCommand.SendWelcomeEmail command)
22+
{
23+
// send email
24+
}
25+
}
26+
```
27+
28+
### 3) Register and dispatch
29+
30+
```csharp
31+
var services = new ServiceCollection()
32+
.AddCommandRunners<UserCommand>();
33+
34+
var dispatcher = services
35+
.BuildServiceProvider()
36+
.GetRequiredService<ICommandDispatcher<UserCommand>>();
37+
38+
dispatcher.Run(new UserCommand.SendWelcomeEmail(Guid.NewGuid()));
39+
```
40+
41+
## Async runners
42+
43+
If any runner implements `IAsyncCommandRunner<TCommand>`, resolve `IAsyncCommandDispatcher<TCommand>` and call `RunAsync`:
44+
45+
```csharp
46+
public sealed class ChargeCardRunner : IAsyncCommandRunner<BillingCommand.ChargeCard>
47+
{
48+
public Task RunAsync(BillingCommand.ChargeCard command)
49+
=> Task.CompletedTask;
50+
}
51+
52+
services.AddCommandRunners<BillingCommand>();
53+
var dispatcher = services.BuildServiceProvider()
54+
.GetRequiredService<IAsyncCommandDispatcher<BillingCommand>>();
55+
56+
await dispatcher.RunAsync(new BillingCommand.ChargeCard(42m));
57+
```
58+
59+
## Options
60+
61+
```csharp
62+
services.AddCommandRunners<UserCommand>(new CommandRunnerOptions
63+
{
64+
MissingBehavior = CommandRunnerMissingBehavior.NoOp,
65+
Lifetime = ServiceLifetime.Scoped,
66+
AutoRegisterRunners = false
67+
});
68+
```
69+
### Missing Behavior
70+
- `MissingBehavior`: Define how to deal with missing command runners
71+
- `Throw` - `[default]` throw when a command has no runner.
72+
- `NoOp` - executing a command that has no runner does nothing.
73+
74+
### Lifetime
75+
- `Lifetime`: Sets the lifetime that the command runners are registered.
76+
- `Transient` - `[default]`
77+
- `Singleton`
78+
- `Scoped`
79+
80+
### Suppress Autoregistration
81+
- `AutoRegisterRunners`: Suppress the auto-registration
82+
- `true` : `[default]`
83+
- `false` : set to `false` to register runners manually.
84+
85+
## Notes
86+
87+
- The dispatcher is source-generated; ensure `FunctionalStateMachine.CommandRunner.Generator` is referenced as an analyzer in projects that call `AddCommandRunners<T>()`.
88+
- See `docs/command-runners.md` for full guidance and examples.

0 commit comments

Comments
 (0)