charlessolar / aggregates.net Goto Github PK
View Code? Open in Web Editor NEW.NET event sourced domain driven design model via NServiceBus and GetEventStore
License: MIT License
.NET event sourced domain driven design model via NServiceBus and GetEventStore
License: MIT License
Provide the option for an endpoint to receive ALL events from $ce-DOMAIN and $ce-OOB
this would be equivalent to
IHandleMessages<IEvent>
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.
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.
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
I'm new to this NServiceBus and EventStore, and this library looks great but the documentation is very poor.
How to know what each extension method means and how to use it?
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
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.
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)
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.
Appveyor has been a great build platform
But it may be time to consolidate building and deploying onto one single (github) platform
General issue for code cleanup and writing more tests
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?
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.
Please, add some unit tests to the library. This will make easier to try the features and watch the library in action
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:
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.
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
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.
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.
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.
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)
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
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.
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
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
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.
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
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:
I tried this and it's not working:
endpointConfiguration.Pipeline.Remove("CommandAcceptor");
endpointConfiguration.Pipeline.Remove("UnitOfWorkExecution");
endpointConfiguration.Pipeline.Remove("ExceptionRejector");
Support event versioning
Quickly glanced at your code and realized that you are not using [ConfigureAwait(false)
](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) in the behaviors. Strictly speaking it is not required because NSB makes sure that we consistently apply it to all await
in the pipeline. But I suggest you stay here with the best practice.
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.
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
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
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
To handle events state methods must be private.
Would be nice to print a warning if we discover public methods called Handle
Running rules in entities currently involve a lot of If
's then
s, and throw
s
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);
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
Low priority - up for grabs?
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
:
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);
In the method CreateProjection
I did the same as the previous one.
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?
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 -
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
Should offer the subscriber a unit of work object he can use to encapsulate all work done by receivers of a single event.
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
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
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:
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
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"
}
}
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.
StreamId % maxConcurrency
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 ?!
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.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.