-
Notifications
You must be signed in to change notification settings - Fork 0
/
MoneyTransferSaga.cs
79 lines (71 loc) · 3.08 KB
/
MoneyTransferSaga.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
using System;
using System.Threading.Tasks;
using Gaev.DurableTask;
using Gaev.DurableTask.Storage;
public class MoneyTransferSaga
{
private readonly IProcessHost _host;
private readonly TransferService _service;
public MoneyTransferSaga(IProcessHost host, TransferService service)
{
_host = host;
_service = service;
}
public void RegisterForResuming()
{
_host.Register(id => id.StartsWith(nameof(MoneyTransferSaga)), id => DurableTask(id));
}
public void StartTransfer(string srcAccount, string destAccount, decimal amount)
{
_host.Watch(DurableTask(nameof(MoneyTransferSaga) + Guid.NewGuid(), srcAccount, destAccount, amount));
}
private async Task DurableTask(string id, string srcAccount = null, string destAccount = null, decimal amount = 0)
{
using (var proc = _host.Spawn(id))
{
// Save values not to lose it if durable task resumes
srcAccount = await proc.Get(srcAccount, "SaveSrcAccount");
destAccount = await proc.Get(destAccount, "SaveDestAccount");
amount = await proc.Get(amount, "SaveAmount");
var srcTranId = Guid.Empty;
var destTranId = Guid.Empty;
try
{
// Start transferring the money
srcTranId = await proc.Do(() => _service.StartTransfer(srcAccount, -amount), "StartTransfer1");
destTranId = await proc.Do(() => _service.StartTransfer(destAccount, +amount), "StartTransfer2");
// Complete transferring the money
await proc.Do(() => _service.CompleteTransfer(srcAccount, srcTranId), "CompleteTransfer1");
await proc.Do(() => _service.CompleteTransfer(destAccount, destTranId), "CompleteTransfer2");
}
catch (ProcessException ex) when (ex.Type == nameof(TransferFailedException))
{
// Rollback logic
if (srcTranId != Guid.Empty)
await proc.Do(() => _service.RollbackTransfer(srcAccount, srcTranId), "RollbackTransfer1");
if (destTranId != Guid.Empty)
await proc.Do(() => _service.RollbackTransfer(destAccount, destTranId), "RollbackTransfer2");
throw;
}
}
}
public class TransferService
{
public Task<Guid> StartTransfer(string account, decimal amount) => Task.FromResult(Guid.NewGuid());
public Task CompleteTransfer(string account, Guid tranId) => Task.CompletedTask;
public Task RollbackTransfer(string account, Guid tranId) => Task.CompletedTask;
}
public class TransferFailedException : Exception { }
static void Main(string[] args)
{
using (var host = new ProcessHost(new FileSystemProcessStorage()))
{
var saga = new MoneyTransferSaga(host, new TransferService());
saga.RegisterForResuming();
host.Resume();
saga.StartTransfer("user1", "user2", 1000);
saga.StartTransfer("user1", "user3", 5000);
Console.Read();
}
}
}