mlidbom / composable Goto Github PK
View Code? Open in Web Editor NEWHome Page: http://composabletk.net/
License: Other
Home Page: http://composabletk.net/
License: Other
A highly performant mode where most reads from the database can be eliminated thus greatly increasing performance and lessening the risk of locking issues in the storage providers.
An endpoint, via configuration, promises that it is the only instance that will use the EventStore, DocumentDb and ServiceBus. This will be enforced by some sort of persistence layer data/locks so that no other instance can start. Given this guarantee we can serve any read that has a cache hit directly from the cache since we can know that no update has happened in the storage. Concurrency will be managed in the style of the TransactionWideLock class used by the InMemory storage provider.
This mean that we eliminate the storage round-trip, storage load and serialization. Given sufficient memory to keep most hot data in the cache this mode should offer at least an order of magnitude increased performance. Given one or more hot-spares we also get availability.
If you create an endpoint without any message handlers you are unable to use the registered event store registered in the endpoint.
See: #16 (Proper support for TransactionScope without using IUnitOfWorkParticipant. )
What this property really tracks is whether or not the endpoint has any remotely accessible message handlers. This should simply be detected by whether or not any exists. The lack of this functionality causes some odd issues when setting up endpoints with no local handlers. Not a standard use case, but until/unless we make it easy to use individual components without the concept of an endpoint we need to take care of this.
Part of #8
The event store session should be refactored so that events are saved to the database and published on the bus when they are raised rather than when the transaction completes.
The implementation of the Save method on the DocumentDbSession should be likewise changed to perform its change immediately.
These changes should ensure that all participants are already enlisted in the transaction when commit is called. That way we should not run into the problems that we currently do with failures to escalate the transaction to a distributed transaction and a normal implementation of IEnlistmentNotification should work.
We want Composable to be usable on all .Net platforms and .Net Standard is the way to go to achieve that according to Microsoft.
https://blogs.msdn.microsoft.com/dotnet/2016/09/26/introducing-net-standard/
Rather than completely removing an aggregate on EventStore.Delete, leave a single AggregateDeleted event behind to ease in debugging issues related to deletion.
Or possibly keep track of deleted aggregates in a separate table?
Currently hhanging data in the event in any way will corrupt the insertion order of the events since we rely on SQL server rowversion. We need to fix this and will take the opportunity to make the refactoring story for events much more palatable.
Goals:
We achieve excellent performance in the event store by caching the event histories of an aggregate.
This relies on the assumption that events are never modified. Currently we have no way of guaranteeing that this is the case. We should at least do some basic validation of the design of event interfaces and classes and throw exceptions if they are mutable.
Then the question is whether we should recursively walk into the types exposed by the properties.
If so what should be the rules for those types? [For now we do not recurse into the exposed property types.]
I found a problem for AccountManagement sample, If I running it with empty database, when application start, it will only create InboxMessages, MessageDispatching, OutboxMessages. If I running register at once it will throw "Invalid object name 'ValueType'." which caused by bus.Execute(AccountApi.Queries.TryGetByEmail(email)) is Some. By if I login first with any account, it will create store and ValueType tables, then everything works.
This can be achieved by leveraging Covariance of generic type parameters.
Currently the exception message just gives you the type...
Using an EventStoreSession instance within multiple transactions leaves you without the guarantee that access to an aggregate for write purposes will be serialized. If contention is high for an aggregate failures due to optimistic concurrency becomes quite likely.
If this occurs the cause of the problem is also quite hard to understand for the framework user since the exception you get is a primary key violation on an insert. This is not acceptable user experience. What we want the user to see in the log instead is something like this:
This enhancement is about adding the warning. I will add another enhancement for catching and wrapping the primary key violation in an OptimisticConcurrencyException.
In principle there is no reason why these classes should be usable only by Composable. They supply a lot of value that would be beneficial even if you use another event store implementation.
As part of this, investigate if it is possible and/or beneficial to remove the requirements that the events inherit AggregateRootEvent
Here we link together the work to be done for the milestone as a whole.
Questions
When we first raise an event of a type that has not been raised before:
When more events of the new type are raised their Event Type Id is fetched from the Dictionary.
Now If for some reason the first transaction fails the database change is rolled back, but the Event Type Id will still exist in the Dictionary in memory.
From this point on all events raised of this type will cause an exception with the error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Event_EventType". The conflict occurred in database "SOME_DATABASE", table "dbo.EventType", column 'Id'.
Part of #8
Support specifying the previous Name, FullName or Path (Namespace + Parent classes) of the event.
See: #3
IHandleRemoteMessage should be removed. It is a fatally flawed routing mechanism. The idea was to get rid of duplicate handlers when the same handler was referenced in process (where the event was originally raised) and in a separate process (some other endpoint). Implementing this interface made sure that the handler was not triggered in memory so it was only executed once. But of course this fails again the second two or more endpoints references the assembly containing the handler so it is simply a broken idea.
The correct approach is to always implement bus message handlers in the assembly defining the endpoint where they should execute. Period.
These links could help avoid PlatformNotSupportedExceptions
https://stackoverflow.com/questions/57321340/find-out-platformnotsupportedexception-during-build
https://stackoverflow.com/questions/40433300/list-all-references-calling-a-method-using-mono-cecil/43608685
The event store, document database, message handler interfaces should all have async versions of the appropriate methods
Sometimes an event is essentially a poison event(Created through a bug). Breaking an aggregate root.
When that happens it should be possible to write a migrator that "deletes" those events.
It should also be simple to write a sql script that does the same.
Of course nothing should be removed from the actual storage. Rather something should be added :)
The support for nesting Components and Entities within an Aggregate is currently more limited than it should be. It locks the structure down to a specific depth of nesting where the exact type is known at each depth.
This is unacceptably limiting. We should support more dynamic nesting strategies.
Implement example of this Aggregate in the Samples project: Thread/Post Composite Aggregate where the Aggregate is a thread with a name and a root Post that is a recursive structure where each post contains a list of Answers that are also Posts.
Early Solution draft
Questions
Is this really a good way to handle it?
When using the RenamedEvent attribute to rename an event it is not clear that it will not work unless you also change wiring. Worse: you will only notice that it does not work if you load an old aggregate that contains the event. This might not occur until in production. And when you get the exception the real cause (missing wiring) is anything but obvious.
Possible short term improvement:
Detect if any event class used by the event store has such an attribute, but not the wiring for it. If so, throw an exception. The exception should contain a message that clearly explains what the developer needs to change. If the event type is used at all in automated or manual testing the bug will be detected.
Optimal long term solution:
Find a way of implementing this without requiring manual wiring, and without causing significant startup slowdown by having to scan every type in every assembly.
Since I can see you have been working on making the event sourcing in Composable perform, and I have a suggestion.
From what I can see, the domain events are created in the domain model/aggreagte constructor, every time a domain model is instantiated.
Example:
public Foo()
{
RegisterEventAppliers()
.For<IRenamedEvent>(Apply)
.For<IFooNamePropertyUpdated>(e => this.Name = e.Name);
}
2 Action<>
objects, 2 event registrations and a closure for ´this´ are instantiated. In a situation where a lot of domain models are instantiated, we get a lot of unnecessary allocations and GC's, especially using a domain model with a lot of events, which they tend to get over time.
Instead, i suggest event registration in a static constructor, and using the domain model as the second argument to the event handler Action<TEvent, TDomainModel>
.
Example (Domain model/aggregate constructor):
static Foo()
{
RegisterEventAppliers()
.For<IRenamedEvent>(Apply)
.For<IFooNamePropertyUpdated>((theEvent, theDomainModel) => theDomainModel.Name = theEvent.Name);
}
2 event registrations and 2 ´Action<,>´ objects are created, but only once. No closure is needed for the domain model, since it's a parameter.
What do you think?
We want a consumer project to get as much of the functionality from Composable.CQRS as possible with as few transitive dependencies pulled in as possible. If the functionality used only requires Composable to reference .Net Standard, then no other dependencies should be pulled in.
In practice this probably means that many/all of the base interfaces and in-memory implementations should move into Composable.Core. Alternatively all code that needs additional dependencies should be pulled out of Composable.CQRS.
Make sure to consider #19 (Migrate to .Net Standard) and #8 (Simple Service Bus) when making this change.
Everything in Composable that uses storage in Sql Server should also work with these providers in addition to Microsoft Sql Server:
See: #16 (Proper support for TransactionScope without using IUnitOfWorkParticipant. )
The type SqlDecimal is SqlServer specific. Having that in the EventStore class makes implementing other storage layers cumbersome and does not feel like a clean design. It is also rather complicated to use. The same goes for the current structure of the table with computed column which is not supported by all relational databases.
We could consider using two 64 bit integer columns for event ordering instead. The first would get the value of the identity column when inserting normal events. The second would be used to order events with the same value in the first column.
As far as I can see this should work and make the code and table structure both simpler and more portable.
Various RPC style frameworks require types to be marked with [Serializable] in order for them to be usable.
Test code should not need to know about how to wire framework level classes for testing.:
Things like wiring these should all be encapsulated:
Also you should not need to know when and how to call the pair of current methods:
...CallBeforeAllOtherWiring, ....CallAfterAllOtherWiring
API should be something like like:
Container.ConfigureForTesting( container =>
{
//The actual container setup code.
}
)
Various RPC style frameworks require this in order to be able to transmit them to the caller.
Check out whether the serialization style constructors need to be implemented for every exception type...
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.