MicroPlumberd.SourceGenerators 1.0.5.18

There is a newer version of this package available.
See the version list below for details.
dotnet add package MicroPlumberd.SourceGenerators --version 1.0.5.18                
NuGet\Install-Package MicroPlumberd.SourceGenerators -Version 1.0.5.18                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MicroPlumberd.SourceGenerators" Version="1.0.5.18" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MicroPlumberd.SourceGenerators --version 1.0.5.18                
#r "nuget: MicroPlumberd.SourceGenerators, 1.0.5.18"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install MicroPlumberd.SourceGenerators as a Cake Addin
#addin nuget:?package=MicroPlumberd.SourceGenerators&version=1.0.5.18

// Install MicroPlumberd.SourceGenerators as a Cake Tool
#tool nuget:?package=MicroPlumberd.SourceGenerators&version=1.0.5.18                

micro-plumberd

Micro library for EventStore, CQRS and EventSourcing Just eXtreamly simple.

Getting started

Install nugets:

dotnet add package MicroPlumberd
dotnet add package MicroPlumberd.SourceGeneratiors

If you'd like to use direct dotnet-dotnet communication to execute command-handlers install MicroPlumberd.DirectConnect

dotnet add package MicroPlumberd.DirectConnect

Configure plumber

/// change to your connection-string.
string connectionString = $"esdb://admin:changeit@localhost:2113?tls=false&tlsVerifyCert=false";
var seetings = EventStoreClientSettings.Create(connectionString);

var plumber = new Plumber(settings) as IPlumber;

Aggregates

  1. Write an aggregate.
[Aggregate]
public partial class FooAggregate(Guid id) : AggregateBase<FooAggregate.FooState>(id)
{
    internal new FooState State => base.State;
    public record FooState { public string Name { get; set; } };
    private static FooState Given(FooState state, FooCreated ev) => state with { Name = ev.Name };
    private static FooState Given(FooState state, FooUpdated ev) => state with { Name =ev.Name };
    public void Open(string msg) => AppendPendingChange(new FooCreated() { Name = msg });
    public void Change(string msg) => AppendPendingChange(new FooUpdated() { Name = msg });
}

Comments:

  • State is encapsulated in nested class FooState.
  • Given methods, that are used when loading aggregate from the EventStoreDB are private and static. State is encouraged to be immutable.
  • [Aggregate] attribute is used by SourceGenerator that will generate dispatching code and handy metadata.
  1. Consume an aggregate.

If you want to create a new aggregate and save it to EventStoreDB:


AppSrc.FooAggregate aggregate = AppSrc.FooAggregate.New(Guid.NewGuid());
aggregate.Open("Hello");

await plumber.SaveNew(aggregate);

If you want to load aggregate from EventStoreDB, change it and save back to EventStoreDB

var aggregate = await plumber.Get<FooAggregate>("YOUR_ID");
aggregate.Change("World");
await plumber.SaveChanges(aggregate);

Write a read-model/processor

  1. Read-Models
[EventHandler]
public partial class FooModel
{
    private async Task Given(Metadata m, FooCreated ev)
    {
        // your code
    }
    private async Task Given(Metadata m, FooUpdated ev)
    {
         // your code
    }
}

Comments:

  • ReadModels have private async Given methods. Since they are async, you can invoke SQL here, or othere APIs to store your model.
  • Metadata contains standard stuff (Created, CorrelationId, CausationId), but can be reconfigured.
var fooModel = new FooModel();
var sub= await plumber.SubscribeModel(fooModel);

// or if you want to persist progress of your subscription
var sub2= await plumber.SubscribeModelPersistently(fooModel);

With SubscribeModel you can subscribe from start, from certain moment or from the end of the stream.

  1. Processors
[EventHandler]
public partial class FooProcessor(IPlumber plumber)
{
    private async Task Given(Metadata m, FooUpdated ev)
    {
        var agg = FooAggregate.New(Guid.NewGuid());
        agg.Open(ev.Name + " new");
        await plumber.SaveNew(agg);
    }
}

Implementing a processor is technically the same as implementing a read-model, but inside the Given method you would typically invoke a command or execute an aggregate.

Features

Conventions

  • SteamNameConvention - from aggregate type, and aggregate id
  • EventNameConvention - from aggregate? instance and event instance
  • MetadataConvention - to enrich event with metadata based on aggregate instance and event instance
  • EventIdConvention - from aggregate instance and event instance

Ultra development cycle for Read-Models (EF example).

Imagine this:

  1. You create a read-model that subscribes persistently.
  2. You subscribe it with plumber.
  3. You changed something in the event and want to see the new model.
  4. Instead of re-creating old read-model, you can easily create new one. Just change MODEL_VER to reflect new version.

