Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Only consider your changes complete when `make check` passes with all green chec

StateFlow uses a **two-step execution model** that separates setup from execution:

1. **Setup Phase**: `StateFlow::transition(State, array $delta)` returns a `StateWorker`
1. **Setup Phase**: `StateFlow::transition(State, Delta $delta)` returns a `StateWorker`
2. **Execution Phase**: `StateWorker::execute()` runs the transition

This separation enables:
Expand Down Expand Up @@ -131,15 +131,15 @@ interface State {
}
```

Users define their own merge strategy in `with()`. The `$changes` array is the delta passed to `transition()`.
Users define their own merge strategy in `with()`. The `$changes` array is converted from the Delta object passed to `transition()`.

### Configuration Provider

Lazy-loaded configuration based on current state and desired delta:

```php
$configProvider = function(State $currentState, array $delta): Configuration {
return match ($delta['status'] ?? null) {
$configProvider = function(State $currentState, Delta $delta): Configuration {
return match ($delta->get('status')) {
'processing' => new Configuration(
transitionGates: [new HasInventoryGate()],
actions: [new ChargePaymentAction(), new ReserveInventoryAction()],
Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ $stateFlow = new StateFlow(

// Execute transition with automatic locking
$order = new Order('pending');
$worker = $stateFlow->transition($order, ['status' => 'processing']);
$worker = $stateFlow->transition($order, new ArrayDelta(['status' => 'processing']));
$context = $worker->execute();


Expand All @@ -98,12 +98,12 @@ Specify only what changes:

```php
// Just this
$worker = $stateFlow->transition($state, ['status' => 'published']);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$context = $worker->execute();


// Not this
$worker = $stateFlow->transition($state, ['status' => 'published', 'author' => 'same', 'created' => 'same', ...]);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published', 'author' => 'same', 'created' => 'same', ...]));
$context = $worker->execute();
```

Expand All @@ -112,7 +112,7 @@ $context = $worker->execute();
The `StateWorker` gives you full control over the workflow execution:

```php
$worker = $stateFlow->transition($state, ['status' => 'published']);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));

// 1. Run gates first
$gateResult = $worker->runGates();
Expand Down Expand Up @@ -149,7 +149,7 @@ $stateFlow = new StateFlow(
);

// This transition will be automatically locked
$worker = $stateFlow->transition($state, ['status' => 'published']);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$context = $worker->execute();
```

Expand Down Expand Up @@ -249,8 +249,8 @@ class OrderState implements State {
}

// 2. Configure workflow based on transition type
$configProvider = function(State $state, array $delta): Configuration {
return match ($delta['status'] ?? null) {
$configProvider = function(State $state, Delta $delta): Configuration {
return match ($delta->get('status')) {
'processing' => new Configuration(
transitionGates: [new HasInventoryGate($inventory)],
actions: [
Expand All @@ -273,7 +273,7 @@ $stateFlow = new StateFlow(
eventDispatcher: new MetricsDispatcher(),
lockProvider: new RedisLockProvider($redis),
lockKeyProvider: new class implements LockKeyProvider {
public function getLockKey(State $state, array $delta): string {
public function getLockKey(State $state, Delta $delta): string {
return "order:" . $state->toArray()['id'];
}
},
Expand All @@ -282,7 +282,7 @@ $stateFlow = new StateFlow(
// 4. Execute with race protection
try {
$order = new OrderState('ORD-123', 'pending', 99.99);
$worker = $stateFlow->transition($order, ['status' => 'processing']);
$worker = $stateFlow->transition($order, new ArrayDelta(['status' => 'processing']));
$context = $worker->execute();

if ($context->isCompleted()) {
Expand Down
14 changes: 7 additions & 7 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Users specify only what should change, not the entire final state:

```php
// Good: Delta approach
$worker = $stateFlow->transition($orderState, ['status' => 'published']);
$worker = $stateFlow->transition($orderState, new ArrayDelta(['status' => 'published']));
$context = $worker->execute();

// Avoided: Full state (verbose and error-prone)
$stateFlow->transition($orderState, ['status' => 'published', 'author' => 'same', 'created' => 'same', ...]);
$stateFlow->transition($orderState, new ArrayDelta(['status' => 'published', 'author' => 'same', 'created' => 'same', ...]));
```

**Rationale:** With rich state objects, deltas are more ergonomic and show clear intent.
Expand All @@ -48,14 +48,14 @@ The `StateWorker` allows for fine-grained control over the execution flow.
**One-Shot Execution:**

```php
$worker = $stateFlow->transition($state, ['status' => 'published']);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$context = $worker->execute(); // Runs gates and actions
```

**Step-Through Execution:**

```php
$worker = $stateFlow->transition($state, ['status' => 'published']);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$gateResult = $worker->runGates();
if ($gateResult->shouldStopTransition()) {
// Handle failed transition
Expand All @@ -75,7 +75,7 @@ class GenerateThumbnailsAction implements Action {
}

// Serialize and wait
$worker = $stateFlow->transition($state, ['status' => 'published']);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$context = $worker->execute();
if ($context->isPaused()) {
saveToDatabase($context->serialize());
Expand Down Expand Up @@ -120,7 +120,7 @@ $stateFlow = new StateFlow(
);

// The worker will use the flows's lock provider
$worker = $stateFlow->transition($state, ['status' => 'published']);
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$context = $worker->execute();
```

Expand Down Expand Up @@ -210,7 +210,7 @@ Users implement their own merge logic in `with()`.
**Why:** Allows dynamic gate/action selection based on what's changing.

```php
$configProvider = function(State $currentState, array $desiredDelta): Configuration {
$configProvider = function(State $currentState, Delta $desiredDelta): Configuration {
// ... return Configuration based on state and delta
};
```
Expand Down
Loading