Skip to content

Commit 0475e7f

Browse files
authored
Merge pull request #115 from mizrael/feature/docs-github-pages
feat: migrate docs from GitBook to GitHub Pages
2 parents cf4b0c3 + 3b7838b commit 0475e7f

File tree

17 files changed

+515
-1
lines changed

17 files changed

+515
-1
lines changed

.github/workflows/docs.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Deploy Docs to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [develop]
6+
paths:
7+
- 'docs/**'
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
pages: write
13+
id-token: write
14+
15+
concurrency:
16+
group: "pages"
17+
cancel-in-progress: false
18+
19+
jobs:
20+
build:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v4
25+
26+
- name: Setup Pages
27+
uses: actions/configure-pages@v5
28+
29+
- name: Build with Jekyll
30+
uses: actions/jekyll-build-pages@v1
31+
with:
32+
source: ./docs
33+
destination: ./_site
34+
35+
- name: Upload artifact
36+
uses: actions/upload-pages-artifact@v3
37+
38+
deploy:
39+
environment:
40+
name: github-pages
41+
url: ${{ steps.deployment.outputs.page_url }}
42+
runs-on: ubuntu-latest
43+
needs: build
44+
steps:
45+
- name: Deploy to GitHub Pages
46+
id: deployment
47+
uses: actions/deploy-pages@v4

docs/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
_site/
2+
.jekyll-cache/
3+
.jekyll-metadata
4+
Gemfile.lock
5+
.sass-cache/

docs/Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source "https://rubygems.org"
2+
3+
gem "jekyll", "~> 4.3"
4+
gem "just-the-docs", "~> 0.10"

docs/_config.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
source: "."
2+
title: OpenSleigh
3+
description: A distributed Saga management library for .NET Core
4+
baseurl: "/OpenSleigh"
5+
url: "https://mizrael.github.io"
6+
7+
theme: just-the-docs
8+
9+
color_scheme: light
10+
11+
nav_enabled: true
12+
search_enabled: true
13+
14+
heading_anchors: true
15+
16+
back_to_top: true
17+
back_to_top_text: "Back to top"
18+
19+
exclude:
20+
- plans/
21+
- Gemfile.lock
389 KB
Loading

