Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nillerr/aggregail
https://github.com/nillerr/aggregail
Last synced: 5 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/nillerr/aggregail
- Owner: Nillerr
- License: mit
- Created: 2020-05-04T14:54:59.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2022-12-08T10:33:10.000Z (about 2 years ago)
- Last Synced: 2024-12-13T03:51:34.933Z (27 days ago)
- Language: C#
- Size: 1.31 MB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Aggregail
A small framework for implementing Aggregate Roots, which are a critical part of Event Sourcing, DDD and CQRS, backed by an event store.
Provides connectors for [Event Store](https://www.eventstore.com) and MongoDB.
| Package | NuGet | |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| Aggregail | [![NuGet](https://img.shields.io/nuget/v/Aggregail)](https://www.nuget.org/packages/Aggregail) | [![Download](https://img.shields.io/nuget/dt/Aggregail)](https://www.nuget.org/packages/Aggregail) |
| [Aggregail.Newtonsoft.Json](Aggregail.Newtonsoft.Json) | [![NuGet](https://img.shields.io/nuget/v/Aggregail.Newtonsoft.Json)](https://www.nuget.org/packages/Aggregail.Newtonsoft.Json) | [![Download](https://img.shields.io/nuget/dt/Aggregail.Newtonsoft.Json)](https://www.nuget.org/packages/Aggregail.Newtonsoft.Json) |
| [Aggregail.System.Text.Json](Aggregail.System.Text.Json) | [![NuGet](https://img.shields.io/nuget/v/Aggregail.System.Text.Json)](https://www.nuget.org/packages/Aggregail.System.Text.Json) | [![Download](https://img.shields.io/nuget/dt/Aggregail.System.Text.Json)](https://www.nuget.org/packages/Aggregail.System.Text.Json) |
| [Aggregail.Testing](Aggregail.Testing) | [![NuGet](https://img.shields.io/nuget/v/Aggregail.Testing)](https://www.nuget.org/packages/Aggregail.Testing) | [![Download](https://img.shields.io/nuget/dt/Aggregail.Testing)](https://www.nuget.org/packages/Aggregail.Testing) |
| [Aggregail.MongoDB](Aggregail.MongoDB) | [![NuGet](https://img.shields.io/nuget/v/Aggregail.MongoDB)](https://www.nuget.org/packages/Aggregail.MongoDB) | [![Download](https://img.shields.io/nuget/dt/Aggregail.MongoDB)](https://www.nuget.org/packages/Aggregail.MongoDB) |
| [Aggregail.MongoDB.Admin](Aggregail.MongoDB.Admin) | [![NuGet](https://img.shields.io/nuget/v/Aggregail.MongoDB.Admin)](https://www.nuget.org/packages/Aggregail.MongoDB.Admin) | [![Download](https://img.shields.io/nuget/dt/Aggregail.MongoDB.Admin)](https://www.nuget.org/packages/Aggregail.MongoDB.Admin) |
| Aggregail.EventStore | WIP | |A practical example
## Goat
The aggregate root in our example will be a goat. Every aggregate needs at least one event to create it,
let's call that event `GoatCreated`:```c#
public class GoatCreated
{
public static readonly EventType EventType = "GoatCreated";public string Name { get; set; }
}
```It wouldn't be much of an example if we could not update the Aggregate Root after creation, so let's
have an event for that `GoatUpdated`:```c#
public class GoatUpdated
{
public static readonly EventType EventType = "GoatUpdated";public string Name { get; set; }
}
```Our goat itself is an example of the minimum amount of code required to make a somewhat interesting
Aggregate Root:```c#
public class Goat : AbstractAggregate
{
static Goat()
{
Configuration = new AggregateConfiguration("goat", Guid.Parse)
.Constructs(GoatCreated.EventType, (id, e) => new Goat(id, e))
.Applies(GoatUpdated.EventType, (goat, e) => goat.Apply(e));
}
public static Goat Create(Guid id, string name) =>
Create(GoatCreated.EventType, new GoatCreated { Name = name }, (id, e) => new Goat(id, e));private Goat(Guid id, GoatCreated e) : base(id)
{
Name = e.Name;
}public string Name { get; private set; }
public void Update(string name) =>
Append(GoatUpdated.EventType, new GoatUpdated { Name = name }, Apply);private void Apply(GoatUpdated e)
{
Name = e.Name;
}
}
```### Commands
The code above defines two _commands_ and two _events_, and configures the applicator
(or constructor) for each event type for the Aggregate Root `Goat`.| Command | Event | Applicator |
|--------------------------------------------|---------------|--------------------------------|
| `static Goat Create(Guid id, string name)` | `GoatCreated` | `Goat(Guid id, GoatCreated e)` |
| `void Update(string name)` | `GoatUpdated` | `void Apply(GoatUpdated e)` |### Create
```c#
public class CreateGoatRequest
{
public Guid Id { get; set; }
public string Name { get; set; }
}[Route("goats")]
public class GoatsController
{
private readonly IEventStore _store;public GoatsController(IEventStore store)
{
_store = store;
}[HttpPost]
public async Task CreateAsync(CreateGoatRequest request)
{
var goat = Goat.Create(request.Id, request.Name);
await gota.CommitAsync(_store);
return Created();
}
}
```### Fetch
```c#
[Route("goats")]
public class GoatsController
{
private readonly IEventStore _store;public GoatsController(IEventStore store)
{
_store = store;
}[HttpGet("{id}")]
public async Task GetAsync(Guid id)
{
var goat = await Goat.FromAsync(_store, id);
if (goat == null)
{
return NotFound();
}
return Ok(goat);
}
}
```### Update
```c#
public class UpdateGoatRequest
{
public string Name { get; set; }
}[Route("goats")]
public class GoatsController
{
private readonly IEventStore _store;public GoatsController(IEventStore store)
{
_store = store;
}[HttpPatch("{id}")]
public async Task UpdateAsync(Guid id, UpdateGoatRequest request)
{
var goat = await Goat.FromAsync(_store, id);
if (goat == null)
{
return NotFound();
}
goat.Update(request.Name);
await goat.CommitAsync(_store);
return Ok();
}
}
```### Delete
Deleting Aggregate Roots, and thus streams is currently not supported by Aggregail, but is actively being worked upon. We
know it is a vital part in supporting several use cases due to GDPR. If using MongoDB as an event store, streams can
easily be deleted without the use of Aggregail:```c#
db.aggregail.deleteMany({ stream: "goat-a4a4e832-f577-4461-a50c-d9c83342ee6f" })
```Likewise, when using Event Store, the streams can be deleted using the `IEventStoreConnection`, or using the UI.
## Concepts
### Aggregate (Root)
#### Events
#### Commands
#### Configuration
##### Constructor
###### Applicator