Skip to content

Commit 05b9650

Browse files
committed
Improve governance DX and docs
1 parent 72b9217 commit 05b9650

15 files changed

Lines changed: 492 additions & 79 deletions

File tree

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Complete API documentation for **ModularityKit.Mutators**.
1515
- [Related Types](#related-types)
1616
- [DI Extension Methods](#di-extension-methods)
1717
- [AddMutators](#addmutators)
18+
- [Governance API](#governance-api)
1819
- [Execution Semantics](#execution-semantics)
1920
- [Best Practices](#best-practices)
2021
- [Complete Examples](#complete-examples)
@@ -246,6 +247,19 @@ services.AddMutators(MutationEngineOptions.Strict, addDefaultLoggingInterceptor:
246247

247248
---
248249

250+
## Governance API
251+
252+
Governance is documented separately so the core API reference can stay focused on direct mutation execution.
253+
254+
- [Governance package overview](../../src/Governance/README.md)
255+
- [Governance API usage](API.md)
256+
- [Redis provider API](Redis.md)
257+
258+
Governance-specific types such as `MutationRequestFactory`, `MutationRequestDecision`, and
259+
`IGovernanceExecutionManager` live in the governance package, not the core mutation runtime.
260+
261+
---
262+
249263
## Execution Semantics
250264

251265
1. Call `Validate(state)`
@@ -272,4 +286,4 @@ services.AddMutators(MutationEngineOptions.Strict, addDefaultLoggingInterceptor:
272286

273287
---
274288

275-
> **Built with ❤️ for .NET developers**
289+
> **Built with ❤️ for .NET developers**

Docs/API/API.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Governance API
2+
3+
This page shows the practical entry points used by the governance examples.
4+
5+
## What this API is for
6+
7+
Use the governance API when execution is not immediate and you need one of these steps first:
8+
9+
- request creation
10+
- approval workflow
11+
- stale-version resolution
12+
- governed execution
13+
- request and decision history queries
14+
15+
The core package owns direct mutation execution. Governance wraps that execution with a request
16+
model and stateful decision history.
17+
18+
## Request creation
19+
20+
Use `MutationRequestFactory` when you want to create a request from explicit strings and values:
21+
22+
```csharp
23+
var request = MutationRequestFactory.PendingApproval(
24+
stateId: "tenant-42:roles",
25+
stateType: "IamRoleState",
26+
mutationType: "GrantRoleMutation",
27+
intent: new MutationIntent
28+
{
29+
OperationName = "GrantRole",
30+
Category = "Security",
31+
Description = "Grant elevated role to tenant operator"
32+
},
33+
context: MutationContext.User("requester-1", "Requester One", "Incident escalation"),
34+
expectedStateVersion: "v10",
35+
approvalRequirements:
36+
[
37+
MutationApprovalRequirement.SingleActorStep("security-lead"),
38+
MutationApprovalRequirement.SingleActorStep("platform-owner")
39+
]);
40+
```
41+
42+
If you already have concrete CLR types, prefer the generic overloads such as:
43+
44+
- `MutationRequestFactory.Approved<TState, TMutation>()`
45+
- `MutationRequestFactory.PendingApproval<TState, TMutation>()`
46+
47+
They remove repeated `stateType` and `mutationType` strings at the call site.
48+
49+
### Factory choices
50+
51+
- `Pending(...)` for a request that should enter the lifecycle without approval requirements
52+
- `PendingApproval(...)` for a request that must collect one or more approval steps
53+
- `Approved(...)` for a request that is terminally approved at creation time
54+
55+
Use the generic overloads when the CLR types already exist. Use the string-based overloads when the
56+
request metadata comes from an external system or serialized payload.
57+
58+
## Decision history
59+
60+
Use `MutationRequestDecision` when you need to record a lifecycle, approval, or version-resolution decision and the category is already known:
61+
62+
```csharp
63+
var decision = MutationRequestDecision.Lifecycle(
64+
MutationRequestLifecycleDecisionType.Approved,
65+
MutationContext.System("governance-runtime", "Approved by governance"));
66+
```
67+
68+
The category-specific helpers are:
69+
70+
- `MutationRequestDecision.Lifecycle(...)`
71+
- `MutationRequestDecision.Approval(...)`
72+
- `MutationRequestDecision.VersionResolution(...)`
73+
74+
Use the low-level `MutationRequestDecision.Create(...)` only when the decision type is already dynamic and you do not know the category up front.
75+
76+
### Decision categories
77+
78+
- lifecycle decisions describe request state transitions such as submitted, pending, approved, rejected, executed, canceled, or expired
79+
- approval decisions describe workflow-level approval actions such as requested, approved, rejected, or quorum satisfied
80+
- version-resolution decisions describe how the runtime handled stale or matching versions before execution
81+
82+
The helper methods exist so call sites stay readable when the category is already known.
83+
84+
## Governed execution
85+
86+
Use `IGovernanceExecutionManager.ExecuteApproved(...)` when the runtime should resolve an approved request and execute the mutation:
87+
88+
```csharp
89+
var execution = await executionManager.ExecuteApproved(
90+
request.RequestId,
91+
mutation,
92+
currentState,
93+
governanceContext: MutationContext.Service("governance-runtime", "Execute approved governance request"),
94+
strategy: VersionedRequestResolutionStrategy.RejectStale);
95+
```
96+
97+
If your state implements `IVersionedState`, use the overload that accepts `currentState` directly and reads `state.Version` for both version selectors.
98+
99+
### Typical execution flow
100+
101+
1. create or load a request
102+
2. approve the request if needed
103+
3. resolve the request against the current state version
104+
4. execute the underlying mutation through the core engine
105+
5. record the terminal request decision
106+
107+
That flow is what the `GovernedExecution` example demonstrates end to end.
108+
109+
## Common operations
110+
111+
### Create a pending approval request
112+
113+
```csharp
114+
var request = MutationRequestFactory.PendingApproval(
115+
stateId: "tenant-42:roles",
116+
stateType: "IamRoleState",
117+
mutationType: "GrantRoleMutation",
118+
intent: new MutationIntent
119+
{
120+
OperationName = "GrantRole",
121+
Category = "Security",
122+
Description = "Grant elevated role to tenant operator"
123+
},
124+
context: MutationContext.User("requester-1", "Requester One", "Incident escalation"),
125+
expectedStateVersion: "v10",
126+
approvalRequirements:
127+
[
128+
MutationApprovalRequirement.SingleActorStep("security-lead"),
129+
MutationApprovalRequirement.SingleActorStep("platform-owner")
130+
]);
131+
```
132+
133+
### Record an approval decision
134+
135+
```csharp
136+
var decision = MutationRequestDecision.Approval(
137+
MutationRequestApprovalDecisionType.Approved,
138+
MutationContext.User("approver-1", "Approver One", "Approved after review"));
139+
```
140+
141+
### Execute an already approved request
142+
143+
```csharp
144+
var result = await executionManager.ExecuteApproved(
145+
request.RequestId,
146+
mutation,
147+
currentState,
148+
governanceContext: MutationContext.Service("governance-runtime", "Execute approved governance request"),
149+
strategy: VersionedRequestResolutionStrategy.RejectStale);
150+
```
151+
152+
## Where to look next
153+
154+
- `README.md` for the package overview
155+
- `Examples/Governance/*/README.md` for runnable scenarios
156+
- `src/Governance/Abstractions/Requests/Factory/MutationRequestFactory.cs` for request creation
157+
- `src/Governance/Abstractions/Requests/Decisions/MutationRequestDecision.cs` for decision helpers
158+
- `src/Governance/Runtime/Execution/Orchestration/GovernanceExecutionManager.cs` for governed execution

Docs/API/Redis.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Redis Governance Provider API
2+
3+
This page covers the Redis-backed provider package `ModularityKit.Mutator.Governance.Redis`.
4+
5+
It is the storage and query backend for governed requests. The package implements the public
6+
request store and query store contracts while keeping Redis-specific persistence details internal.
7+
8+
## What this API is for
9+
10+
Use the Redis provider when you want the governance model from `ModularityKit.Mutator.Governance`
11+
but need a persistent backing store instead of in-memory state.
12+
13+
Typical reasons:
14+
15+
- you want request persistence across process restarts
16+
- you want Redis-backed query projections for approval queues and decision history
17+
- you want the same governance API surface with a different storage backend
18+
- you want to share governed request state across multiple runtime instances
19+
20+
## Primary APIs
21+
22+
### Dependency injection
23+
24+
- `RedisGovernanceServiceCollectionExtensions.AddRedisGovernanceStore(...)`
25+
26+
Registers the Redis-backed governance store and the query store against an existing
27+
`IConnectionMultiplexer`.
28+
29+
```csharp
30+
using Microsoft.Extensions.DependencyInjection;
31+
using ModularityKit.Mutator.Governance.Redis;
32+
using StackExchange.Redis;
33+
34+
var services = new ServiceCollection();
35+
var multiplexer = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
36+
37+
services.AddRedisGovernanceStore(
38+
multiplexer,
39+
options => options.KeyPrefix = "modularitykit:governance");
40+
```
41+
42+
The extension method registers the provider-facing storage and query services against DI.
43+
44+
### Configuration details
45+
46+
`RedisMutationRequestStoreOptions.KeyPrefix` scopes all Redis keys created by the provider.
47+
48+
Use a dedicated prefix per environment or application if you need isolation between deployments.
49+
The examples use `modularitykit:governance` as a readable default.
50+
51+
### Configuration
52+
53+
- `RedisMutationRequestStoreOptions`
54+
55+
Current option:
56+
57+
- `KeyPrefix` - Redis key prefix used for all request data, indexes, and query projections
58+
59+
### Storage facade
60+
61+
- `RedisMutationRequestStore`
62+
63+
Implements:
64+
65+
- `IMutationRequestStore`
66+
- `IMutationRequestQueryStore`
67+
68+
Use it through DI rather than constructing it directly unless you are testing internals.
69+
70+
### What gets stored
71+
72+
The provider stores the data needed for:
73+
74+
- request documents
75+
- decision and approval projections
76+
- query indexes and queue membership
77+
- optimistic concurrency state for request writes
78+
79+
The provider does not change the governance model. It only persists the same model through Redis.
80+
81+
### Keyspace and key helpers
82+
83+
- `RedisMutationRequestKeyspace`
84+
- `RedisMutationRequestDocumentKeyFactory`
85+
86+
These types define the Redis naming and partitioning rules for request documents, indexes, and
87+
query views.
88+
89+
## What the provider does
90+
91+
The Redis provider handles:
92+
93+
- request persistence
94+
- query projection
95+
- optimistic concurrency on request writes
96+
- Redis index maintenance
97+
- candidate selection for query execution
98+
99+
It does not change the governance model itself. It only replaces the in-memory storage backend with
100+
Redis-specific persistence and read paths.
101+
102+
## Common usage
103+
104+
### Register the provider
105+
106+
```csharp
107+
services.AddRedisGovernanceStore(
108+
multiplexer,
109+
options => options.KeyPrefix = "modularitykit:governance");
110+
```
111+
112+
### Resolve the stores
113+
114+
```csharp
115+
var requestStore = provider.GetRequiredService<IMutationRequestStore>();
116+
var queryStore = provider.GetRequiredService<IMutationRequestQueryStore>();
117+
```
118+
119+
### Use the normal governance APIs
120+
121+
Once registered, the rest of the application uses the same request and query contracts as the
122+
in-memory provider.
123+
124+
## Usage pattern
125+
126+
1. Connect to Redis with `StackExchange.Redis`
127+
2. Register the provider with `AddRedisGovernanceStore(...)`
128+
3. Resolve `IMutationRequestStore` and `IMutationRequestQueryStore` from DI
129+
4. Use the normal governance request and query APIs
130+
131+
## Related docs
132+
133+
- [`src/Redis/README.md`](../../src/Redis/README.md)
134+
- [`Examples/Governance/RedisQueries/README.md`](../../Examples/Governance/RedisQueries/README.md)
135+
- [`API-Reference.md`](API-Reference.md)
136+
- [Governance API](API.md)

0 commit comments

Comments
 (0)