{"id":21559798,"url":"https://github.com/dasiths/neventlite","last_synced_at":"2025-04-06T02:12:52.058Z","repository":{"id":38009717,"uuid":"72304374","full_name":"dasiths/NEventLite","owner":"dasiths","description":"NEventLite - An extensible lightweight library for .NET that manages the Aggregate lifecycle in an Event Sourced system. Supports Event and Snapshot storage providers like EventStore/Redis or SQL Server. Built with dependency injection in mind and seamlessly integrates with AspNetCore.","archived":false,"fork":false,"pushed_at":"2023-03-03T04:11:07.000Z","size":1376,"stargazers_count":132,"open_issues_count":10,"forks_count":19,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-03-30T01:12:40.487Z","etag":null,"topics":["aggregate-root","aspnetcore","cqrs-es","cqrs-pattern","domain-driven-design","event","event-driven","event-sourcing","event-store","netcore"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dasiths.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-29T19:12:10.000Z","updated_at":"2024-06-12T20:42:19.000Z","dependencies_parsed_at":"2024-06-19T05:32:39.762Z","dependency_job_id":"a9d6726b-3674-4792-927d-bc4ab59e0b39","html_url":"https://github.com/dasiths/NEventLite","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dasiths%2FNEventLite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dasiths%2FNEventLite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dasiths%2FNEventLite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dasiths%2FNEventLite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dasiths","download_url":"https://codeload.github.com/dasiths/NEventLite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247423518,"owners_count":20936626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aggregate-root","aspnetcore","cqrs-es","cqrs-pattern","domain-driven-design","event","event-driven","event-sourcing","event-store","netcore"],"created_at":"2024-11-24T09:09:29.416Z","updated_at":"2025-04-06T02:12:52.015Z","avatar_url":"https://github.com/dasiths.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NEventLite [![Build status](https://ci.appveyor.com/api/projects/status/yfc041h8uvt7u3dq?svg=true)](https://ci.appveyor.com/project/dasiths/neventlite) [![NuGet](https://img.shields.io/nuget/v/NEventLite.svg)](https://www.nuget.org/packages/NEventLite) [![Downloads](https://img.shields.io/nuget/dt/NEventLite.svg)](https://www.nuget.org/packages/NEventLite/)\n\n## An extensible lightweight library for .NET that manages the Aggregate lifecycle in an **Event Sourced** system\n\nSupports `Event` and `Snapshot` storage providers like EventStore/Redis or SQL Server. Built with dependency injection in mind and seamlessly integrates with AspNetCore.\n\n### Give a star :star: if you appreciate the effort\n\n## What is Event Sourcing?\n\n\u003e Use an append-only store to record the full series of events that describe actions taken on data in a domain, rather than storing just the current state.\n\nStart here [https://dasith.me/2016/12/02/event-sourcing-examined-part-1-of-3/](https://dasith.me/2016/12/02/event-sourcing-examined-part-1-of-3/)\n\n## :goal_net: What does NEventLite solve?\nNEventLite makes it easier to implement the event sourcing pattern in your .NET project. The library is opinionated and introduces some patterns to manage the life cycle of your [Aggregates](https://martinfowler.com/bliki/DDD_Aggregate.html) in an event sourced system. It will manage Creating, Loading, Mutating and Persisting Aggregates and Events.\n\n## :warning: What doesn't it solve?\n\nNEventLite is **not a framework** that manages your application end to end. It doesn't enforce ports and adapters pattern or any of the application level concerns. The aim is to do one thing (Manage aggregate lifecycle) and do that well. If you need to implement command and event handlers you can have a look at something like [SimpleMediator](https://github.com/dasiths/SimpleMediator) or [Brighter](https://github.com/BrighterCommand/Brighter) and NEventLite will complement them nicely.\n\n## What about v1.0? Wasn't it advertised as a framework? \n*NEventLite V1.0 tried to solve similar problems but the scope of the project very large and it was decided to narrow down the scope. If you're still looking for reference it's hosted [here](https://github.com/dasiths/NEventLite/blob/master/legacy/v1.0).*\n\n## :eyes: Before you start\n\n- The library targets .NET Standard 2.0\n- *Optional:* Installation of EventStore - [https://eventstore.com/](https://eventstore.com/) (You can use the in memory event and snapshot providers when developing)\n\n## :hammer: Using It\n\n1. Install and reference the NuGet `NEventLite`\n\n    In the NuGet Package Manager Console, type:\n\n    ```ps\n    Install-Package NEventLite\n    ```\n\n2. Define the events. They are simple pocos that will be serialized and stored in `EventStorage` when changes are saved. Events use `Guid` for Id by *default* but they can be changed to use any data type as Id. See `Event\u003cTAggregate, TAggregateKey, TEventKey\u003e` for reference.\n\n    ```csharp\n        public class ScheduleCreatedEvent : Event\u003cSchedule\u003e\n        {\n            public string ScheduleName { get; set; }\n\n            public ScheduleCreatedEvent(Guid scheduleId, string scheduleName) : base(Guid.NewGuid(), scheduleId)\n            {\n                ScheduleName = scheduleName;\n            }\n        }\n\n        public class TodoCreatedEvent : Event\u003cSchedule\u003e\n        {\n            public Guid TodoId { get; set; }\n            public string Text { get; set; }\n\n            public TodoCreatedEvent(Guid aggregateId, long targetVersion, Guid todoId, string text) : base(Guid.NewGuid(), aggregateId, targetVersion)\n            {\n                TodoId = todoId;\n                Text = text;\n            }\n        }\n    ```\n\n3. Define the Aggregate (`Schedule.cs` in the sample). Aggregates use `Guid` as Id by *default* but just like the events, they can be changed to the Id type of your choice. See `AggregateRoot\u003cTAggregateKey\u003e` for reference.\n\n    ```csharp\n        public class Schedule : AggregateRoot\n        {\n            public IList\u003cTodo\u003e Todos { get; private set; }\n            public string ScheduleName { get; private set; }\n\n            // To create or mutate, call the constructor or methods.\n\n            // Constructor\n            public Schedule(string scheduleName)\n            {\n                // Pattern: Create an event and apply it\n                var newScheduleId = Guid.NewGuid();\n                var @event = new ScheduleCreatedEvent(newScheduleId, scheduleName);\n                ApplyEvent(@event);\n            }\n\n            public Guid AddTodo(string text)\n            {\n                // Pattern: Create an event and apply it\n                var newTodoId = Guid.NewGuid();\n                var @event = new TodoCreatedEvent(Id, CurrentVersion, newTodoId, text);\n                ApplyEvent(@event);\n                return newTodoId;\n            }\n\n            // ** Mutations happen through applying events **\n            // The library identifies the internal event handler methods though a special method attribute.\n\n            [InternalEventHandler]\n            public void OnScheduleCreated(ScheduleCreatedEvent @event)\n            {\n                ScheduleName = @event.ScheduleName;\n                Todos = new List\u003cTodo\u003e();\n            }\n\n            [InternalEventHandler]\n            public void OnTodoCreated(TodoCreatedEvent @event)\n            {\n                var todo = new Todo(@event.TodoId, @event.Text);\n                Todos.Add(todo);\n            }\n        }\n    ```\n\n4. Use the built in `Session` and `Repository` implementations to manage the Aggregate lifecycle. This is an example of how you would create and update a `Schedule` in your domain layer using some sort of command handler.\n\n    ```csharp\n    // We recommend using a DI container to inject the Session. Keep it scoped (per request in a web application)\n\n        public class CreateScheduleCommandHandler\n        {\n            private readonly ISession\u003cSchedule\u003e _session;\n\n            public CreateScheduleCommandHandler(ISession\u003cSchedule\u003e session)\n            {\n                _session = session;\n            }\n\n            public async Task\u003cGuid\u003e HandleAsync()\n            {\n                var schedule = new Schedule(\"my new schedule\");\n                _session.Attach(schedule);\n                await _session.SaveAsync();\n                return schedule.Id;\n            }\n        }\n\n        public class AddTodoCommandHandler\n        {\n            private readonly ISession\u003cSchedule\u003e _session;\n\n            public AddTodoCommandHandler(ISession\u003cSchedule\u003e session)\n            {\n                _session = session;\n            }\n\n            public async Task\u003cGuid\u003e HandleAsync(Guid scheduleId)\n            {\n                var schedule = await _session.GetByIdAsync(scheduleId);\n                var id = schedule.AddTodo(\"test todo 1\");\n                await _session.SaveAsync();\n                return id;\n            }\n        }\n    ```\n\n5. If you are using it in ASPNET.CORE consider using the `NEventLite.Extensions.Microsoft.DependencyInjection` NuGet package to setup your container in the dependency injection example below.\n\n## :syringe: Dependency Injection\n\nThe library is built with DI as a first class concept. Wiring it up is easy. This is an example of how you would do it with `Microsoft.Extensions.DependencyInjection`. You can find a detailed [example in the sample console app](https://github.com/dasiths/NEventLite/blob/master/src/Samples/NEventLite.Samples.ConsoleApp/DependencyInjection.cs).\n\n```csharp\n    public static ServiceProvider GetContainer() {\n        var services = new ServiceCollection();\n\n        // \u003e\u003e Step 1: Register the storage provider\n\n            // Wiring up the event storage provider. \"https://eventstore.com/\" in this example\n            // Event Store specific settings etc\n            services.AddScoped\u003cIEventStoreSettings, EventStoreSettings\u003e(\n                (sp) =\u003e new EventStoreSettings(SnapshotFrequency, PageSize));\n            services.AddScoped\u003cIEventStoreStorageConnectionProvider, EventStoreStorageConnectionProvider\u003e();\n            services.AddScoped\u003cIEventStoreStorageCore, EventStoreStorageCore\u003e();\n\n            // The storage provider implementations\n            services.AddScoped\u003cIEventStorageProvider\u003cGuid\u003e, EventStoreEventStorageProvider\u003e();\n            services.AddScoped\u003cISnapshotStorageProvider\u003cGuid\u003e, EventStoreSnapshotStorageProvider\u003e();\n\n        // \u003e\u003e Step 2: Register the Repository and Session\n\n            // Use the extension method \"ScanAndRegisterAggregates()\" in the\n            // \"NEventLite.Extensions.Microsoft.DependencyInjection\" nuget library as shown below\n\n            services.ScanAndRegisterAggregates();\n\n            // Or if you prefer to register the manually\n\n            // Register the repository\n            services.AddScoped\u003cIRepository\u003cSchedule, Guid, Guid\u003e, Repository\u003cSchedule, ScheduleSnapshot, Guid, Guid, Guid\u003e\u003e();\n            // If you prefer to work without snapshots and use events only repository\n            // services.AddScoped\u003cIRepository\u003cSchedule, Guid, Guid\u003e, EventOnlyRepository\u003cSchedule\u003e\u003e();\n\n            // register the session implementation for the Aggregate\n            services.AddScoped\u003cISession\u003cSchedule\u003e, Session\u003cSchedule\u003e\u003e();\n            // or if prefer to you use the more detailed interface\n            // services.AddScoped\u003cISession\u003cSchedule, Guid, Guid\u003e, Session\u003cSchedule\u003e\u003e();\n\n        // \u003e\u003e Step 3: Register the other required dependencies\n\n            // Use the defaults\n            services.AddSingleton\u003cIClock, DefaultSystemClock\u003e();\n            services.AddSingleton\u003cIEventPublisher, DefaultNoOpEventPublisher\u003e();\n\n            // Or\n            // use your own implementation\n            // services.AddSingleton\u003cIClock, MyClock\u003e();\n            // services.AddSingleton\u003cIEventPublisher, MyEventPublisher\u003e();\n\n        var container = services.BuildServiceProvider();\n        return container;\n    }\n```\n\nIf you want to use it with a different dependency injection framework, you can look at how the assembly scanning and registration is implemented for `Microsoft.Extensions.DependencyInjection` as an example and come up with your own implementation. The file is [located here](https://github.com/dasiths/NEventLite/blob/master/src/Extensions/NEventLite.Extensions.Microsoft.DependencyInjection/Extensions.cs).\n\n```csharp\n    public static void ScanAndRegisterAggregates(this ServiceCollection services, IList\u003cAssembly\u003e assemblies)\n    {\n        foreach (var a in assemblies.GetAllAggregates()) // Use the built in GetAllAggregates() extensions method to find aggregate information\n        {\n            services.RegisterAggregate(a);\n        }\n    }\n\n    public static void RegisterAggregate(this ServiceCollection services, AggregateInformation a)\n    {\n        // Register full generic types\n        services.AddScoped(typeof(IRepository\u003c,,\u003e).MakeGenericType(a.Aggregate, a.AggregateKey, a.EventKey),\n            a.Snapshot != null\n                ? typeof(Repository\u003c,,,,\u003e).MakeGenericType(a.Aggregate, a.Snapshot, a.AggregateKey,\n                    a.EventKey, a.SnapshotKey)\n                : typeof(EventOnlyRepository\u003c,,\u003e).MakeGenericType(a.Aggregate, a.AggregateKey, a.EventKey));\n\n        services.AddScoped(typeof(ISession\u003c,,\u003e).MakeGenericType(a.Aggregate, a.AggregateKey, a.EventKey),\n            typeof(Session\u003c,,\u003e).MakeGenericType(a.Aggregate, a.AggregateKey, a.EventKey));\n\n        // Register the convenience GUID scoped ISession interface as well\n        if (a.AggregateKey == typeof(Guid) \u0026\u0026 a.EventKey == typeof(Guid))\n        {\n            services.AddScoped(typeof(ISession\u003c\u003e).MakeGenericType(a.Aggregate),\n                typeof(Session\u003c\u003e).MakeGenericType(a.Aggregate));\n        }\n    }\n```\n\n## :ledger: Storage providers\n\nThe library contains storage provider implementation for **[EventStore](https://eventstore.com/)** and we plan to include a few more in the future. We have also included an in memory event and snapshot storage provider to get you up and running faster.\n\nIt's very easy to implement your own as well. Implement `IEventStorageProvider` and `ISnapshotStorageProvider` interfaces shown below and register then in your DI. If you need help look at the `NEventLite.StorageProviders.EventStore` project in the repository.\n\n```csharp\n    // Interface for persisting and reading events\n    public interface IEventStorageProvider\u003cTEventKey\u003e\n    {\n        Task\u003cIEnumerable\u003cIEvent\u003cAggregateRoot\u003cTAggregateKey, TEventKey\u003e, TAggregateKey, TEventKey\u003e\u003e\u003e GetEventsAsync\u003cTAggregate, TAggregateKey\u003e(TAggregateKey aggregateId, long start, long count)\n            where TAggregate : AggregateRoot\u003cTAggregateKey, TEventKey\u003e;\n\n        Task\u003cIEvent\u003cAggregateRoot\u003cTAggregateKey, TEventKey\u003e, TAggregateKey, TEventKey\u003e\u003e GetLastEventAsync\u003cTAggregate, TAggregateKey\u003e(TAggregateKey aggregateId)\n            where TAggregate : AggregateRoot\u003cTAggregateKey, TEventKey\u003e;\n\n        Task SaveAsync\u003cTAggregate, TAggregateKey\u003e(TAggregateKey aggregateId, IEnumerable\u003cIEvent\u003cAggregateRoot\u003cTAggregateKey, TEventKey\u003e, TAggregateKey, TEventKey\u003e\u003e events)\n            where TAggregate: AggregateRoot\u003cTAggregateKey, TEventKey\u003e;\n    }\n\n    // Interface for persisting and reading snapshots\n    public interface ISnapshotStorageProvider\u003cin TSnapshotKey\u003e\n    {\n        int SnapshotFrequency { get; }\n\n        Task\u003cTSnapshot\u003e GetSnapshotAsync\u003cTSnapshot, TAggregateKey\u003e(TAggregateKey aggregateId)\n            where TSnapshot : ISnapshot\u003cTAggregateKey, TSnapshotKey\u003e;\n\n        Task SaveSnapshotAsync\u003cTSnapshot, TAggregateKey\u003e(TSnapshot snapshot)\n            where TSnapshot : ISnapshot\u003cTAggregateKey, TSnapshotKey\u003e;\n    }\n\n    // Implement the interfaces\n    // Then register them in your DI\n    services.AddScoped\u003cIEventStorageProvider\u003cYourKeyType\u003e, MyEventStorageProvider\u003e();\n    services.AddScoped\u003cISnapshotStorageProvider\u003cYourKeyType\u003e, MySnapshotStorageProvider\u003e();\n```\n\n## Examples\n\nThere are more examples in the [Samples folder](https://github.com/dasiths/NEventLite/tree/master/src/Samples) if you need help figuring out how to put everything together.\n\n## Notes\n\nPlease feel free to contribute and improve the code as you see fit. Please raise an issue if you find a bug or have an improvement idea. The repository is shared under the MIT license.\n\n**Share the :heart: and let your friends and colleagues know about this cool project. Thank you.**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdasiths%2Fneventlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdasiths%2Fneventlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdasiths%2Fneventlite/lists"}