Git Product home page Git Product logo

aggregates.net's People

Contributors

charlessolar avatar dabbas-manteq avatar mikeminutillo 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  avatar  avatar  avatar

aggregates.net's Issues

Adding EventStore to my endpoint fails

Hi,
I'm trying to add EventStore to my endpoint but it's failing with this exception:

2020-08-02 14:08:42.505 ERROR Moving message '54a9f936-25fa-4eb0-afc6-ac0b00b7a829' to the error queue 'error' because processing failed due to an exception:
System.Collections.Generic.KeyNotFoundException: No item found in behavior context with key: Aggregates.UnitOfWork.IDomain
   at NServiceBus.Extensibility.ContextBag.Get[T](String key)
   at NServiceBus.Extensibility.ContextBag.Get[T]()
........

In the handler endpoint, I copied the code from the ToDo example (from the Domain\Endpoin.cs) to initialize the endpoint.
In the sender endpoint I modified the CommandToDomain method (copied it from Infrastructure\Extensions\BusExtensions.cs) to be like this:

public static async Task CommandToDomain<T>(this IMessageSession bus, T message, bool timeout = true) where T: MyBaseCommand
{
	try
	{
		var options = new SendOptions();
		options.SetDestination("DomainDestName");

		Task task = bus.Send(message, options);

		await Task
			.WhenAny(Task.Delay(TimeoutSeconds), task)
			.ConfigureAwait(false);

		if (task.IsFaulted && task.Exception != null)
			throw task.Exception.InnerException;

		if (!task.IsCompleted)
			throw new CommandTimeoutException("Command timed out");
	}
	catch (Exception ex)
	{
		// TODO: Logger
		throw;
	}
}

P.S:
I don't know if this is related but I even added the same Mutator used in the ToDo sample but request is not reaching MutateIncoming method at all.

I don't know what's missing or what should I specify in other files (as I said I copied the Endpoint.cs file to my project)

Replay all events if application endpoint version upgraded

Hi @charlessolar,
I'm facing something I don't know if it's an issue or not.
After I do some changes in the application endpoint (the models or events) and try to run the project, it asks me to upgrade the version, and after I do (Upgrade the assembly version) ALL the events in I triggered from the domain endpoint replied again.

Is that normal and should I consider it in my application handler that maybe the same event may be triggered twice so the handler needs to be idempotent ?!

Keep a hashset of queued/processed event ids

In a situation such as this:

1. EventStore delivers event to endpoint
2. Endpoint loses connection to store
3. Endpoint processes event and queues up ACK
4. Endpoint reconnects to store

there could be a race condition here:

5. Endpoint ACKs processed event
5. EventStore re-delivers event to endpoint

Event delay

Currently to overcome the event ordering issue I put a 2 second delay between receiving events from ES and processing the event. As explained here: https://github.com/volak/Aggregates.NET/blob/master/src/Aggregates.NET.Consumer/Internal/EventSubscriber.cs#L368

A better solution would be to keep track of streams the client has seen and a rough estimate of the event number I'm expecting then do a delay only if I detect an out of order event and only for the duration it takes for the next event to come.

We'll have to insert some clever algorithm tricks like bloom filters. Won't need to be 100% certain but 99.99 at least

Exceptions while still starting up

Instances start 2 message handling systems, one for events from eventstore, one for commands from nservicebus.
Some instances are initially throwing: "StructureMap.StructureMapConfigurationException: No default Instance is registered and cannot be automatically determined for type 'NServiceBus.IMessageSession'" for a few seconds upon startup because the event listener got up before nservicebus finished setting up.

Need to add a check in event subscriber to not send events until NSB is ready

UOW for delivered events

Should offer the subscriber a unit of work object he can use to encapsulate all work done by receivers of a single event.

Support string based entity resolution

Currently we use the full namespace in part of the eventstream name for events and entities.
It would be better to allow a attribute annotation to "name" the event or entity for cases when a refactor affects the namespaces we expect to continue to exist.

This will also help with another feature I'm planning #21

Use RabbitMq's competing consumers