Please note that Sql schema create/drop auto-generation script will be covered in a different article. (For now we leave it for developers.)

Comments:

  • By creating a new read-model you can always compare the differences with the previous one.
  • You can leverage canary-deployment strategy and have 2 versions of your system running in parallel.
[OutputStream(FooModel.MODEL_NAME)]
[EventHandler]
public partial class FooModel : DbContext
{
    internal const string MODEL_VER = "_v1";
    internal const string MODEL_NAME = $"FooModel{MODEL_VER}";
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder
           .Entity<FooEntity>()
           .ToTable($"FooEntities{MODEL_VER}");
    }
    private async Task Given(Metadata m, FooCreated ev)
    {
        // your code
    }
    private async Task Given(Metadata m, FooUpdated ev)
    {
        // your code
    }
}

Subscription Sets - Models ultra-composition

  • You can easily create a stream that joins events together by event-type.

GRPC Direct communication

/// Let's configure server:
services.AddCommandHandler<FooCommandHandler>().AddServerDirectConnect();

/// Add mapping to direct-connect service
app.MapDirectConnect();

Here is an example of a command handler code:

[CommandHandler]
public partial class FooCommandHandler(IPlumber plumber)
{

    [ThrowsFaultException<BusinessFault>]
    public async Task Handle(Guid id, CreateFoo cmd)
    {
        if (cmd.Name == "error")
            throw new BusinessFaultException("Foo");

        var agg = FooAggregate.New(id);
        agg.Open(cmd.Name);

        await plumber.SaveNew(agg);
    }

    [ThrowsFaultException<BusinessFault>]
    public async Task<HandlerOperationStatus> Handle(Guid id, ChangeFoo cmd)
    {
        if (cmd.Name == "error")
            throw new BusinessFaultException("Foo");

        var agg = await plumber.Get<FooAggregate>(id);
        agg.Change(cmd.Name);

        await plumber.SaveChanges(agg);
        return HandlerOperationStatus.Ok();
    }
}

And how on the client side:

service.AddClientDirectConnect().AddCommandInvokers();

// And invocation
 var clientPool = sp.GetRequiredService<IRequestInvokerPool>();
 var invoker = clientPool.Get("YOUR_GRPC_URL");
 await invoker.Execute(Guid.NewId(), new CreateFoo(){});

Aspects

You can easily inject aspects through decorator pattern.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on MicroPlumberd.SourceGenerators:

Package Downloads
EventPi.Services.Camera

EventPi services for camera configuration on Raspberry Pi (rpicamera).

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.77.125 60 11/26/2024
1.0.76.125 94 10/12/2024
1.0.75.125 109 10/12/2024
1.0.74.125 84 10/12/2024
1.0.73.124 84 10/12/2024
1.0.72.122 167 6/9/2024
1.0.71.121 95 6/7/2024
1.0.70.121 105 6/6/2024
1.0.69.119 116 5/15/2024
1.0.68.118 100 5/15/2024
1.0.67.118 99 5/15/2024
1.0.66.118 92 5/15/2024
1.0.65.117 108 5/15/2024
1.0.64.116 97 5/14/2024
1.0.63.115 111 5/11/2024
1.0.62.114 104 5/11/2024
1.0.61.113 88 5/11/2024
1.0.60.112 122 5/8/2024
1.0.58.112 108 5/8/2024
1.0.57.111 180 4/26/2024
1.0.55.111 122 4/23/2024
1.0.54.110 111 4/23/2024
1.0.53.110 105 4/23/2024
1.0.51.109 99 4/22/2024
1.0.50.109 103 4/22/2024
1.0.49.108 114 4/22/2024
1.0.48.108 109 4/20/2024
1.0.46.107 100 4/20/2024
1.0.45.106 110 4/20/2024
1.0.44.106 108 4/20/2024
1.0.43.100 116 4/17/2024
1.0.41.97 172 4/11/2024
1.0.40.95 172 4/11/2024
1.0.39.94 177 4/11/2024
1.0.37.94 155 4/9/2024
1.0.36.93 171 4/9/2024
1.0.35.90 187 4/8/2024
1.0.34.87 183 4/8/2024
1.0.29.85 175 4/7/2024
1.0.26.83 179 4/7/2024
1.0.20.72 176 3/24/2024
1.0.19.71 139 3/24/2024
1.0.17.71 161 3/24/2024
1.0.16.71 146 3/23/2024
1.0.15.70 174 3/23/2024
1.0.14.57 174 3/21/2024
1.0.13.56 166 3/21/2024
1.0.11.54 159 3/21/2024
1.0.10.40 192 3/17/2024
1.0.9.40 208 3/17/2024
1.0.8.32 195 3/13/2024
1.0.5.18 124 3/12/2024
1.0.0 135 3/10/2024
0.0.4.18 124 3/12/2024