All Projects → jacqueskang → Eventsourcing

jacqueskang / Eventsourcing

Licence: mit
.NET Core event sourcing framework

Projects that are alternatives of or similar to Eventsourcing

ddes
JS/TypeScript framework for Distributed Event Sourcing & CQRS
Stars: ✭ 28 (-79.1%)
Mutual labels:  cqrs, dynamodb, event-sourcing
Event Store Symfony Bundle
Event Store Symfony Bundle
Stars: ✭ 93 (-30.6%)
Mutual labels:  event-sourcing, cqrs
Vertex
Vertex is a distributed, ultimately consistent, event traceable cross platform framework based on Orleans, which is used to build high-performance, high throughput, low latency, scalable distributed applications
Stars: ✭ 117 (-12.69%)
Mutual labels:  event-sourcing, cqrs
Axondemo
Using Axon + Spring Cloud + Spring Cloud Stream + JPA to implement event sourcing and CQRS
Stars: ✭ 101 (-24.63%)
Mutual labels:  event-sourcing, cqrs
Aspnetcore Ddd
Full ASP.NET Core 3.1 LTS application with DDD, CQRS and Event Sourcing
Stars: ✭ 88 (-34.33%)
Mutual labels:  event-sourcing, cqrs
Productcontext Eventsourcing
A practical/experimental Event Sourcing application on Product Bounded Context in an e-commerce
Stars: ✭ 88 (-34.33%)
Mutual labels:  event-sourcing, cqrs
Patientmangement
A simple example of how to build an event sourced application
Stars: ✭ 97 (-27.61%)
Mutual labels:  event-sourcing, cqrs
Nestjs Cqrs Starter
NestJS CQRS Microservices Starter Project
Stars: ✭ 80 (-40.3%)
Mutual labels:  event-sourcing, cqrs
Bifrost
This is the stable release of Dolittle till its out of alpha->beta stages
Stars: ✭ 111 (-17.16%)
Mutual labels:  event-sourcing, cqrs
Dotnet Cqrs Intro
Examples of implementation CQRS with Event Sourcing - evolutionary approach
Stars: ✭ 113 (-15.67%)
Mutual labels:  event-sourcing, cqrs
User Bundle
A new Symfony user bundle
Stars: ✭ 116 (-13.43%)
Mutual labels:  event-sourcing, cqrs
Symfony Demo App
A Symfony demo application with basic user management
Stars: ✭ 122 (-8.96%)
Mutual labels:  event-sourcing, cqrs
Commanded
Use Commanded to build Elixir CQRS/ES applications
Stars: ✭ 1,280 (+855.22%)
Mutual labels:  event-sourcing, cqrs
Loom
Stars: ✭ 88 (-34.33%)
Mutual labels:  event-sourcing, cqrs
Cafeapp
A Real World Business Application using F# and Suave
Stars: ✭ 86 (-35.82%)
Mutual labels:  event-sourcing, cqrs
Pdo Event Store
PDO implementation of ProophEventStore http://getprooph.org
Stars: ✭ 96 (-28.36%)
Mutual labels:  event-sourcing, cqrs
Eventflow.example
DDD+CQRS+Event-sourcing examples using EventFlow following CQRS-ES architecture. It is configured with RabbitMQ, MongoDB(Snapshot store), PostgreSQL(Read store), EventStore(GES). It's targeted to .Net Core 2.2 and include docker compose file.
Stars: ✭ 131 (-2.24%)
Mutual labels:  event-sourcing, cqrs
Event Sourcing Castanha
An Event Sourcing service template with DDD, TDD and SOLID. It has High Cohesion and Loose Coupling, it's a good start for your next Microservice application.
Stars: ✭ 68 (-49.25%)
Mutual labels:  event-sourcing, cqrs
Hacker News Resolve
React & Redux & Resolve implementation of Hacker News
Stars: ✭ 79 (-41.04%)
Mutual labels:  event-sourcing, cqrs
Extreme
Elixir Adapter for EventStore
Stars: ✭ 110 (-17.91%)
Mutual labels:  event-sourcing, cqrs

Build Status

EventSourcing

A .NET Core event sourcing framework.

Easy to be integrated in ASP.NET Core web application, Lambda function or Azure function.

Support various of event store:

NuGet packages

  • JKang.EventSourcing NuGet version
  • JKang.EventSourcing.Persistence.FileSystem NuGet version
  • JKang.EventSourcing.Persistence.EfCore NuGet version
  • JKang.EventSourcing.Persistence.DynamoDB NuGet version
  • JKang.EventSourcing.Persistence.CosmosDB NuGet version
  • JKang.EventSourcing.Persistence.S3 NuGet version
  • JKang.EventSourcing.Persistence.Caching NuGet version

Quick Start:

Let's implement a simple gift card management system with the following use cases:

  • Create gift cards with initial credit
  • Debit the gift card specifying amount while overpaying is not allowed

I'm adopting DDD (Domain Driven Design) approach and implement the GiftCard entity as an Rich Domain Aggregate which encapsulates/protects its internal data/state, and contains itself business logics ensuring data integrity.

Step 1 - Create aggregate events