Something to look into -
Currently the competing consumer implementation has every consumer reading from the store, discarding events that are not in their assigned bucket. An implementation completely written into Aggregates.NET.
It occurs to me that libraries like RabbitMq support competing consumers out of the box and in a much nicer way then my solution.

I could
A) Have a little consumer reading from the store sending all events to the main consumer exchange to be processed by any number of actual consumers
B) Have NSB publish written events to rabbitmq in addition to writing to the store - the domain consumer who is reading events that itself generated could forward events out to RabbitMq as well, avoiding the distributed transaction.

Unit of Work Redesign

IUnitOfWork, IDomain, IApplication, IGeneric

Too many interfaces and too specific. Im thinking of implementing some kind of generic typed unit of work and a configuration step

Aggregates.Configure().WithUnitOfWork<MyUnitOfWork>();

and then ctx.Uow() would return MyUnitOfWork - somehow

Important that instances available to handlers don't have Begin and End methods, which was one of the reasons I split up unit of works to begin with.

ExceptionRejector

I notice the complex retry behavior in ExceptionRejector: https://github.com/volak/Aggregates.NET/blob/master/src/Aggregates.NET/Internal/ExceptionRejector.cs#L49

Wouldn't it be easier if it was catching a specific business exception type? I mean throwing a business exception that causes a command to be rejected is usually deterministic. Either a command can be accepted or not. Retrying the action would probably not help.

The alternative would be to move the catching behavior to where the actual method on the aggregate is executed. If the aggregate throws, it is a business exception and should cause a command to be rejected. If persisting an aggregate throws (e.g. concurrency error), this would be handled by standard NServiceBus retry mechanism.

Parallel Event Processing

Prior to v0.4.14 we were executing events in parallel - up to 10 running at the same time by default.
With the changes to EventSubscriber we now run 1 thread for normal events and 1 thread for OOB events.

This can be easily expanded to again support multiple events concurrently.

  • Note that events should still be run in order by stream - so use StreamId % maxConcurrency

Change ValueObject implementation to an immutable "State" type

Been working with React, Redux, and ImmutableJs recently - seems to me that instead of implementing several SingleValueObjects to make aggregate properties immutable I can instead model an aggregate state which is immutable and only modified by events.

Testing and tests

General issue for code cleanup and writing more tests

  • Switch to xUnit
  • Use Autofixture and FakeItEasy
  • Reduce number of frivolous interfaces
  • Provide easy access to certain internals for foreign testing
  • Get OpenCover working

Isn't there a way to NOT use EventStore?

I'm trying to build an application that uses SQL Server to read data from, so I don't need the EventStore to be used, I noticed in your example TodoDDD in the project Application which only needs the UnitOfWork and not EventStore, it also creates ES client and adds it to the endpoint configuration.

Why do I need to initialize ES if I will not use it? I tried not to it throws an exception:

2020-08-17 12:05:06.976 ERROR Moving message '4920b48b-5ac6-4e4f-a3fc-ac1a0095b7b1' to the error queue 'error' because processing failed due to an exception:
StructureMap.StructureMapConfigurationException: No default Instance is registered and cannot be automatically determined for type 'Aggregates.Contracts.IStoreEvents'

There is no configuration specified for Aggregates.Contracts.IStoreEvents

