docs/sagas.md
A Saga allows you to encapsulate a sequence of steps within a saga transaction and specify compensation steps for each.
In the sample, Task2 will throw an exception, then UndoTask2 and UndoTask1 will be triggered.
builder
.StartWith(context => Console.WriteLine("Begin"))
.Saga(saga => saga
.StartWith<Task1>()
.CompensateWith<UndoTask1>()
.Then<Task2>()
.CompensateWith<UndoTask2>()
.Then<Task3>()
.CompensateWith<UndoTask3>()
)
.CompensateWith<CleanUp>()
.Then(context => Console.WriteLine("End"));
This particular example will retry the saga every 5 seconds, but you could also simply fail completely, and process a master compensation task for the whole saga.
builder
.StartWith(context => Console.WriteLine("Begin"))
.Saga(saga => saga
.StartWith<Task1>()
.CompensateWith<UndoTask1>()
.Then<Task2>()
.CompensateWith<UndoTask2>()
.Then<Task3>()
.CompensateWith<UndoTask3>()
)
.OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5))
.Then(context => Console.WriteLine("End"));
You could also only specify a master compensation step, as follows
builder
.StartWith(context => Console.WriteLine("Begin"))
.Saga(saga => saga
.StartWith<Task1>()
.Then<Task2>()
.Then<Task3>()
)
.CompensateWith<UndoEverything>()
.Then(context => Console.WriteLine("End"));
Parameters can be passed to a compensation step as follows
builder
.StartWith<SayHello>()
.CompensateWith<PrintMessage>(compensate =>
{
compensate.Input(step => step.Message, data => "undoing...");
})
A saga transaction can be expressed in JSON or YAML, by using the WorkflowCore.Primitives.Sequence step and setting the Saga parameter to true.
The compensation steps can be defined by specifying the CompensateWith parameter.
{
"Id": "Saga-Sample",
"Version": 1,
"DataType": "MyApp.MyDataClass, MyApp",
"Steps": [
{
"Id": "Hello",
"StepType": "MyApp.HelloWorld, MyApp",
"NextStepId": "MySaga"
},
{
"Id": "MySaga",
"StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore",
"NextStepId": "Bye",
"Saga": true,
"Do": [
[
{
"Id": "do1",
"StepType": "MyApp.Task1, MyApp",
"NextStepId": "do2",
"CompensateWith": [
{
"Id": "undo1",
"StepType": "MyApp.UndoTask1, MyApp"
}
]
},
{
"Id": "do2",
"StepType": "MyApp.Task2, MyApp",
"CompensateWith": [
{
"Id": "undo2-1",
"NextStepId": "undo2-2",
"StepType": "MyApp.UndoTask2, MyApp"
},
{
"Id": "undo2-2",
"StepType": "MyApp.DoSomethingElse, MyApp"
}
]
}
]
]
},
{
"Id": "Bye",
"StepType": "MyApp.GoodbyeWorld, MyApp"
}
]
}
Id: Saga-Sample
Version: 1
DataType: MyApp.MyDataClass, MyApp
Steps:
- Id: Hello
StepType: MyApp.HelloWorld, MyApp
NextStepId: MySaga
- Id: MySaga
StepType: WorkflowCore.Primitives.Sequence, WorkflowCore
NextStepId: Bye
Saga: true
Do:
- - Id: do1
StepType: MyApp.Task1, MyApp
NextStepId: do2
CompensateWith:
- Id: undo1
StepType: MyApp.UndoTask1, MyApp
- Id: do2
StepType: MyApp.Task2, MyApp
CompensateWith:
- Id: undo2-1
NextStepId: undo2-2
StepType: MyApp.UndoTask2, MyApp
- Id: undo2-2
StepType: MyApp.DoSomethingElse, MyApp
- Id: Bye
StepType: MyApp.GoodbyeWorld, MyApp