docs/how-to/first-steps.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
layout: default
3+
title: First Steps
4+
parent: How-To
5+
nav_order: 2
6+
---
7+
8+
# First Steps
9+
10+
OpenSleigh is intended to be flexible and developer-friendly. It makes use of Dependency Injection for its own initialization and the setup of the dependencies.
11+
12+
## Configure Transport and Persistence
13+
14+
The first step, once you have installed the [Core library](https://www.nuget.org/packages/OpenSleigh/), is to add OpenSleigh to the Services collection:
15+
16+
```csharp
17+
Host.CreateDefaultBuilder(args)
18+
.ConfigureServices((hostContext, services) => {
19+
services.AddOpenSleigh(cfg =>{ ... });
20+
});
21+
```
22+
23+
OpenSleigh needs to be configured to point to a specific Transport bus and a Persistence mechanism for the Saga States:
24+
25+
```csharp
26+
Host.CreateDefaultBuilder(args)
27+
.ConfigureServices((hostContext, services) => {
28+
services.AddOpenSleigh(cfg =>{
29+
var rabbitSection = hostContext.Configuration.GetSection("Rabbit");
30+
var rabbitCfg = new RabbitConfiguration(rabbitSection["HostName"],
31+
rabbitSection["UserName"],
32+
rabbitSection["Password"]);
33+
34+
cfg.UseRabbitMQTransport(rabbitCfg);
35+
36+
var mongoSection = hostContext.Configuration.GetSection("Mongo");
37+
var mongoCfg = new MongoConfiguration(mongoSection["ConnectionString"],
38+
mongoSection["DbName"],
39+
MongoSagaStateRepositoryOptions.Default);
40+
41+
cfg.UseMongoPersistence(mongoCfg);
42+
});
43+
});
44+
```
45+
46+
In this example, the system is configured to use RabbitMQ as message bus and MongoDB to persist the data.
47+
48+
**IMPORTANT**: for detailed instructions on each Transport and Persistence mechanism, please refer to the library's documentation.
49+
50+
## Adding a Saga
51+
52+
A Saga is a simple class inheriting from the base [`Saga`](https://github.com/mizrael/OpenSleigh/blob/develop/src/OpenSleigh/Saga.cs) class:
53+
54+
```csharp
55+
public record MyAwesomeSagaState { }
56+
57+
public class MyAwesomeSaga : Saga
58+
{
59+
private readonly ILogger<MyAwesomeSaga> _logger;
60+
61+
public MyAwesomeSaga(ILogger<MyAwesomeSaga> logger)
62+
{
63+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
64+
}
65+
}
66+
```
67+
68+
Dependency injection can be used to provide services to a Saga.
69+
70+
Now, all you have to do is register and configure the Saga:
71+
72+
```csharp
73+
services.AddOpenSleigh(cfg =>{
74+
cfg.AddSaga<MyAwesomeSaga>();
75+
});
76+
```
77+
78+
Sagas can also hold some _state_. In this case, we need to define its shape by creating a `class` or a `record` and setting it on the Saga:
79+
80+
```csharp
81+
public record MySagaState
82+
{
83+
public int Foo = 42;
84+
public string Bar = "71";
85+
};
86+
87+
public class SagaWithState : Saga<MySagaState> {
88+
89+
}
90+
```
91+
92+
Now you can register it this way:
93+
94+
```csharp
95+
services.AddOpenSleigh(cfg =>{
96+
cfg.AddSaga<SagaWithState, MySagaState>();
97+
});
98+
```
99+
100+
The State can be accessed later on via `this.Context.State` and is also persisted automatically after a message is processed. For more details, check the [Handling Messages]({% link how-to/handling-messages.md %}) page.
101+
102+
**IMPORTANT**: each Saga should have its own State class. Don't reuse State classes!

docs/how-to/handling-messages.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
layout: default
3+
title: Handling Messages
4+
parent: How-To
5+
nav_order: 3
6+
---
7+
8+
# Handling Messages
9+
10+
## Starting a Saga
11+
12+
In order to start a Saga, we need to tell OpenSleigh which message type can be used as "initiator". To do that, we need to add the [`IStartedBy<>`](https://github.com/mizrael/OpenSleigh/blob/develop/src/OpenSleigh/Transport/IStartedBy.cs) interface to the Saga and implement it:
13+
14+
```csharp
15+
public class MyAwesomeSaga :
16+
Saga,
17+
IStartedBy<StartMyAwesomeSaga>
18+
{
19+
public async ValueTask HandleAsync(
20+
IMessageContext<StartMyAwesomeSaga> context,
21+
CancellationToken cancellationToken = default)
22+
{
23+
_logger.LogInformation($"starting saga '{context.Message.CorrelationId}'...");
24+
}
25+
}
26+
```
27+
28+
Messages are simple POCO classes (or records), implementing the [`IMessage`](https://github.com/mizrael/OpenSleigh/blob/develop/src/OpenSleigh/Transport/IMessage.cs) interface:
29+
30+
```csharp
31+
public record StartMyAwesomeSaga() : IMessage { }
32+
```
33+
34+
## Handling messages
35+
36+
In order to handle more message types, it is necessary to add and implement the [`IHandleMessage<>`](https://github.com/mizrael/OpenSleigh/blob/develop/src/OpenSleigh/Transport/IHandleMessage.cs) interface on the Saga:
37+
38+
```csharp
39+
public class MyAwesomeSaga :
40+
Saga,
41+
IStartedBy<StartMyAwesomeSaga>,
42+
IHandleMessage<MyAwesomeSagaCompleted>
43+
{
44+
// code omitted for brevity
45+
46+
public async ValueTask HandleAsync(
47+
IMessageContext<MyAwesomeSagaCompleted> context,
48+
CancellationToken cancellationToken = default)
49+
{
50+
_logger.LogInformation($"saga '{context.Message.CorrelationId}' completed!");
51+
}
52+
}
53+
```
54+
55+
## Stopping a Saga
56+
57+
A Saga can be marked as completed by calling the `MarkAsCompleted()` on its state:
58+
59+
```csharp
60+
public class MyAwesomeSaga :
61+
Saga,
62+
IStartedBy<StartMyAwesomeSaga>,
63+
IHandleMessage<MyAwesomeSagaCompleted>
64+
{
65+
// code omitted for brevity
66+
67+
public async ValueTask HandleAsync(
68+
IMessageContext<MyAwesomeSagaCompleted> context,
69+
CancellationToken cancellationToken = default)
70+
{
71+
this.Context.MarkAsCompleted();
72+
}
73+
}
74+
```
75+
76+
This step is completely optional but signals OpenSleigh to stop sending messages to that Saga instance.

docs/how-to/idempotency.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
layout: default
3+
title: Idempotency
4+
parent: How-To
5+
nav_order: 5
6+
---
7+
8+
# Idempotency
9+
10+
By design, OpenSleigh uses the [Outbox pattern](https://www.davidguida.net/improving-microservices-reliability-part-2-outbox-pattern/) to ensure at-least-once delivery. This guarantees the reliability of the publisher side, but we still need to rely on the transport mechanism to deliver the message.
11+
12+
On the receiver side, we can expect messages to be processed as they arrive. But what happens when the same message is published more than once?
13+
14+
OpenSleigh assigns an ID to each outgoing message, which is then used on the receiver side to ensure at-most-once processing. When a message is received, OpenSleigh fetches the metadata of the desired Saga, locks it so no other service instance can access it, and compares the message ID against the list of all the messages processed so far.
15+
16+
Normally, this message ID is a simple [GUID v7](https://learn.microsoft.com/en-us/dotnet/api/system.guid.createversion7?view=net-9.0), but it is also possible to customize the ID generation by having your message class implement [`IIdempotentMessage`](https://github.com/mizrael/OpenSleigh/blob/develop/src/OpenSleigh/Transport/IIdempotentMessage.cs) instead of [`IMessage`](https://github.com/mizrael/OpenSleigh/blob/develop/src/OpenSleigh/Transport/IMessage.cs):
17+
18+
```csharp
19+
public record IdempotentMessage(string RequestId, int Foo) : IIdempotentMessage
20+
{
21+
public IEnumerable<object> GetIdempotencyComponents()
22+
{
23+
yield return this.Foo;
24+
}
25+
}
26+
```
27+
28+
The `GetIdempotencyComponents` property helps OpenSleigh pick which elements of the message have to be utilized to build the _idempotency key_ that will later on be used as message ID.

docs/how-to/index.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
layout: default
3+
title: How-To
4+
nav_order: 3
5+
has_children: true
6+
---
7+
8+
# How-To
9+
10+
This section contains step-by-step guides to help you get started with OpenSleigh and make the most of its features.
11+
12+
- [Installation]({% link how-to/installation.md %}) — Install the NuGet packages
13+
- [First Steps]({% link how-to/first-steps.md %}) — Configure transport, persistence, and your first Saga
14+
- [Handling Messages]({% link how-to/handling-messages.md %}) — Start, handle, and stop Sagas
15+
- [Publishing Messages]({% link how-to/publishing-messages.md %}) — Publish messages and use publish-only mode
16+
- [Idempotency]({% link how-to/idempotency.md %}) — Ensure at-most-once message processing
17+
- [Logging Levels]({% link how-to/logging-levels.md %}) — Configure logging for OpenSleigh

docs/how-to/installation.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
layout: default
3+
title: Installation
4+
parent: How-To
5+
nav_order: 1
6+
---
7+
8+
# Installation
9+
10+
OpenSleigh Core, Persistence, and Transport libraries are all available as NuGet packages:
11+
12+
## Core
13+
14+
- [Core library](https://www.nuget.org/packages/OpenSleigh/)
15+
- [In-Memory](https://www.nuget.org/packages/OpenSleigh.InMemory/) (useful for local dev)
16+
17+
## Persistence
18+
19+
- [MongoDB](https://www.nuget.org/packages/OpenSleigh.Persistence.Mongo/)
20+
- [MSSQL](https://www.nuget.org/packages/OpenSleigh.Persistence.SQLServer/)
21+
- [PostgreSQL](https://www.nuget.org/packages/OpenSleigh.Persistence.PostgreSQL/)
22+
23+
## Transport
24+
25+
- [RabbitMQ](https://www.nuget.org/packages/OpenSleigh.Transport.RabbitMQ/)
26+
- [Kafka](https://www.nuget.org/packages/OpenSleigh.Transport.Kafka/)

0 commit comments

Comments
 (0)