1.) new StoreEntities(*Default of IMetrics*, *Default of IStoreEvents*, *Default of IStoreSnapshots*, *Default of IOobWriter*, *Default of IEventFactory*, *Default of IVersionRegistrar*, *Default of ITrackChildren*)
2.) Aggregates.Internal.StoreEntities
3.) Instance of Aggregates.Contracts.IStoreEntities (Aggregates.Internal.StoreEntities)
4.) new RepositoryFactory(*Default of IStoreEntities*)
5.) Aggregates.Internal.RepositoryFactory
6.) Instance of Aggregates.Contracts.IRepositoryFactory (Aggregates.Internal.RepositoryFactory)
7.) Container.GetInstance<Aggregates.Contracts.IRepositoryFactory>()
8.) Lambda: Invoke(value(Aggregates.Internal.Container+<>c__DisplayClass7_0`1[Aggregates.UnitOfWork.IDomain]).factory, value(Aggregates.Internal.Container))
9.) Instance of Aggregates.UnitOfWork.IDomain
10.) Container.GetInstance<Aggregates.UnitOfWork.IDomain>()

   at lambda_method(Closure , IBuildSession , IContext )
   at StructureMap.Building.BuildPlan.Build(IBuildSession session, IContext context)
   at StructureMap.BuildSession.BuildNewInSession(Type pluginType, Instance instance)
   at StructureMap.Pipeline.ContainerSpecificObjectCache.buildWithSession(Type pluginType, Instance instance, IBuildSession session)
   at StructureMap.Pipeline.LifecycleObjectCache.<>c__DisplayClass5_0.<Get>b__0(Int32 _)
   at StructureMap.Pipeline.LazyLifecycleObjectCacheExtensions.<>c__DisplayClass1_1`2.<GetOrAdd>b__1()
   at StructureMap.Pipeline.LazyLifecycleObject`1.CreateValue()
   at StructureMap.Pipeline.LazyLifecycleObject`1.get_Value()
   at StructureMap.Pipeline.LazyLifecycleObjectCacheExtensions.GetOrAdd[TKey,TValue](ConcurrentDictionary`2 cache, TKey key, Func`2 valueFactory)
   at StructureMap.Pipeline.LifecycleObjectCache.Get(Type pluginType, Instance instance, IBuildSession session)
   at StructureMap.BuildSession.ResolveFromLifecycle(Type pluginType, Instance instance)
   at StructureMap.SessionCache.GetObject(Type pluginType, Instance instance, ILifecycle lifecycle)
   at StructureMap.SessionCache.GetDefault(Type pluginType, IPipelineGraph pipelineGraph)
   at StructureMap.BuildSession.GetInstance(Type pluginType)
   at StructureMap.Container.DoGetInstance(Type pluginType)
   at StructureMap.Container.GetInstance[T]()
   at Aggregates.Internal.Container.Resolve[TResolve]() in D:\a\Aggregates.NET\Aggregates.NET\src\Aggregates.NET.StructureMap\Internal\Container.cs:line 116
   at Aggregates.Internal.UnitOfWorkExecutor.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in D:\a\Aggregates.NET\Aggregates.NET\src\Aggregates.NET.NServiceBus\Internal\UnitOfWorkExecutor.cs:line 52
   at Aggregates.Internal.CommandAcceptor.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in D:\a\Aggregates.NET\Aggregates.NET\src\Aggregates.NET.NServiceBus\Internal\CommandAcceptor.cs:line 41
   at NServiceBus.ScheduledTaskHandlingBehavior.Invoke(IIncomingLogicalMessageContext context, Func`2 next)
   at NServiceBus.InvokeSagaNotFoundBehavior.Invoke(IIncomingLogicalMessageContext context, Func`2 next)
   at Aggregates.Internal.ExceptionRejector.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in D:\a\Aggregates.NET\Aggregates.NET\src\Aggregates.NET.NServiceBus\Internal\ExceptionRejector.cs:line 48
   at Aggregates.Internal.ExceptionRejector.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in D:\a\Aggregates.NET\Aggregates.NET\src\Aggregates.NET.NServiceBus\Internal\ExceptionRejector.cs:line 99
   at Aggregates.Internal.SagaBehaviour.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in D:\a\Aggregates.NET\Aggregates.NET\src\Aggregates.NET.NServiceBus\Internal\SagaBehaviour.cs:line 31
   at NServiceBus.DeserializeMessageConnector.Invoke(IIncomingPhysicalMessageContext context, Func`2 stage)
   at NServiceBus.InvokeAuditPipelineBehavior.Invoke(IIncomingPhysicalMessageContext context, Func`2 next)
   at NServiceBus.ProcessingStatisticsBehavior.Invoke(IIncomingPhysicalMessageContext context, Func`2 next)
   at NServiceBus.TransportReceiveToPhysicalMessageConnector.Invoke(ITransportReceiveContext context, Func`2 next)
   at NServiceBus.MainPipelineExecutor.Invoke(MessageContext messageContext)
   at Aggregates.Internal.Dispatcher.SendLocal(IFullMessage message, IDictionary`2 headers) in D:\a\Aggregates.NET\Aggregates.NET\src\Aggregates.NET.NServiceBus\Internal\Dispatcher.cs:line 117
Exception details:
        Message ID: 4920b48b-5ac6-4e4f-a3fc-ac1a0095b7b1

Setting Correlation Id Can Make NSB Throw

According to #50 - when MutateOutgoing sets all the carry over headers NSB complains later in the pipeline about not being able to set the Correlation Id.

Investigate what Agg.Net uses Correlation Id for -

  • If not needed add to ignore list so its not carried over
  • If used, properly implement correlation id override so NSB doesn't complain

Try to avoid ReadOnlySettings usage in behaviors

Just a best-practice we apply in our code bases. We extract the settings relevant for a behavior from the feature context and pass them in strongly typed into the behaviors. With that approach the settings retrieval belongs to the bootstrapping / startup phase and you think twice before you access the settings in an Invoke call (which would have implications like unnecessary allocations due to the nature of the settings dictionary)

Separate consumer projection for OOB events

Consumers each create a projection for reading only the events they care about from ES. These projections are consumed using PINNED consumers because order is important.
However order is NOT important for OOB events, and we can speed things up by creating a separate OOB projection and consuming from it using round robin instead.

Conflict resolution

Removed the logic for conflict resolution because I realized simply hydrating the conflicting commands on a fresh stream and retrying the commit is a pretty bad policy.
The correct thing to do is to re-run the business logic executed by the command.
To do that off the top of my head we'd have to re-run the command's handle method for the specific instance that changed the stream.. which is just not possible to do correctly either.

Will have to look at other resolution strategies.

I guess the best way would be to let the user define his own custom conflict resolvers so he can explicitly define when a version conflict can be accepted and when it can't

Id not set until after hydrate

Due to the TId stuff the repository class doesn't set the entity's Id field until after hydration - which doesn't matter too much, but is a little odd.

There is also a related issue of Id not being set when resolving conflicts

Optional snapshot subscriptions

In the rewrite, all endpoints are subscribing to $ce-SNAPSHOT

Doesn't hurt anything, but there should be a way to detect a domain endpoint vs a read model endpoint to disable/enable snapshot subscribing

Inheritance in models

Hi Charles,

I would like to ask you about your opinion in a use case I have.
Consider you have class Car which is abstract, and from it inherit multiple types (Audi, BMW ...etc).
The current structure required by Aggregates.NET doesn't seem to support this inheritance (because I need to inherit the Entity<TEntity, TState> to be used in the context.For<TEntity>().New(Id) for example.
If I build the Audit : Car I can't use it for context.For<Audi> and I don't actually want to because if have another class to link to "a" car like Person 1->* Cars I won't be able to know the Id of the List of cars this Person have of which type of cars, and also both Person and Car are Aggregate roots, so I can't put Car as sub entity to Person .

Any suggestion?

Keeping track of children

We currently support children getting information from their parent via

this.Parent

we can support a parent getting it's children like so

this.Children<ChildEntity>()

which would read all ChildEntity streams and return a list of ChildEntities.

Child entity stream ids would be in metadata? Or perhaps an internal Aggregates.Net event

UOW for command processing

When processing a command, its possible that the domain might raise several events. We should encapsulate all events raised from a single command in an event store transaction

Multiple projections per endpoint

To reduce the need to replace the entire endpoint stream every upgrade - lets create projections for each namespace value in [Versioned] events and subscribe to each.

Then, a replay would only be needed for the specific projection if an event version is upgraded inside a projection or a new event added.

thoughts:

  • should this only be for events? Or can we do commands too?
  • what happens if a command is updated but only 1 handler is updated to use the new version?
  • should handler projection names include a hash of handled event versions to prevent a user from updating the handler and not incrementing version?
  • should we support event upgrading/downgrading into a specific handler?

Error when calling Apply method in the Domain app

I've built an application project to listen to events triggered by the domain (via the Apply method) and update the read model.
The handler is receiving the event and handling it but in the domain project console I'm seeing the following exception:

  • OrderPlaced is the event I'm triggering.
No handlers could be found for message type: Messages.OrderPlaced
   at NServiceBus.LoadHandlersConnector.Invoke(IIncomingLogicalMessageContext context, Func`2 stage)
   at CurrentSessionBehavior.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in /_/src/SqlPersistence/SynchronizedStorage/CurrentSessionBehavior.cs:line 19
   at Aggregates.Internal.LogContextProviderBehaviour.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\LogContextProviderBehaviour.cs:line 39
   at Aggregates.Internal.LocalMessageUnpack.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\LocalMessageUnpack.cs:line 110
   at Aggregates.Internal.UnitOfWorkExecutor.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\UnitOfWorkExecutor.cs:line 90
   at Aggregates.Internal.UnitOfWorkExecutor.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\UnitOfWorkExecutor.cs:line 129
   at Aggregates.Internal.CommandAcceptor.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\CommandAcceptor.cs:line 76
   at NServiceBus.ScheduledTaskHandlingBehavior.Invoke(IIncomingLogicalMessageContext context, Func`2 next)
   at Aggregates.Internal.ExceptionRejector.Invoke(IIncomingLogicalMessageContext context, Func`1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\ExceptionRejector.cs:line 49

Any idea what could be the issue? I tried to replicate the code in the ToDo example, but with no luck, I'm missing something.

Frozen streams - failure cases

In several places such as conflict resolution and delayed channels streams can be frozen - meaning no one else is allowed to write to them until the instance who froze the stream releases the lock.

Currently there is no good way to detect if a stream is deadlocked (an instance freezes a stream then promptly crashes)

And maybe some others - need to think of how to handle faulted freezes.

Sagas Doesn't Work

Hi Charles,

Trying to use sagas with persistent storage but it doesn't work, so I created a simple project where I tried using both scenarios: using Aggregates.NET's Apply and using NServiceBus's native context.Publish, in the native scenario the saga works fine, but with Apply scenario the saga never gets executed (same configurations in both scenarios)

Here you can find the project I created for this test.

Thank you

Migrate to Actions

Appveyor has been a great build platform

But it may be time to consolidate building and deploying onto one single (github) platform

Business Rules

Running rules in entities currently involve a lot of If's thens, and throws

We can make this easier.

There's a few changes I can make right off the bat - adding a Rule method on entities to validate against the state of the entity automatically.

   public void PostInvoice() {

       // Instead of....
       if( State.Address == null )
           throw new BusinessException("No address set");
     
       // Do this!
       Rule("Address Set", x => x.Address == null, "No address set");

       Apply<Events.Posted>();            
   }

In the long term however we want to support dynamic rules and validation against data that may not be inside the entity. If for instance we want to make sure the customer is in good standing before posting invoice - we'd have to load the customer entity to check. And a business manager might want to override a rule or create his own rules without involving a programmer to do so.

The above works for static validation - so lets also add dynamic!

I want each public method on a entity to be available for rule creation dynamically via the app.

I envision some kind of extendable entity validator. Something like

    RuleFor<Invoice>()
        .When(x => x.PostInvoice)
                .Rule("Address Set", x => x.Address == null, "No address set");
    

we already know how to serialize expressions so aggregates.net would just need to keep track of rules for entities and apply them while executing commands and events.

public Task Handle(Commands.AddRuleToInvoices command, IMessageHandlerContext ctx) {
    Aggregates.For<Invoice>()
              .When(command.Action)
              .Rule(command.Name, command.Expression, command.Message);
}

Dynamically loading other entities for checking

Aggregates.For<Invoice>()
    .When(x => x.PostInvoice)
    .Load<Customer>(x => x.CustomerId)
    .Load<PaymentMethod, Customer>(customer => customer.DefaultPaymentMethodId)
    .Rule<PaymentMethod>("Payment Is CreditCard", method => method.Type == Methods.CreditCard);

Todos

  • Static RuleFor on entities
  • Expressive rules on entity types
  • Dynamic rules via serialized expressions
  • Test generation for seeing new rules in action (?)

IDomainUnitOfWork - not for consumers

Currently domain units of work are created even when solely handling events. This is kind of unexpected because it allows event consumers to load entities from the domain.

Might also be a good idea to re-look into the dual unit of work implementations and attempt to merge them into one.

Support non-entity event raising

In certain circumstances it would be useful to allow posting events not apart of an entity.

Take the example:

You have an "EmployeeType" field, which presents the user with a list of already stored employee types or allows them to add another. Making this type of object an entity or aggregate doesn't make sense as it only has one field "Name" with 1 or 2 commands (create, destroy).

Instead of that we can make a

_uow.Raise<Events.EmployeeType>("STREAM", x => {
x.Name= Name;
});

feature to support directly posting an event to a named stream.

The event consumers would see the event and store the employee type name in a table which would be given to the user next time he searches for an employee type.
No need for a domain object at all - because there is no business logic for these objects

DelayedAttribute add "once"

When delaying a message like:

[Delayed(typeof(Tick), delayMs: 10000)]

the messages are saved until 10 seconds go by and executed all at once.

Some message handlers don't need to receive all messages, just the very last one. So we can support a once: True parameter - and now that I think about it maybe it makes more sense to support first: True and last: True instead.

Add endpoint version, aggregates version to event + snapshot headers

As part of the process to identify message objects by string names and not assembly types - it would be good to include version information into events and snapshots so we can maintain backward compatibility.

Current:

Metadata
{
  "EventId": "4c6c03d0-d04a-4571-b88c-0096acf0a420",
  "EntityType": "HelloWorld, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null",
  "StreamType": "SNAPSHOT",
  "Bucket": "default",
  "StreamId": "World",
  "Parents": [
  ],
  "Compressed": false,
  "Version": 102,
  "Timestamp": "2018-01-04T12:31:19.3904729Z",
  "Headers": {},
  "CommitHeaders": {
    "CommitId": "3e91d239-b11f-48b4-8c1a-a85e00ccd4da",
    "Instance": "8763fe67-48eb-4124-9588-b04cb948f6e1"
  }
}

Proposed:

Metadata
{
  "EventId": "4c6c03d0-d04a-4571-b88c-0096acf0a420",
  "EntityType": "HelloWorld, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null",
  "StreamType": "SNAPSHOT",
  "Bucket": "default",
  "StreamId": "World",
  "Parents": [
  ],
  "Compressed": false,
  "Version": 102,
  "Timestamp": "2018-01-04T12:31:19.3904729Z",
  "Headers": {
      "Aggregates.Net.Version": "0.1.0.252",
      "Domain.Version": "0.1.0"
   },
  "CommitHeaders": {
    "CommitId": "3e91d239-b11f-48b4-8c1a-a85e00ccd4da",
    "Instance": "8763fe67-48eb-4124-9588-b04cb948f6e1"
  }
}

Support for sending commands from message handlers

In NSB 6 I can't use request / response in message handlers - see Particular/NServiceBus.Callbacks#42

The "correct" way to do this from what I'm told is to setup a saga which can collect replies from normal Sends and handle timeouts / retries and such in itself.

It sounds good - and sounds like something we can include in the library so users won't have to deal with that logic themselves

Taking snapshot to speed reading from EventStore

Hi @charlessolar,

I'm facing slowness reading from EventStore after an aggregate root raised many events, reply them looks like takes sometime.

Is there way from the Aggregates.Net to take a snapshot of the events to speed things up?

I tried inside the Entity to call Snapshot but didn't get any thing.

External state object

Entities can really be divided into 2 operations, reading and writing.

Only 1 entity should be opened for writing, whereas many entities can be opened for reading. To more easily define and enforce this best-practice we can support a "State" object which can be read from the store and hydrated but cannot raise new events.

The idea being when an entity is defined the user will define the read and write side separately and entities that depend on information in other entities will receive access to the state of the entity not the entity itself.

`RecieveAllEvents()` config option

Provide the option for an endpoint to receive ALL events from $ce-DOMAIN and $ce-OOB

this would be equivalent to

IHandleMessages<IEvent>

ProjectionsManager is failing because there's no SSL certificate available.

Hi again :),
In EventStore v20 they use TLS by default, so when I try to run EventStore without enabling TLS, on my dev machine, I face an exception about SSL connection and I figured out (by EventStore's users help) that if I'm not using secure connection I need to specify the HTTP schema in the ProjectionsManager creation like this:

var manager = new ProjectionsManager(client.Settings.Log, client.Settings.GossipSeeds[0].EndPoint, TimeSpan.FromSeconds(5), httpSchema: _httpSchema);

I cloned the source code of Aggregates.NET and did the changes needed and it worked !.
Two changes in file EventStoreConsumer.cs:

  1. In method EnableProjection I replaced:
var manager = new ProjectionsManager(connection.Settings.Log, connection.Settings.GossipSeeds[0].EndPoint, TimeSpan.FromSeconds(30));

With:

var httpSchema = GetHttpSchema(connection.Settings);

var manager = new ProjectionsManager(connection.Settings.Log, connection.Settings.GossipSeeds[0].EndPoint, TimeSpan.FromSeconds(30), httpSchema: httpSchema);
  1. In the method CreateProjection I did the same as the previous one.

  2. Method GetHttpSchema is as the following:

private string GetHttpSchema(ConnectionSettings settings)
{
	return settings.UseSslConnection ? "https" : "http";
}

What do you think? is that a correct solution for such an issue?

Add unit tests

Please, add some unit tests to the library. This will make easier to try the features and watch the library in action

Add support for storing OOB events into different storage

OOB events, which I use currently for events on entities which don't affect state (example would be cpu measurements every second on an entity monitoring performance) are currently stored in eventstore.

For intense apps these events end up being 90% of the data in the store, and you can argue that sometimes events that are older than a certain age no longer matter at all.

Aggregates.NET can export an interface to allow users to write OOB events to a different storage mechanism which more easily allows for archiving and deletion or even perhaps just sends directly to the consumer (NSB) skipping the store all together when storage of these events doesn't matter.

Projections

This project looks very promising! I am new to event sourcing and I am looking through the source to find where the view models are being built from projections. Can you point me to the correct place?

Thanks

Writing to EventStore is taking so much time, sometimes it doesn't work, and events don't reach directly to Application endpoint.

I'm facing an issue trying to build the Application endpoint that will have handlers and listen to events that's triggered in the Domain endpoint using Apply method.

  • When I run both endpoints together:
    I was facing an issue that sometimes command reach the domain handler and the method Apply but is not saved in the EventStore (I can't see it in the "Stream Browser" window), then it started working.

  • When I run only the Domain endpoint without running the Application endpoint, all works perfectly.

  • When I run again the Domain endpoint+Application endpoint the domain works and data saved in ES but event doesn't reach the Application, BUT if I turned off the Application endpoint and run it again, I receive ALL the events that I Domain triggered from the beginning of the test (all stacked there), even after receiving them, If I re-run the Application endpoint again I receive them again.

The tests all done using the ToDo sample code.

Code coverage report

After a few hours attempting to add OpenCover into the cake build script I must give up for the time being.

The unit console runner is not generating test output to the txt file as requested and I have no idea why. It would be nice to have this kind of feedback on builds but not necessary. Will look at it again later.

Might have to switch to xUnit

Upgrade OOB events

Raise can support several options - a transient event which is not saved at all, an event that is deleted after X days, and an event which is saved forever.

How to catch BusinessException in custom built behavior?

Hi Charles,

I was trying to build a custom behavior to catch exceptions and publish a notification, and also tried to follow this tutorial about Subscribing to Error notifications from ParticularSoftware site, but I can't catch any BusinessException thrown.

In my system I'm not using the Command method in the class BusExtensions in Aggregates.NET, I'm using directly the bus.Send like in the method PassiveCommand in the pre-mentioned class.

I looked in the source code and figured out the library is handling it in the ExceptionRejector class, any way to catch the exception? and way to remove this step? because I couldn't, 3 steps are depending on each other:

  • CommandAcceptor
  • UnitOfWorkExecution
  • ExceptionRejector

I tried this and it's not working:

endpointConfiguration.Pipeline.Remove("CommandAcceptor");
endpointConfiguration.Pipeline.Remove("UnitOfWorkExecution");
endpointConfiguration.Pipeline.Remove("ExceptionRejector");

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.