NSaga - Getting started

使用 NSaga 來做 Saga pattern,需先透過 NuGet 安裝 NSaga 套件。

PM> Install-Package NSaga


接著要定義 Transaction 中的每一個動作,也就是 NSaga 中的 Message。


這邊要注意 NSaga 必須特別定義 Transaction 中的第一個 Message,定義的方式就是造一個專屬的 Message Class,並實作 IInitiatingSagaMessage 介面。

1
2
3
4
public class StartSagaMessage: IInitiatingSagaMessage
{
public Guid CorrelationId { get; set; }
}


IInitiatingSagaMessage 介面只有一個屬性 CorrelationId,用來接 Transaction Id 用,可用來識別這次觸發的 Transaction。除了介面定義的屬性外,若是需要額外的資料也可以自己附加。


第一個 Message 定義完其它後面的 Message 定義方式就一樣了,只要造專屬的 Message Class,並實作 ISagaMessage 介面即可。

1
2
3
4
public class SagaMessage: ISagaMessage
{
public Guid CorrelationId { get; set; }
}


接著要定義 Transaction 要儲存的資訊,簡單造個 Model 類別即可。

1
2
3
4
public class SagaData
{
public List<string> Executed { get; } = new List<string>();
}


再來要定義 Transaction,也就是 NSaga 中的 Saga。造一個專屬的 Saga Class,實作 ISaga、InitatedBy、ConsumerOf 介面。

1
2
3
4
public class Saga: ISaga<SagaData>, InitiatedBy<StartSagaMessage>, ConsumerOf<SagaMessage>
{
...
}


Transaction 的 Message 由 InitatedBy、ConsumerOf 介面實作去定義,Transaction的第一個 Message 用 InitatedBy 去實作,其他的 Message 都用 ConsumerOf 介面去實作,對應的實作方法用來定義執行對應 Message 時所要做的動作。

1
2
3
4
5
6
7
8
9
...
public OperationResult Initiate(StartSagaMessage message)
{
Console.WriteLine(message.GetType().Name);
SagaData.Executed.Add(message.GetType().Name);

return new OperationResult();
}
...


像是下面這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Saga: ISaga<SagaData>, InitiatedBy<StartSagaMessage>, ConsumerOf<SagaMessage>
{
public Guid CorrelationId { get; set; }
public Dictionary<string, string> Headers { get; set; }
public SagaData SagaData { get; set; }
public OperationResult Initiate(StartSagaMessage message)
{
Console.WriteLine(message.GetType().Name);
SagaData.Executed.Add(message.GetType().Name);

return new OperationResult();
}

public OperationResult Consume(SagaMessage message)
{
Console.WriteLine(message.GetType().Name);
SagaData.Executed.Add(message.GetType().Name);

return new OperationResult();
}
}


最後開始實際運行,取得 mediator 與 repository。

1
2
3
4
5
...
var builder = Wireup.UseInternalContainer();
var mediator = builder.ResolveMediator();
var repository = builder.ResolveRepository();
...


透過 mediator 依序調用 Message 即可。

1
2
3
4
5
6
...
mediator.Consume(new StartSagaMessage()
{
CorrelationId = correlationId,
});
...


若有需要可從 repository 取出儲存的資料。

1
2
3
...
var saga = repository.Find<Saga>(correlationId);
...


像是下面這樣:

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
using System;

namespace NSaga.Demo
{
class Program
{
static void Main(string[] args)
{
var builder = Wireup.UseInternalContainer();
var mediator = builder.ResolveMediator();
var repository = builder.ResolveRepository();

var correlationId = Guid.NewGuid();

mediator.Consume(new StartSagaMessage()
{
CorrelationId = correlationId,
});

mediator.Consume(new SagaMessage()
{
CorrelationId = correlationId
});

var saga = repository.Find<Saga>(correlationId);

Console.WriteLine(string.Join(", ", saga.SagaData.Executed.ToArray()));
}
}
}