Skip to content

Commit 5155b07

Browse files
committed
samples, readme
1 parent de5701c commit 5155b07

File tree

13 files changed

+261
-26
lines changed

13 files changed

+261
-26
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class MyWorkflow : IWorkflow
5353
}
5454
```
5555

56-
* Resilient service orchestration
56+
* Saga Transactions
5757

5858
```c#
5959
public class MyWorkflow : IWorkflow
@@ -70,6 +70,21 @@ public class MyWorkflow : IWorkflow
7070
}
7171
```
7272

73+
```c#
74+
builder
75+
.StartWith<LogStart>()
76+
.Saga(saga => saga
77+
.StartWith<Task1>()
78+
.CompensateWith<UndoTask1>()
79+
.Then<Task2>()
80+
.CompensateWith<UndoTask2>()
81+
.Then<Task3>()
82+
.CompensateWith<UndoTask3>()
83+
)
84+
.OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10))
85+
.Then<LogEnd>();
86+
```
87+
7388
## Persistence
7489

7590
Since workflows are typically long running processes, they will need to be persisted to storage between steps.
@@ -106,6 +121,8 @@ There are several persistence providers available as separate Nuget packages.
106121

107122
* [Parallel Tasks](src/samples/WorkflowCore.Sample13)
108123

124+
* [Saga Transactions (with compensation)](src/samples/WorkflowCore.Sample17)
125+
109126
* [Scheduled Background Tasks](src/samples/WorkflowCore.Sample16)
110127

111128
* [Recurring Background Tasks](src/samples/WorkflowCore.Sample14)

ReleaseNotes/1.6.0.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#### Specifying compensation steps for each component of a saga transaction
99

10+
In this sample, if `Task2` throws an exception, then `UndoTask2` and `UndoTask1` will be triggered.
11+
1012
```c#
1113
builder
1214
.StartWith<SayHello>()
@@ -24,6 +26,8 @@ builder
2426

2527
#### Retrying a failed transaction
2628

