Skip to content

A lightweight project that prepares grounds for abstractions represented in EventStorming and EventModeling

License

Notifications You must be signed in to change notification settings

modelingevolution/plumberd

Repository files navigation

INTRODUCTION

User Voice: Vote for features here

Plumberd is a lightweight library to make working with Event driven apps easy. It unifies abstractions and thus combines Event Drive Architecture (messageing) and Event Sourcing (persistance) together. It's makes it super easy for everyone familiar with ASP Controllers or WCF to jump into CQRS and EventSourcing. All the plumbing is automated. Current version is aimed to support your Handlers (CommandHandlers, EventHandlers) to work on server-side or client-side without changing the logic. This becomes important especially because Blazor and WebAssembly is getting more traction. Supported plumbing:

  • EventStore [Done]
  • gRPC Web [Done]

GETTING STARTED

Prepare your environment - run EventStore. You can run it in docker (or however you like):

docker run --name esdb-node -it -p 2113:2113 -p 1113:1113 eventstore/eventstore:latest --insecure --run-projections=All

You can find more on EventStore from documentation there.

Install nugets

dotnet add package Plumberd
dotnet add package Plumberd.EventStore

Write your command-handlers, projections, processors and other handlers:

public class MyCreazyCommandHandler 
{
	// Command Handler Method Signature
	public async Task When(Guid id, MyCreazyCommand cmd) 
	{
		// do some stuff
	}
}

[StreamName("Creazy")]  					// Category - optional.
public class MyCreazyCommand : Command { }  // you can implement ICommand instead.

and add bit of configuration:

var p = new PlumberBuilder()
	.WithDefaultEventStore(x => x.InSecure())
	.Build()
	.RegisterController(new MyCreazyCommandHandler());  // or RegisterController<MyCreazyCommandHandler>();
await p.StartAsync();

Now it would make sense to send a command:

await p.DefaultCommandInvoker.Execute(Guid.NewGuid(), new MyCreazyCommand());

DONE You ready to run the app. F5! This will save 'MyCreazyCommand' to a stream named Creazy-{id}. Persistent projection will be created under-the-hood and command-handler will be invoked. You can modify When method to return events. Then those will be saved to the stream. This can remove dependensies on command-handlers.

Handlers or Controllers

CommandHandlers, EventHandlers are just processing units. They contain methods that correspond to certain signatures. You may have pure command-handers and event-handlers. Or you can flex your design and have controllers with same methods. It's all up to you. Plumberd supports out of the box quite a few signatures. Let's see a more complex example:

public class HappyController
{
	public async IEnumerable<IEvent> When(Guid id, MyCreazyCommand cmd) 
	{
		yield return MyCreazyEvent();
	}
}

[MyProjectionConfiguration]
public class MyProjection
{
	public async Task Given(IMetadata m, MyCreazyEvent e) { }
	public async MyDerivedEvent Given(IMetadata m, MyCreazyEvent e) { }
	/* ... */
}

[MyProcessorConfiguration]
public class MyProcessor
{
	public async IAsyncEnumerable<ICommand> When(IMetadata m, MyCreazyEvent2 e) { }
	public async Task<(Guid, MyCreazyCommand3)> When(IMetadata m, MyCreazyEvent2 e) { }
	/* ... */
}

Configuration can be done in attributes or in Fluent API:

public class MyProcessorAttribute : ProcessingUnitConfigAttribute
{
    public MyProcessorAttribute()
    {
        SubscribesFromBeginning = false;	// Subscribe to stream from beginning or from now.
        IsEventEmitEnabled = true;    // Methods can return events or (Guid,TEvent)
        IsCommandEmitEnabled = true;  // Methods can return commands or (Guid, TCommand)
        IsPersistent = true;		  // Cursor is saved in EventStore are persistant subscription
        BindingFlags = BindingFlags.ProcessEvents |		// A flag that is used to narrow the scope of binding in 'Controllers'
                                  BindingFlags.ReturnEvents |
                                  BindingFlags.ReturnCommands;
        }
    }
}

Dependencies

Using the library requires using 3 interfaces:

  • ICommand (all your commands need to implement it)
  • IEvent (all your events need to implement it)
  • IMatadata (most likely you won't derive from it.)

The interface is very simple requireing you only to have Id property of type Guid.

Projections

Processing events in good-enough order is very important. That's why out of the box Plumberd is:

  • Creating projection for every Controller (except one method ones).
  • The projection emits a new stream that links to orignal events.
  • This way you can track what events where processed by projection.
  • CorrelationId and CausationId are appended automatically.

Metadata

Metadata in EventStore is like a headers in WebAPI. Plumberd makes it easy to write your own Enricher, that can append new properties to metadata.

ROADMAP

Current version is Alfa.

pre-Alfa In this version we are experimenting with new features as fast as possible. It's well known that eventsourcing libraries are to complex and do not fit every case. We want to check if we can came up with fix number of features that satisfy many usecases. To make it happen, we need to iterate fast and experiment with the set of features. From QA perspective this means that few unit-tests are there, however you will find integration-tests and api-tests (if we can achive something though expressing it using lib.).

Alfa In this version we might introduce new breaking features. The library might be unstable and is not suitable to production environment - unless you know what you do ;) Alfa testing will spread testing upon interested people in closed team.

Beta In this version we'll be encouraging everybody to test the library and give feedback. Some breaking changes might be still introduces.

Release candidate will be published as soon as we are satisfied with the scope of features. In this stage will focus on performance.

Release 1.0 yet we don't know when :('.

NEXT VERSION RELEASE:

Abstractions over processing units seems to workout. Next version will:

  • Provide migration tools for rolling up next eventstore
  • Make LiveQueryExecutor simpler
  • Provide FluentConfiguration as an alternative to attributes.
  • Get rid of ICommand, IEvent and IMetadata interfaces. We will build binders and thus all your hander logic will be fully independent on Plumberd
  • Rewrite all the spagetti code that is there in PlumberdRuntime :)
  • Support for snapshots
  • Better support for session

If anyone would like to take part in design or help with the library please DM on Twitter/Li or through e-mail: rafal.maciag [at] modelingevolution.com

WEB GRPC & Error handling

  • Each client creates a session-id - which identified the client's client. One running client == one session id.
  • When invoking a command, command handler might throw exception. This exceptions need to be propagated to the app and dispatched.
  • CommandInvoker can "chain" exception-handler, that will be invoked when exception is propagated back to the client. It will use 'correlation-id' to dispatch right handler.

-- Under the hood:

  • When invoking a command 'session-id' will be appended to metadata.
  • When invoking a command handler, when exception is rised and Exception event will be emited in 'command-stream', that starts typically with '>'.
  • A projection with copy 'error-exceptions' responses to 'session-stream'.
  • Each grpc client can subscribe to this stream and thus receive exceptions.

Usage

  1. Register approparite error to be copied to session-stream using SessionProjection
  2. Index Exception in TypeRegister: register.Index(typeof(EventException<CommandType, ErrorType>));
  3. In command handler raise new ProcessingException
  4. In your .razor file invoke the command using ICommandManager.Execute - with appropriate overload.

About

A lightweight project that prepares grounds for abstractions represented in EventStorming and EventModeling

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages