Giter Club home page Giter Club logo

cirqus's Introduction

d60 Cirqus

Simple but powerful event sourcing + CQRS kit.

Provides a model for creating commands, aggregate roots, events, and views, tying it all up in one simple and neat CommandProcessor.

How simple?

You do this:

var processor = CommandProcessor.With()
    .(...)
    .Create();

and let the API guide you through selecting an event store, possibly configuring some more stuff as well, and then you can do this:

processor.ProcessCommand(myCommand);

and then everything will flow from there. The command processor is reentrant and is meant to be kept around for the duration of your application's lifetime. Remember to

processor.Dispose();

when the application shuts down.

More docs

Check out the official documentation wiki.

Is Cirqus for you?

It depends ;) to answer that question, it is necessary to think a little bit about your requirements. Cirqus is not in the extreme high performance camp (if there is such a camp), although it doesn't mean that Cirqus cannot perform extremely well - it's more that on the following scale:

Raw performance                  Polished APIs and general usefulness
================================================(+)==================

Cirqus is positioned around the (+) - it doesn't mean that you cannot achieve great - excellent even - performance with Cirqus, it just means that certain choices have probably been made for your with Cirqus that makes certain things easier, even though it may sacrifice a little bit of raw performance.

And on this scale:

Complex event processing        Domain-model-centric event processing
================================================(+)==================

Cirqus is again positioned around the (+) - it is mostly meant to be used in conjunction with an event-driven domain model, so if you're content with driving everything directly off of events, you will probably not benefit as much from using Cirqus.

Again, it's not that Cirqus doesn't do complex event processing - Cirqus' views can easily be brought to do that - it's just that that is not really the focus (at least not with the existing implementations).

Lastly, on this scale:

.NET-centric                                            Interoperable
=================(+)=================================================

it shows that many things are just made extremely easy for you if you're using Cirqus and .NET. You can of course easily set up something else to read events since all the existing event stores are simply using JSON to represent their contents, but from time to time a .NET type name might show up in the JSON data - which means that it's easier to use from .NET, but not impossible to use from something else (e.g. to have node.js-driven projections or whatever).

Configuration example

This is how you can set up a fully functioning command processor, including a view:

// configure one single view manager
var viewManager = new MsSqlViewManager<CounterView>("sqltestdb");

// let's create & initialize the command processor
var processor = CommandProcessor.With()
    .EventStore(e => e.UseSqlServer("sqltestdb", "Events"))
    .EventDispatcher(e => e.UseViewManagerEventDispatcher(viewManager))
    .Create();

// use the command processor, possibly from multiple threads,
// for the entire lifetime of your application....

// and then, when your application shuts down:
processor.Dispose();

Elaborate configuration example

If you're interested in seeing which moving parts are involved in the command processor, here's the equivalent configuration where all the things are wired together manually. As you can see, it's actually fairly simple (although the configuration API is much more intuitive and concise).

// this is the origin of truth - let's keep it in SQL Server!
var eventStore = new MsSqlEventStore("sqltestdb", "Events", 
                                     automaticallyCreateSchema: true);

// aggregate roots are simply built when needed by replaying events
// for the requested root
var repository = new DefaultAggregateRootRepository(eventStore);

// configure one single view manager in another table in our SQL Server
var viewManager = new MsSqlViewManager<CounterView>("sqltestdb", "CounterView", 
                                                    automaticallyCreateSchema: true);

// Cirqus will deliver emitted events to the event dispatcher when they have
//  been persisted
var eventDispatcher = new ViewManagerEventDispatcher(repository, eventStore, viewManager);

// we can create the processor now
var processor = new CommandProcessor(eventStore, repository, eventDispatcher);

// and then, when your application shuts down:
processor.Dispose();

Code example

This is an example of a command whose purpose it is to instruct the Counter aggregate root to increment itself by some specific value, as indicated by the given delta parameter:

public class IncrementCounter : Command<Counter>
{
    public IncrementCounter(string aggregateRootId, int delta)
        : base(aggregateRootId)
    {
        Delta = delta;
    }

    public int Delta { get; private set; }

    public override void Execute(Counter aggregateRoot)
    {
        aggregateRoot.Increment(Delta);
    }
}

Note how the command indicates the type and ID of the aggregate root to address, as well as an Execute method that will be invoked by the framework. Let's take a look at Counter - aggregate roots must be based on the AggregateRoot base class and must of course follow the emit/apply pattern for mutating themselves - it looks like this:

public class Counter : AggregateRoot, IEmit<CounterIncremented>
{
    int _currentValue;

    public void Increment(int delta)
    {
        Emit(new CounterIncremented(delta));
    }

    public void Apply(CounterIncremented e)
    {
        _currentValue += e.Delta;
    }

    public int CurrentValue
    {
        get { return _currentValue; }
    }

    public double GetSecretBizValue()
    {
        return CurrentValue%2 == 0
            ? Math.PI
            : CurrentValue;
    }
}

As you can see, the command's Execute method will invoke the Increment(delta) method on the root, which in turn will emit a CounterIncremented event, which simply looks like this:

public class CounterIncremented : DomainEvent<Counter>
{
    public CounterIncremented(int delta)
    {
        Delta = delta;
    }

    public int Delta { get; private set; }
}

The event is immediately applied (via the root's Apply method that comes from implementing IEmit<CounterIncremented>), and this is the place where the root is free to mutate itself - in this case, we increment the private _currentValue variable, which serves to demonstrate that aggregate roots are free to keep their privates private.

Note also how the aggregate root is capable of calculating a secret business value, which happens to alternate between the counter's value and π, depending on whether the counter's value is odd or even.

Lastly, we have set up a MsSqlViewManager that operates on a CounterView that looks like this:

public class CounterView : IViewInstance<InstancePerAggregateRootLocator>,
    ISubscribeTo<CounterIncremented>
{
    public CounterView()
    {
        SomeRecentBizValues = new List<double>();
    }

    public string Id { get; set; }

    public long LastGlobalSequenceNumber { get; set; }

    public int CurrentValue { get; set; }

    public double SecretBizValue { get; set; }

    public List<double> SomeRecentBizValues { get; set; }

    public void Handle(IViewContext context, CounterIncremented domainEvent)
    {
        CurrentValue += domainEvent.Delta;

        var id = domainEvent.GetAggregateRootId();
        var version = domainEvent.GetGlobalSequenceNumber();
        var counter = context.Load<Counter>(id, version);

        SecretBizValue = counter.GetSecretBizValue();

        SomeRecentBizValues.Add(SecretBizValue);

        // trim to 10 most recent biz values
        while(SomeRecentBizValues.Count > 10) 
            SomeRecentBizValues.RemoveAt(0);
    }
}

Views can define how processed events are mapped to view IDs via the ViewLocator implementation that closes the IViewInstance<> interface - in this case, we're using InstancePerAggregateRootLocator which means that the aggregate root ID of the processed event is simply used as the ID of the view, in turn resulting in one instance of the view per aggregate root.

In order to actually get to receive events, the view class must implement one or more ISubscribeTo<> interfaces - in this case, we subscribe to CounterIncremented which requires that we implement the Handle method.

In addition to the two required properties, Id and LastGLobalSequenceNumber, we've added a property for the current value of the counter (CurrentValue), a property for the secret business value (SecretBizValue), and a list that can contain the 10 most recent business values (SomeRecentBizValues).

Note how the IViewContext gives access to a Load method that can be used by the view to load aggregate roots if it needs to invoke domain logic to extract certain values out of the domain (like e.g. our secret business value).

Note also how an aggregate root must be loaded by specifying a global sequence number which will serve as a "roof" for applied event, thus ensuring that the loaded aggregate root has the version that corresponds to the time when the event was emitted, thus allowing for eternally consistent replay of events. It also allows for peeking back and forth in time, but that's a story for another time... ;)

cirqus's People

Contributors

asgerhallas avatar caspertdk avatar dampee avatar enriquein avatar kimbirkelund avatar mhertis avatar mookid8000 avatar pvivera avatar sdebruyn avatar ssboisen avatar tompazourek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cirqus's Issues

Performance measurement hooks for command processing

It would be neat if a few events were in place that would allow for measuring time spent doing various things.... or maybe it could simply be implemented as decorators?

For each processed command, it would be nice to be able to track the entire command itself along with the time spent

  • hydrating aggregate roots (by aggregate root type)
  • executing actual domain logic (by aggregate root type)
  • committing events to the event store
  • dispatching events

which could aid in diagnosing (and predicting!) the performance characteristics of the application.

Serialization customization

Hi,

I am trying to use Cirqus to develop a system that makes use of event sourcing.

Right now I am using the Sql Server event store and I am in the need to customize how events are serialized.
I can see you use (and merge) Json.Net and that the DomainEventSerializer is used to serialize events and that type exposes a JsonSerializerSettings.
Unfortunately the creation of isntances of that type occurs inside the event store itself and I do not have access to it, in order to add my own converters and such.

Any ideas on how to accomplish this?

Thanks in advance

Aggregate string keys

Would it make sense to change AggregatesRootId's to string, so that we can use "natural" keys for uniqueness purposes?

There could still be the interfaces for using guids as today, but behind the scenes, keys could be persisted as strings.

FoundationDB event store

would be neat ;)

(I have no idea how FoundationDB would actually be used to implement an event store - would it be plain key-value, use a document structure, or the new SQL layer? ?????)

Letting go of the type parameter on TestContext.Save()

I'd like to be able to do something like this,

    public void Given(params IEnumerable<DomainEvent>[] events)
    {
        foreach (var domainEvent in events.SelectMany(x => x))
        {
            context.Save((dynamic)domainEvent);
        }
    }

but without dynamic.

Would it be an idea to relax the Save method signature, or have non-generic overloads, that enables that? And also enables - the thing you talked about - being able to emit events that do not originate from an aggregate (albeit not the actual feature, just removing hindrance)

ElasticSearch view manager

Would be neat!

I'm imagining a view manager that is configured to use a specific index and type... or would it make sense to vary the type within one view manager?

It would be cool if the view manager would somehow make it easy to configure the maps for any given index/type.

Ways of loading aggregate roots

  • ICommandContext:
    • Create<T>
    • Load<T>
    • TryLoad<T>
  • AggregateRoot:
    • Create<T>
    • Load<T>
    • TryLoad<T>
  • IViewContext:
    • Load<T>
    • TryLoad<T>

where Create should create a new instance and throw if one already exists with that ID, Load should load and throw if the instance does not exist, and TryLoad should load and return null if the instance does not exist.

This way, Load corresponds to the current Load with createIfNotExists = false (default), and createIfNotExists = true can be had by going

var instance = context.TryLoad<SomeRoot>(id) ?? context.Create<SomeRoot>(id);

Handling dependencies during command processing

Any ideas on how to handle dependencies during processing of a command. Say you need access to some external data for validation.

You could load another aggregate if the data was available there, I think the Load<> feature is a great compromise/strength :)

But it might be data stored in views or something completely different, maybe an entirely different system.

Would love your input.

Transfer metadata to events before emit

Metadata from commands are transferred to events and can then be used in handlers on views. But they cannot be used in apply methods on roots, as they have not been transferred yet, at the time. Would there be any problems in moving the transfer of metadata into the aggregate root before the applies are called?

Configuration API

When the command processor is created, it is easy to forget to call Initialize - this is of course because we're missing a real configuration API that will auto-complete-lead you to Do The Right Thing when invoking the configuration spells.

E.g.

var database = new MongoClient("mongodb://some-server")
    .GetServer().GetDatabase("some-db");
var eventStore = new MongoDbEventStore(database, "Events");
var aggregateRootRepository = new DefaultAggregateRootRepository(eventStore);
var eventDispatcher = new ViewManagerEventDispatcher(aggregateRootRepository);
var commandProcessor = new CommandProcessor(eventStore, aggregateRootRepository, eventDispatcher) {
    Options = {
        LoggerFactory = new ConsoleLoggerFactory(minLevel: Level.Debug),
        PurgeExistingViews = true
    }
}

could be replaced by

var commandProcessor = CommandProcessor.With()
    .Logging(l => l.UseConsole(minLevel: Level.Debug))
    .EventStore(e => e.UseMongoDb("mongodb://some-server/some-db", "Events"))
    .AggregateRootRepository(r => r.UseDefault())
    .EventDispatcher(e => e.UseViewManagerEventDispatcher())
    .Options(o => o.PurgeExistingViews(true))
    .Create();

which would make it impossible to end up with e.g. an uninitialized command processor.

The configuration API should build upon extension methods, allowing difference integration packages to bring their own APIs into the mix. Moreover, it will be fab if there's a concept of a registered decorator which will make for a nice way of configuring e.g. a caching snapshot-enabled aggregate root repository decorator.

NLog logger

Add integration library package for NLog integration

View manager for PostgreSql

Could be similar to what MongoDBViewManager is capable of by using the JSONB data type for the view.

Should provide a mechanism to easily create indexes into the JSON-serialized view instances, and it would be absolutely awesome if some basic LINQ spells were supported as well.

Logger in ViewManagerEventDispatcher is not changed

I am building a project using dependency injection with Autofac and CQRS using Cirqus.

I built a DebugLoggerFactory and a DebugLogger which are exact copies of the ConsoleLogger(Factory), but they log to Debug instead of to Console (my web project doesn't have a console).
If you're interested in this, I'll submit a pull request with the debug logger.

When I create a new aggregate root, my view is not updated. To further investigate this issue, I wanted to log everything that happens in the ViewManagerEventDispatcher. The problem is that the ViewManagerEventDispatcher still uses the NullLogger instead of the DebugLogger that is set in the CommandProcessor.
This is what I noticed while debugging and looking at the static non-public members of the ViewManagerEventDispatcher in my ViewManager...

This is how I initialize everything with Autofac:

            builder.RegisterInstance(new EntityFrameworkViewManager<SellerView>(_connectionStringName, false))
                    .AsSelf()
                    .As<IViewManager>();
            // add all views here (or find a better solution that automagically registers all views in our Views namespace ;) ), use .PreserveExistingDefaults() on every view except the first one

            builder.RegisterType<MsSqlEventStore>().WithParameters(new List<Parameter>
            {
                    new PositionalParameter(0, _connectionStringName),
                    new PositionalParameter(1, _eventsTableName)
            }).As<IEventStore>().SingleInstance();

            builder.RegisterType<DefaultDomainTypeNameMapper>().As<IDomainTypeNameMapper>();
            builder.RegisterType<JsonDomainEventSerializer>().As<IDomainEventSerializer>();
            builder.RegisterType<DefaultCommandMapper>().As<ICommandMapper>();
            builder.RegisterType<RealUnitOfWork>().As<IUnitOfWork>().InstancePerDependency();

            builder.RegisterType<DebugLoggerFactory>().As<CirqusLoggerFactory>();
            builder.RegisterType<Options>().PropertiesAutowired().AsSelf();

            builder.RegisterType<DefaultAggregateRootRepository>().As<IAggregateRootRepository>().SingleInstance();
            builder.RegisterType<ViewManagerEventDispatcher>().As<IEventDispatcher>().SingleInstance();
            builder.RegisterType<CommandProcessor>().As<ICommandProcessor>().SingleInstance();

The code above comes from a module that is being registered in the web API project that uses the class library which contains my CQRS code.

View / Projections

i have not fully dove into this yet but is their any examples out there that show more in-depth usage of the view manager / projections? does the framework handle idempotent such that the view will only receive an event once?

Default event dispatcher

should be a DevNullEventDispatcher or something... no need to require an event dispatcher to get started persisting some events

FoundationDB view manager

would be neato!

(I'm not a FoundationDB expert, but I guess it would be neat if the view manager could use some kind of document-oriented layer for the views, although I'm unsure whether there is an official document-oriented layer....)

Replace services in ConfigurationBuilder container

Currently _container.Register() will throw if you try to register a service, that already has a factory. That means I cannot call .Create() on a configuration twice, because it has the side effect that it registers default in the container. Now I would like to be able to append and manipulate the configuration when testing and I wonder if we could - without problems - change the implementation so a _container.Register will override existing services (if it's not set to decorate)?

Inheritance of AggregateRoots and Emit issue

Hello,

First of all, Cirqus is great and thank you for dev it! We use it a lot and recently came across an issue with inheritance. Is there any reason why aggregates should not emit events from aggregates extending them?

Example:

Base aggregate & event:
public class Device : AggregateRoot {}
public class DeviceChanged : DomainEvent

Two classes extending the base aggregate:
public class SpecialDeviceA : Device {}
public class SpecialDeviceB : Device {}

Although SpecialDeviceA is Device, if I create a SpecialDeviceA and call some method implemented by Device (base) emitting DeviceChanged event I get:
System.InvalidOperationException: Attempted to emit event “DeviceChanged” which is owned by “Device” from aggregate root of type “SpecialDeviceA”.

For now I fixed it with checking if Agg type is assignable from X (and only the exact type of X) in the d60.Cirqus.Aggregates.AggregateRoot.cs, line 81:

From: if (typeof(TAggregateRoot) != GetType())
To: if(!typeof(TAggregateRoot).IsAssignableFrom(GetType()))

Aggregate tests still pass :)

Is this ok? As we (and probably others too) use inheritance with Aggregates (nature of our domain), maybe Cirqus could enable this by default?

Kind regards,
Martin

Rollbacking event writes

Hi, we are using Cirqus as an event repository (in SQL Server) but our views are hand-crafted (handling the dispatched event via a custom IEventDispatcher) in RavenDb.

Problem comes when for some reason one of the handlers for the dispatched event fails. In that situation the event has been commited to the store, but the "views" are not in sync with what in the event store. In our case, that becomes a problem.

What would be the best way to approach this situation and "get rid of" the mismatch?
Note: enlisting the whole operation inside a distributed transaction (via TransactionScope) is not a possibility.

We are using 0.41.0 and we'd like to avoid updating if possible.

Issue with VS 2015 and Vnext

Not sure if its a bug in Cirqus or Vnext, but if I have the newest version of Cirqus in my Asp vnext project I get these errors

https://dl.dropboxusercontent.com/u/19153690/cirqus.png

If I then remove the nuget package I get more errors (Which makes sense)

https://dl.dropboxusercontent.com/u/19153690/cirqusremoved.png

But in the editor there is no error, only down in the error list, and why I get issues in the first screenshot is beyond me...

Anyone else seen something similar?

Link to project https://github.com/Mech0z/Foosball9000

Type argument constraint to AggregateRoot

I see that this has been changed a little while back:

 -        TAggregateRoot TryLoad<TAggregateRoot>(string aggregateRootId) where TAggregateRoot : AggregateRoot, new();

 +        TAggregateRoot TryLoad<TAggregateRoot>(string aggregateRootId) where TAggregateRoot : class;

Is there any reason not to constrain this to AggregateRoot as opposed to just class? This spreads out down to repositories, and I have some excessive casting in some of my decorators because of it.

Loading other aggregates from inside aggregate root

As we discussed, it might be an idea to remove the Load methods from inside aggregate roots. There were four arguments for it, as far as I remember, but I can only remember three of them :)

  1. It would make dependencies to other aggregate roots and coordination between them explicit in the application services where the scope of the transaction is also easily seen.
  2. If loading in the aggregate root seems neccessary (eg some action must - dependent on some business logic - result in an action on another aggregate root), one could inject a repository in the root's method, or a domain event handler could just as easily react to the emitted events, making action -> reaction clear.
  3. It would simplify testing of the domain models, as IUnitOfWork would become a simpler interface to implement. And IUnitOfWork would live up to its name.

Remove TOwner from DomainEvent<TOwner>

After @mhertis implemented the ability to emit events from subclasses of a root, the term owner is now ambiguous: if the event is emitted from a subclass the Owner metadata is actually the type of the emitter, not TOwner.

Thus now TOwner only serves one purpose: to prevent emitting events, that belong to another root hierarchy. And I'm not sure if that adds any value. The id is what the emitted event belongs to, and I could easily emit the same event from two different types of roots (or even from a command) without that posing a problem - we always use the id when retrieving it again and will never get events that does not belong to that id.

I think removing TOwner from DomainEvent<TOwner> will:

  1. Make it more obvious that TOwner is not always the same as Metadata.Owner.
  2. Remove some gotchas from the testing tools, by making it more explicit about who's emitting events.
  3. Make it ready for emitting events from commands.

And if one really needs the check for certain events only being emitted from a certain root (or it's subtypes) then that is easily implemented as a decorator on the UnitOfWork.

What do you think? :)

Possible bug in order of events in ViewManagerEventDispatcher

Hi guys,

In the ViewManagerEventDispatcher on line #112 you determine the max sequence number in the batch of events by using Max, I assume this is because no ordering of the events are assumed?

However in lin #221 in the CatchUpTo method you use events.Last() to get the max global sequence number. The code doesn't sort the events in any of the intermediate code.

So just for consistency sake, is the order of the events expected to be sorted by global sequence number or not?

ViewInstanceDeletion

There's a test named ViewInstanceDeletion which is currently skipped. Shouldn't we be able to delete view instances? Why is it skipped?

Merge JSON.NET into core DLL

Like Rebus, the core NuGet package should be absolutely dependency-less - but since we rely so much on JSON.NET to work its magic, we must merge it into c60.Cirqus.dll.

Should just be part of the build script - no need to merge during ordinary SLN build.

Make it an option to halt ViewManagerEventDispatchers on exceptions

It would be nice for testing with ViewManagerEventDispatcher and DependentViewManagerEventDispatcher, if they could be set up to rethrow exceptions from the view on the parent thread.

Would you mind a HaltOnExceptions() option for these two? Or how do you use these in tests?

Improvements for the TsClient

@larsudengaard and I was talking about improving the TsClient for:

  1. Making it possible to select types by some sort of "in project" configuration class
  2. Making special conversion of given types, like Id to a string
  3. Support TypeScript interfaces for Views too

The default command line tool could still work as today.

Do you have any thoughts on that?

New view manager event dispatcher implementation

Primarily pull-based, automatically catches up if necessary, asynchronous by default and with async/await support when dispatching to individual view managers, with an option to block until one or more particular views have caught up to some specific global sequence number

ConfigurationBuilders and extensions

Right now there are two ways to implement configuration api... directly in the ConfigurationBuilder and as an extension method to a configuration builder.

I can see the point in extension methods for external modules adding their own config options, but I don't see why the core config is moved out into extension methods? Are there a good reason for this - that I have just forgotten about? :)

An argument for moving them into the ConfigurationBuilders is discoverability, as they will be highlighted in intellisense.

Is there a reason not to?

CommandProcessor/Command

I can't see how I could write a handler that needed to load up more than a single graggregate. The example I usually use for this is Enrolling a Student in a Class. There is a Student aggregate and a Class aggregate (both have business rules that need to be met). Eventually you end up with something like

student.EnrollIn(class)

How can I hydrate multiple aggregates in the handler?

Load without creating

I'm a little confused about loading, getting and creating roots using the various methods on AggregateRoot, CommandContext and UnitOfWork.

AggregateRoot.Load throws if not found but can optionally be configured to create.
DefaultCommandContext.Load creates by default and gives me no way to see if the root was found.
UnitOfWork.Get has a different name but behaves like Aggreroot.Load if I'm not mistaken.

May I suggest that for both AggregateRoot and CommandContext (inspired by NHibernates api): Load() returns a root or null if nonexistant and Get() returns a root or throws with the possiblity to have it created?

So actually two questions:

  1. Could we change the naming and availability of loading methods to be consistant on AggregateRoot and CommandContext.
  2. Could we have a method that returns null when the root is nonexistant.

Does that make sense or am I looking in the wrong direction? :)

Auto-event when initializing an event stream

When a new event stream is initialized, I'm thinking that an automatic event could be emitted - EventStreamCreated or something like that - which could carry the aggregate root type information (if any? in the current implementation we always have an aggregate root type, but maybe that shouldn't be such a hard requirement)

For now, this feature should enable this scenario:

var instance = context.Load<ICanPerformSomeParticularAction>(id);
instance.PerformSomeParticularAction();

where ICanPerformSomeParticularAction is, obviously, an interface.

Right now, it is assumed to be the concrete type of the aggregate root to load, which is also indicated by the type constraints on the Load methods. If the concrete aggregate root type was specified on the EventStreamCreated event, that would no longer be required anywhere but the first time the aggregate root is loaded (which is when it gets created....)

Comments will be appreciated :)

HybridDb ViewManager

Support for easily persisting and querying views-as-documents with SQL Server using HybridDb.

  • fix three failing tests
  • fix HybridDb Id projection, so we can get the id from private classes
  • fix HybridDb multi-thread support when testing with temp tables
  • support multi-key indices - either multi-key views for joining or directly in HDB using xml-cols
  • look at easy setup of Schema/ViewManagers - need to avoid some duplication

New MSSQL view manager should re-create schema if necessary

When creating it with automaticallyCreateSchema: true, the schema-thingie should check whether the schema matches the on required by the model (at least by checking whether all the necessary columns are there)

If not, it should drop the table and re-create it.

Performance measurement hooks for event processing

At least for the view manager event dispatcher, it would be neat if hooks were in place that allowed for tracking time spent dispatching events to individual views.

A result of this could be the ability to measure how long it takes for the managed views to catch up to reality after having processed each command, which in turn could provide the means to better allocate threads to views.

Testing helpers

We've made some tools (base classes mostly) for testing commands and views in a structured way.

I wonder if we should include these in Cirqus?

It goes kind of like this:

        Emit(new FolderCreated("MyFolder"));
        Emit(new FolderCreated("MySafeFolder"));
        Emit(new FileUploaded(fileId_1, Id<User>(), "first.txt", "MyFolder"));
        Emit(new FileUploaded(fileId_2, Id<User>(), "second.txt", "MyFolder"));
        Emit(new FileUploaded(Guid.NewGuid(), Id<User>(), "third.txt", "MySafeFolder"));

        When(new DeleteFolder
        {
            EstateId = Id<Estate>(),
            FolderName = "MyFolder"
        });

        Then(new FileDeleted(fileId_1),
             new FileDeleted(fileId_2),
             new FolderDeleted("MyFolder"));

It gives an understable log output like this:

Given that:
  Company named Version3 with id company/24ee58a6-ee5e-41e9-a73d-1017daf609dd was registered.
    InboxId: inbox/6280ae9a-fd32-4699-b883-8b51c929b74b

  InboxAttachedToCompany
    Id: inbox/6280ae9a-fd32-4699-b883-8b51c929b74b
    CompanyId: company/24ee58a6-ee5e-41e9-a73d-1017daf609dd

  EstateRegistered
    Id: 413446f4-5cfa-4b8d-867a-e541082a8aca
    CompanyId: company/24ee58a6-ee5e-41e9-a73d-1017daf609dd
    EstateName: Unavngivet

  FolderCreated
    Id: 413446f4-5cfa-4b8d-867a-e541082a8aca
    FolderName: MyFolder

  FileUploaded
    Id: 413446f4-5cfa-4b8d-867a-e541082a8aca
    FileId: f5ef85fa-5ed1-4181-b8b3-0779b8fd2ae3
    Author: user/[email protected]
    OriginalFilename: first.txt
    FolderName: MyFolder

When users:
  DeleteFolder
    FolderName: MyFolder
    EstateId: 413446f4-5cfa-4b8d-867a-e541082a8aca

Then:
  √ FileDeleted
      FileId: f5ef85fa-5ed1-4181-b8b3-0779b8fd2ae3

  √ FileDeleted
      FileId: f6b8bbbe-1fdc-4980-a5e3-01f700b3abbb

  √ FolderDeleted
      FolderName: MyFolder

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.