public sealed class GiftCardCreated : AggregateCreatedEvent<Guid>
{
    public GiftCardCreated(Guid aggregateId, DateTime timestamp, decimal initialCredit)
        : base(aggregateId, timestamp)
    {
        InitialCredit = initialCredit;
    }

    public decimal InitialCredit { get; }
}
public class GiftCardDebited : AggregateEvent<Guid>
{
    public GiftCardDebited(Guid aggregateId, int aggregateVersion, DateTime timestamp, decimal amount)
        : base(aggregateId, aggregateVersion, timestamp)
    {
        Amount = amount;
    }

    public decimal Amount { get; }
}

Notes:

  • It's recommended to implement aggregate event in an immutable way.
  • Inheriting from AggregateEvent<TKey> or AggregateCreatedEvent<TKey> is not mandatory, but an aggreagte event must at least implement IAggregateEvent<TKey> interface.
  • In order to use built-in event stores, please make sure event can be properly serialized using Json.NET.

Step 2 - Create domain aggregate

public class GiftCard : Aggregate<Guid>
{
    /// <summary>
    /// Constructor for creating an new gift card from scratch
    /// </summary>
    public GiftCard(decimal initialCredit)
        : base(new GiftCardCreated(Guid.NewGuid(), DateTime.UtcNow, initialCredit))
    { }

    /// <summary>
    /// Constructor for rehydrating gift card from historical events
    /// </summary>
    public GiftCard(Guid id, IEnumerable<IAggregateEvent<Guid>> savedEvents)
        : base(id, savedEvents)
    { }

    /// <summary>
    /// Constructor for rehydrating gift card from a snapshot + historical events after the snapshot
    /// </summary>
    public GiftCard(Guid id, IAggregateSnapshot<Guid> snapshot, IEnumerable<IAggregateEvent<Guid>> savedEvents)
        : base(id, snapshot, savedEvents)
    { }

    public decimal Balance { get; private set; }

    public void Debit(decimal amout)
        => ReceiveEvent(new GiftCardDebited(Id, GetNextVersion(), DateTime.UtcNow, amout));

    protected override void ApplyEvent(IAggregateEvent<Guid> @event)
    {
        if (@event is GiftCardCreated created)
        {
            Balance = created.InitialCredit;
        }
        else if (@event is GiftCardDebited debited)
        {
            if (debited.Amount < 0)
            {
                throw new InvalidOperationException("Negative debit amout is not allowed.");
            }

            if (Balance < debited.Amount)
            {
                throw new InvalidOperationException("Not enough credit");
            }

            Balance -= debited.Amount;
        }
    }
}

Notes:

  • Please ensure that state of domain aggregate can only be changed by applying aggregate events.
  • Inheriting from Aggregate<TKey> is not mandatory, but the minimum requirements for implementing a domain aggregate are:
    • Implement IAggregate<TKey> interface
    • Have a public constructor with signature MyAggregate(TKey id, IEnumerable<IAggregateEvent<TKey>> savedEvents)
    • Have a public constructor with signature MyAggregate(TKey id, IAggregateSnapshot<TKey> snapshot, IEnumerable<IAggregateEvent<TKey>> savedEvents)

Step 3 - Implement repository

By definition of Event Sourcing, persisting an aggregate insists on persisting all historical events.

public interface IGiftCardRepository
{
    Task SaveGiftCardAsync(GiftCard giftCard);
    Task<GiftCard> FindGiftCardAsync(Guid id);
}
public class GiftCardRepository : AggregateRepository<GiftCard, Guid>, 
    IGiftCardRepository
{
    public GiftCardRepository(IEventStore<GiftCard, Guid> eventStore)
        : base(eventStore)
    { }

    public Task SaveGiftCardAsync(GiftCard giftCard) =>
        SaveAggregateAsync(giftCard);

    public Task<GiftCard> FindGiftCardAsync(Guid id) =>
        FindAggregateAsync(id);
}

Step 4 - Register your repository interface and configure event store in dependency injection framework

services
    .AddScoped<IGiftCardRepository, GiftCardRepository>();

services
    .AddEventSourcing(builder =>
    {
        builder.UseTextFileEventStore<GiftCard, Guid>(x =>
            x.Folder = "C:/Temp/GiftcardEvents");
    });

Notes:

  • You can choose other persistence store provided such as CosmosDB or DynamoDB etc.

Step 5 - implmement use cases

// create a new gift card with initial credit 100
var giftCard = new GiftCard(100);

// persist the gift card
await _repository.SaveGiftCardAsync(giftCard);

// rehydrate the giftcard
giftCard = await _repository.FindGiftCardAsync(giftCard.Id);

// payments
giftCard.Debit(40); // ==> balance: 60
giftCard.Debit(50); // ==> balance: 10
giftCard.Debit(20); // ==> invalid operation exception

FAQs

How to programmatically initialize event store?

See this page.

How to use snapshots to optimize performance?

See this page.

How to improve performance using caching?

Consider install the nuget package JKang.EventSourcing.Persistence.Caching and inherit the CachedAggregateRepository class. It leverages Microsoft.Extensions.Caching.Distributed.IDistributedCache to cache aggregate every time after loaded from or saved into repository.

Consider configuring a short sliding expiration (e.g., 5 sec) to reduce the chance of having cache out of date.


Please feel free to download, fork and/or provide any feedback!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].