29+
This particular example will retry the entire saga every 5 seconds
30+
2731
```c#
2832
builder
2933
.StartWith<SayHello>()
@@ -42,6 +46,8 @@ builder
4246

4347
#### Compensating the entire transaction
4448

49+
You could also only specify a master compensation step, as follows
50+
4551
```c#
4652
builder
4753
.StartWith<SayHello>()
@@ -66,7 +72,7 @@ Parameters can be passed to a compensation step as follows
6672
```c#
6773
builder
6874
.StartWith<SayHello>()
69-
.CompensateWith<CustomMessage>(compensate =>
75+
.CompensateWith<PrintMessage>(compensate =>
7076
{
7177
compensate.Input(step => step.Message, data => "undoing...");
7278
})

src/samples/WorkflowCore.Sample17/CompensatingWorkflow.cs

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,17 @@ class CompensatingWorkflow : IWorkflow
1515
public void Build(IWorkflowBuilder<object> builder)
1616
{
1717
builder
18-
.StartWith(context => Console.WriteLine("Hello"))
19-
.CompensateWith(context => Console.WriteLine("undo hello"))
18+
.StartWith(context => Console.WriteLine("Begin"))
2019
.Saga(saga => saga
21-
.StartWith(context => Console.WriteLine("1"))
22-
.CompensateWith(context => Console.WriteLine("undo 1"))
23-
.Then(context =>
24-
{
25-
Console.WriteLine("2");
26-
throw new Exception("boo");
27-
Console.WriteLine("2.5");
28-
})
29-
.CompensateWith<CustomMessage>(x => x.Input(step => step.Message, data => "undo 2"))
30-
.Then(context => Console.WriteLine("3"))
31-
)
32-
//.CompensateWithSequence(comp => comp
33-
// .StartWith(ctx => Console.WriteLine("fail saga1"))
34-
// .Then(ctx => Console.WriteLine("fail saga2"))
35-
// )
36-
.OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5))
37-
.Then(context => Console.WriteLine("end"));
20+
.StartWith<Task1>()
21+
.CompensateWith<UndoTask1>()
22+
.Then<Task2>()
23+
.CompensateWith<UndoTask2>()
24+
.Then<Task3>()
25+
.CompensateWith<UndoTask3>()
26+
)
27+
.OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5))
28+
.Then(context => Console.WriteLine("End"));
3829
}
3930
}
4031
}

src/samples/WorkflowCore.Sample17/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ private static IServiceProvider ConfigureServices()
2727
//setup dependency injection
2828
IServiceCollection services = new ServiceCollection();
2929
services.AddLogging();
30-
//services.AddWorkflow();
31-
services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow"));
30+
services.AddWorkflow();
31+
//services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow"));
3232
//services.AddWorkflow(x => x.UseSqlServer(@"Server=.;Database=WorkflowCore;Trusted_Connection=True;", true, true));
3333
//services.AddWorkflow(x => x.UsePostgreSQL(@"Server=127.0.0.1;Port=5432;Database=workflow;User Id=postgres;", true, true));
3434

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Saga transaction with compensation sample
2+
3+
Illustrates how to encapsulate a sequence of steps within a saga transaction and specify compensation steps for each.
4+
5+
In the sample, `Task2` will throw an exception, then `UndoTask2` and `UndoTask1` will be triggered.
6+
7+
```c#
8+
builder
9+
.StartWith(context => Console.WriteLine("Begin"))
10+
.Saga(saga => saga
11+
.StartWith<Task1>()
12+
.CompensateWith<UndoTask1>()
13+
.Then<Task2>()
14+
.CompensateWith<UndoTask2>()
15+
.Then<Task3>()
16+
.CompensateWith<UndoTask3>()
17+
)
18+
.OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5))
19+
.Then(context => Console.WriteLine("End"));
20+
```
21+
22+
## Retry policy for failed saga transaction
23+
24+
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.
25+
26+
```c#
27+
builder
28+
.StartWith(context => Console.WriteLine("Begin"))
29+
.Saga(saga => saga
30+
.StartWith<Task1>()
31+
.CompensateWith<UndoTask1>()
32+
.Then<Task2>()
33+
.CompensateWith<UndoTask2>()
34+
.Then<Task3>()
35+
.CompensateWith<UndoTask3>()
36+
)
37+
.CompensateWith<CleanUp>()
38+
.Then(context => Console.WriteLine("End"));
39+
```
40+
41+
## Compensate entire saga transaction
42+
43+
You could also only specify a master compensation step, as follows
44+
45+
```c#
46+
builder
47+
.StartWith(context => Console.WriteLine("Begin"))
48+
.Saga(saga => saga
49+
.StartWith<Task1>()
50+
.Then<Task2>()
51+
.Then<Task3>()
52+
)
53+
.CompensateWith<UndoEverything>()
54+
.Then(context => Console.WriteLine("End"));
55+
```
56+
57+
## Passing parameters to compensation steps
58+
59+
Parameters can be passed to a compensation step as follows
60+
61+
```c#
62+
builder
63+
.StartWith<SayHello>()
64+
.CompensateWith<PrintMessage>(compensate =>
65+
{
66+
compensate.Input(step => step.Message, data => "undoing...");
67+
})
68+
```
69+
70+
## Expressing a saga in JSON
71+
72+
A saga transaction can be expressed in JSON, by using the `WorkflowCore.Primitives.Sequence` step and setting the `Saga` parameter to `true`.
73+
74+
The compensation steps can be defined by specifying the `CompensateWith` parameter.
75+
76+
```json
77+
{
78+
"Id": "Saga-Sample",
79+
"Version": 1,
80+
"DataType": "MyApp.MyDataClass, MyApp",
81+
"Steps": [
82+
{
83+
"Id": "Hello",
84+
"StepType": "MyApp.HelloWorld, MyApp",
85+
"NextStepId": "MySaga"
86+
},
87+
{
88+
"Id": "MySaga",
89+
"StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore",
90+
"NextStepId": "Bye",
91+
"Saga": true,
92+
"Do": [
93+
[
94+
{
95+
"Id": "do1",
96+
"StepType": "MyApp.Task1, MyApp",
97+
"NextStepId": "do2",
98+
"CompensateWith": [
99+
{
100+
"Id": "undo1",
101+
"StepType": "MyApp.UndoTask1, MyApp"
102+
}
103+
]
104+
},
105+
{
106+
"Id": "do2",
107+
"StepType": "MyApp.Task2, MyApp",
108+
"CompensateWith": [
109+
{
110+
"Id": "undo2-1",
111+
"NextStepId": "undo2-2",
112+
"StepType": "MyApp.UndoTask2, MyApp"
113+
},
114+
{
115+
"Id": "undo2-2",
116+
"StepType": "MyApp.DoSomethingElse, MyApp"
117+
}
118+
]
119+
}
120+
]
121+
]
122+
},
123+
{
124+
"Id": "Bye",
125+
"StepType": "MyApp.GoodbyeWorld, MyApp"
126+
}
127+
]
128+
}
129+
```
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using WorkflowCore.Interface;
3+
using WorkflowCore.Models;
4+
5+
namespace WorkflowCore.Sample17.Steps
6+
{
7+
public class Task1 : StepBody
8+
{
9+
public override ExecutionResult Run(IStepExecutionContext context)
10+
{
11+
Console.WriteLine("Doing Task 1");
12+
return ExecutionResult.Next();
13+
}
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using WorkflowCore.Interface;
3+
using WorkflowCore.Models;
4+
5+
namespace WorkflowCore.Sample17.Steps
6+
{
7+
public class Task2 : StepBody
8+
{
9+
public override ExecutionResult Run(IStepExecutionContext context)
10+
{
11+
Console.WriteLine("Doing Task 2");
12+
throw new Exception();
13+
}
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using WorkflowCore.Interface;
3+
using WorkflowCore.Models;
4+
5+
namespace WorkflowCore.Sample17.Steps
6+
{
7+
public class Task3 : StepBody
8+
{
9+
public override ExecutionResult Run(IStepExecutionContext context)
10+
{
11+
Console.WriteLine("Doing Task 3");
12+
return ExecutionResult.Next();
13+
}
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using WorkflowCore.Interface;
3+
using WorkflowCore.Models;
4+
5+
namespace WorkflowCore.Sample17.Steps
6+
{
7+
public class UndoTask1 : StepBody
8+
{
9+
public override ExecutionResult Run(IStepExecutionContext context)
10+
{
11+
Console.WriteLine("Undoing Task 1");
12+
return ExecutionResult.Next();
13+
}
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using WorkflowCore.Interface;
3+
using WorkflowCore.Models;
4+
5+
namespace WorkflowCore.Sample17.Steps
6+
{
7+
public class UndoTask2 : StepBody
8+
{
9+
public override ExecutionResult Run(IStepExecutionContext context)
10+
{
11+
Console.WriteLine("Undoing Task 2");
12+
return ExecutionResult.Next();
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)