Claude-skill-registry acc-create-saga-pattern
Generates Saga pattern components for PHP 8.5. Creates Saga interfaces, steps, orchestrator, state management, and compensation logic with unit tests.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/acc-create-saga-pattern" ~/.claude/skills/majiayu000-claude-skill-registry-acc-create-saga-pattern && rm -rf "$T"
manifest:
skills/data/acc-create-saga-pattern/SKILL.mdsource content
Saga Pattern Generator
Creates Saga pattern infrastructure for distributed transaction coordination.
When to Use
- Distributed transactions across multiple services
- Long-running business processes
- Operations requiring compensation on failure
- Multi-step workflows with rollback capability
Component Characteristics
SagaState Enum
- Domain layer enum
- Defines saga lifecycle states: Pending, Running, Compensating, Completed, Failed, CompensationFailed
- Includes state transition validation
- Terminal states detection
SagaStep Interface
- Domain layer interface
- Execute and compensate methods
- Idempotency declaration
- Timeout configuration
SagaContext
- Carries saga execution data
- Serializable for persistence
- Supports data accumulation across steps
SagaOrchestrator
- Application layer coordinator
- Manages step execution
- Handles compensation flow (reverse order)
- Persists state after each step
SagaPersistence
- Stores saga state for recovery
- Supports finding incomplete sagas
- Dead letter handling
Generation Process
Step 1: Analyze Request
Determine:
- Context name (Order, Payment, Shipping)
- Required saga steps
- Compensation logic for each step
Step 2: Generate Core Components
Create in this order:
-
Domain Layer (
)src/Domain/Shared/Saga/
— State enum with transitionsSagaState.php
— Step result value objectStepResult.php
— Step contractSagaStepInterface.php
— Execution contextSagaContext.php
— Saga resultSagaResult.php- Exception classes
-
Application Layer (
)src/Application/Shared/Saga/
— Persistence portSagaPersistenceInterface.php
— Persisted recordSagaRecord.php
— Base step classAbstractSagaStep.php
— OrchestratorSagaOrchestrator.php
-
Infrastructure Layer
— Doctrine implementationDoctrineSagaPersistence.php- Database migration
-
Tests
SagaStateTest.phpSagaOrchestratorTest.php
Step 3: Generate Context-Specific Steps
For each saga step (e.g., Order saga):
src/Application/{Context}/Saga/Step/ ├── ReserveInventoryStep.php ├── ChargePaymentStep.php └── CreateShipmentStep.php src/Application/{Context}/Saga/ └── {Context}SagaFactory.php
File Placement
| Layer | Path |
|---|---|
| Domain Types | |
| Application Saga | |
| Context Steps | |
| Saga Factory | |
| Infrastructure | |
| Unit Tests | |
Key Principles
Compensation Rules
- Compensate in reverse order of execution
- Compensations must be idempotent
- Handle "already compensated" gracefully
- Log all compensation attempts
Idempotency
- Use idempotency keys for each step
- Check for existing results before executing
- Return existing result if found
State Transitions
Pending → Running → Completed ↓ Compensating → Failed ↓ CompensationFailed
Naming Conventions
| Component | Pattern | Example |
|---|---|---|
| State Enum | | |
| Step Interface | | |
| Abstract Step | | |
| Concrete Step | | |
| Orchestrator | | |
| Factory | | |
| Test | | |
Quick Template Reference
SagaStepInterface
interface SagaStepInterface { public function name(): string; public function execute(SagaContext $context): StepResult; public function compensate(SagaContext $context): StepResult; public function isIdempotent(): bool; public function timeout(): int; }
StepResult
final readonly class StepResult { public static function success(array $data = []): self; public static function failure(string $error): self; public function isSuccess(): bool; public function isFailure(): bool; }
Concrete Step Pattern
final readonly class {Action}Step extends AbstractSagaStep { public function name(): string { return '{action_name}'; } public function execute(SagaContext $context): StepResult { $idempotencyKey = $this->idempotencyKey($context); // Check for existing result (idempotency) // Execute action // Return StepResult::success([...data...]) or failure } public function compensate(SagaContext $context): StepResult { // Get data from context // Undo action // Handle "already undone" gracefully return StepResult::success(); } }
Usage Example
// Create saga $saga = $orderSagaFactory->create($command); // Execute $result = $saga->execute(); if ($result->isCompleted()) { // Success } elseif ($result->isFailed()) { // Failed but compensated $error = $result->error; } elseif ($result->isCompensationFailed()) { // Needs manual intervention $originalError = $result->error; $compensationError = $result->compensationError; }
DI Configuration
# Symfony services.yaml Domain\Shared\Saga\SagaStepInterface: tags: ['saga.step'] Application\Shared\Saga\SagaPersistenceInterface: alias: Infrastructure\Persistence\Doctrine\Repository\DoctrineSagaPersistence Application\Order\Saga\OrderSagaFactory: arguments: $reserveStep: '@Application\Order\Saga\Step\ReserveInventoryStep' $chargeStep: '@Application\Order\Saga\Step\ChargePaymentStep' $shipStep: '@Application\Order\Saga\Step\CreateShipmentStep'
Database Schema
CREATE TABLE sagas ( id VARCHAR(255) PRIMARY KEY, type VARCHAR(255) NOT NULL, state VARCHAR(50) NOT NULL, completed_steps JSONB NOT NULL DEFAULT '[]', context JSONB NOT NULL, error TEXT, created_at TIMESTAMP(6) NOT NULL, updated_at TIMESTAMP(6) NOT NULL, completed_at TIMESTAMP(6) ); CREATE INDEX idx_sagas_state ON sagas (state); CREATE INDEX idx_sagas_type_state ON sagas (type, state);
References
For complete PHP templates and test examples, see:
— All component templatesreferences/templates.md
— Order saga example and unit testsreferences/examples.md