Skip to content

Commit f2d58ac

Browse files
committed
Merge branch 'develop'
2 parents f97b05e + 6e4dc6a commit f2d58ac

79 files changed

Lines changed: 2716 additions & 105 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Documentation/Aggregates.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
Initially before you can create a aggregate, you need to create its
44
identity. You can create your own implementation by implementing
5-
the `IIdentity` interface or you can use a base class that EventFlow provides
6-
like this.
5+
the `IIdentity` interface or you can use a base class `Identity<>` that
6+
EventFlow provides, like this.
77

88
```csharp
99
public class TestId : Identity<TestId>
@@ -14,7 +14,17 @@ public class TestId : Identity<TestId>
1414
}
1515
```
1616

17-
Note that its important to call the constructor argument for `value` as
17+
The `Identity<>` value object provides generic functionality to create and
18+
validate aggregate root IDs.
19+
20+
- IDs follow the form `{class without "Id"}-{guid}` e.g.
21+
`test-c93fdb8c-5c9a-4134-bbcd-87c0644ca34f` for the above `TestId`
22+
- IDs can be generated using the static `New` property
23+
- IDs can be validated using the static `bool IsValid(string)` method
24+
- ID validation errors (if any) can be gathered using the static
25+
`IEnumerable<string> Validate(string)` method
26+
27+
Note that its important to _name_ the constructor argument `value` as
1828
its significant if you serialize the ID.
1929

2030
Next, to create a new aggregate, simply inherit from `AggregateRoot<,>` like

Documentation/Customize.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
When ever EventFlow doesn't meet your needs, e.g. if you want to collect
44
statistics on each command execution time, you can customize EventFlow.
55

6-
You have two options
6+
Basically EventFlow relies on an IoC container to allow developers to customize
7+
the different parts of EventFlow.
8+
9+
Note: Read the section "Changing IoC container" for details on how to change
10+
the IoC container used if you have specific needs like e.g. integrating
11+
EventFlow into an Owin application.
12+
13+
You have two options for when you want to customize EventFlow
714

815
* Decorate an implementation
916
* Replace an implementation
@@ -53,3 +60,23 @@ A example of a service that you might be interested in creating your own
5360
custom implementation of is `IAggregateFactory` which handles all aggregate
5461
creation, enabling you to pass additional services to a aggregate upon
5562
creation before events are applied.
63+
64+
## Changing IoC container
65+
66+
EventFlow provides the NuGet package `EventFlow.Autofac` that allows you
67+
to set the internal `ContainerBuilder` used during EventFlow initialization.
68+
69+
Pass the `ContainerBuilder` to EventFlow and call `CreateContainer()` when
70+
configuration is done to create the container.
71+
72+
```csharp
73+
var containerBuilder = new ContainerBuilder();
74+
75+
var container = EventFlowOptions.With
76+
.UseAutofacContainerBuilder(containerBuilder) // Must be the first line!
77+
...
78+
.CreateContainer();
79+
```
80+
81+
Maybe call `UseAutofacAggregateRootFactory()` just before the
82+
`CreateContainer()` to use the Autofac aggregate root factory.

Documentation/DoesAndDonts.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Does and Don'ts
2+
Whenever creating an application that uses CQRS+ES there are several things
3+
you need to keep in mind to make it easier and minimize the potential bugs.
4+
This guide will give you some details on typical problems and how EventFlow
5+
can help you minimize the risk.
6+
7+
## Events
8+
9+
#### Produce clean JSON
10+
Make sure that when your aggregate events are JSON serialized, they produce
11+
clean JSON as it makes it easier to work with and enable you to easier
12+
deserialize the events in the future.
13+
14+
- No type information
15+
- No hints of value objects (see [value objects](ValueObjects.md))
16+
17+
Here's an example of good clean event JSON produced from a create user event.
18+
19+
```JSON
20+
{
21+
"Username": "root",
22+
"PasswordHash": "1234567890ABCDEF",
23+
"EMail": "root@example.org",
24+
}
25+
```
26+
27+
#### Keep old event types
28+
Keep in mind, that you need to keep the event types in your code for as long as
29+
these events are in the event source, which in most cases are _forever_ as
30+
storage is cheap and information, i.e., your domain events, is expensive.
31+
32+
However, you should still clear your code, have a look at how you can
33+
[upgrade and version your events](./EventUpgrade.md) for details on how
34+
EventFlow supports you in this.

Documentation/FAQ.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# FAQ - frequently asked questions
2+
3+
#### Why isn't there a "global sequence number" on domain events?
4+
5+
While this is easy to support in some event stores like MSSQL, it doesn't
6+
really make sense from a domain perspective. Greg Young also has this to say
7+
on the subject:
8+
9+
> Order is only assured per a handler within an aggregate root
10+
> boundary. There is no assurance of order between handlers or
11+
> between aggregates. Trying to provide those things leads to
12+
> the dark side.
13+
>> [Greg Young](https://groups.yahoo.com/neo/groups/domaindrivendesign/conversations/topics/18453)
14+
15+
#### Why doesn't EventFlow have a unit of work concept?
16+
17+
Short answer, you shouldn't need it. But Mike has a way better answer:
18+
19+
> In the Domain, everything flows in one direction: forward. When something bad
20+
> happens, a correction is applied. The Domain doesn't care about the database
21+
> and UoW is very coupled to the db. In my opinion, it's a pattern which is
22+
> usable only with data access objects, and in probably 99% of the cases you
23+
> won't be needing it. As with the Singleton, there are better ways but
24+
> everything depends on proper domain design.
25+
>> [Mike Mogosanu](http://blog.sapiensworks.com/post/2014/06/04/Unit-Of-Work-is-the-new-Singleton.aspx/)
26+
27+
If your case falls within the 1% case, write an decorator for the `ICommandBus`
28+
that starts a transaction, use MSSQL as event store and make sure your read
29+
models are stored in MSSQL as well.

Documentation/RabbitMQ.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# RabbitMQ
2+
3+
Configuring EventFlow to publish events to [RabbitMQ](http://www.rabbitmq.com/)
4+
is simple, just install the NuGet package `EventFlow.RabbitMQ` and add this to
5+
your EventFlow setup.
6+
7+
```csharp
8+
var uri = new Uri("amqp://localhost");
9+
10+
var resolver = EventFlowOptions.with
11+
.PublishToRabbitMq(RabbitMqConfiguration.With(uri))
12+
...
13+
.CreateResolver();
14+
```
15+
16+
Events are published to a exchange named `eventflow` with routing keys in the
17+
following format.
18+
19+
```
20+
eventflow.domainevent.[Aggregate name].[Event name].[Event version]
21+
```
22+
23+
Which will be the following for an event named `CreateUser` version `1` for the
24+
`MyUserAggregate`.
25+
26+
```
27+
eventflow.domainevent.my-user.create-user.1
28+
```
29+
30+
Note the lowercasing and adding of `-` whenever there's a capital letter.
31+
32+
All the above is the default behavior, if you don't like it replace e.g. the
33+
service `IRabbitMqMessageFactory` to customize what routing key or exchange to
34+
use. Have a look at how [EventFlow](https://github.com/rasmus/EventFlow) has
35+
done its implementation to get started.

EventFlow.sln

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 2013
4-
VisualStudioVersion = 12.0.31101.0
3+
# Visual Studio 14
4+
VisualStudioVersion = 14.0.23107.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow", "Source\EventFlow\EventFlow.csproj", "{11131251-778D-4D2E-BDD1-4844A789BCA9}"
77
EndProject
@@ -33,6 +33,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.EventStores.Event
3333
EndProject
3434
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.EventStores.EventStore.Tests", "Source\EventFlow.EventStores.EventStore.Tests\EventFlow.EventStores.EventStore.Tests.csproj", "{BC4F0E41-6659-4D6D-9D25-1558CBA1649B}"
3535
EndProject
36+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RabbitMQ", "RabbitMQ", "{7951DC73-5DAF-4322-9AF0-099BF5C90837}"
37+
EndProject
38+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.RabbitMQ", "Source\EventFlow.RabbitMQ\EventFlow.RabbitMQ.csproj", "{4B06F01F-ACE6-489D-A92A-012F533EFA3C}"
39+
EndProject
40+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.RabbitMQ.Tests", "Source\EventFlow.RabbitMQ.Tests\EventFlow.RabbitMQ.Tests.csproj", "{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}"
41+
EndProject
42+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.Autofac.Tests", "Source\EventFlow.Autofac.Tests\EventFlow.Autofac.Tests.csproj", "{EDCD8854-6224-4329-87C2-9ADD7D153070}"
43+
EndProject
44+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Autofac", "Autofac", "{980EEDAA-1FEF-4D7C-8811-5EF1D9729773}"
45+
EndProject
3646
Global
3747
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3848
Debug|Any CPU = Debug|Any CPU
@@ -87,6 +97,18 @@ Global
8797
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B}.Debug|Any CPU.Build.0 = Debug|Any CPU
8898
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B}.Release|Any CPU.ActiveCfg = Release|Any CPU
8999
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B}.Release|Any CPU.Build.0 = Release|Any CPU
100+
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
101+
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
102+
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
103+
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Release|Any CPU.Build.0 = Release|Any CPU
104+
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
105+
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Debug|Any CPU.Build.0 = Debug|Any CPU
106+
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Release|Any CPU.ActiveCfg = Release|Any CPU
107+
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Release|Any CPU.Build.0 = Release|Any CPU
108+
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
109+
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Debug|Any CPU.Build.0 = Debug|Any CPU
110+
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Release|Any CPU.ActiveCfg = Release|Any CPU
111+
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Release|Any CPU.Build.0 = Release|Any CPU
90112
EndGlobalSection
91113
GlobalSection(SolutionProperties) = preSolution
92114
HideSolutionNode = FALSE
@@ -98,7 +120,11 @@ Global
98120
{A6F6232B-764F-4428-9EB5-CC98BE4F5E90} = {E4FC24C0-3EB3-4203-B4F2-0B534B42574A}
99121
{EE6F7B78-3EF1-488F-B90A-8E7F350B7D51} = {9876C758-0A72-400E-A1B1-685E1C22ACB2}
100122
{2F3A5BCA-5336-4BB1-BA3D-0FEEA78C0415} = {9876C758-0A72-400E-A1B1-685E1C22ACB2}
123+
{26F06682-3364-4C22-B9B2-2F2653D0BE0D} = {980EEDAA-1FEF-4D7C-8811-5EF1D9729773}
101124
{E42A253D-2011-4799-B55D-1D0C61E171C2} = {F6D62A27-50EA-4846-8F36-F3D36F52DCA6}
102125
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B} = {F6D62A27-50EA-4846-8F36-F3D36F52DCA6}
126+
{4B06F01F-ACE6-489D-A92A-012F533EFA3C} = {7951DC73-5DAF-4322-9AF0-099BF5C90837}
127+
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A} = {7951DC73-5DAF-4322-9AF0-099BF5C90837}
128+
{EDCD8854-6224-4329-87C2-9ADD7D153070} = {980EEDAA-1FEF-4D7C-8811-5EF1D9729773}
103129
EndGlobalSection
104130
EndGlobal

README.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
# EventFlow
22

3+
[![Join the chat at https://gitter.im/rasmus/EventFlow](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/rasmus/EventFlow?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4+
35
[![NuGet Status](http://img.shields.io/nuget/v/EventFlow.svg?style=flat)](https://www.nuget.org/packages/EventFlow/)
46
[![Build status](https://ci.appveyor.com/api/projects/status/51yvhvbd909e4o82/branch/develop?svg=true)](https://ci.appveyor.com/project/rasmusnu/eventflow)
57
[![License](https://img.shields.io/github/license/rasmus/eventflow.svg)](./LICENSE)
68

79
EventFlow is a basic CQRS+ES framework designed to be easy to use.
810

9-
Have a look at our [Getting started guide](./Documentation/GettingStarted.md).
11+
Have a look at our [getting started guide](./Documentation/GettingStarted.md),
12+
the [dos and don'ts](./Documentation/DoesAndDonts.md) and the
13+
[FAQ](./Documentation/FAQ.md).
1014

1115
### Features
1216

@@ -17,8 +21,8 @@ Have a look at our [Getting started guide](./Documentation/GettingStarted.md).
1721
* **Highly configurable and extendable**
1822
* **Easy to use**
1923
* **No use of threads or background workers making it "web friendly"**
20-
* **Cancellation:** All methods that does IO work or might delay execution,
21-
takes a `CancellationToken` argument to allow you to cancel the operation
24+
* **Cancellation:** All methods that does IO work or might delay execution (due to
25+
retries), takes a `CancellationToken` argument to allow you to cancel the operation
2226

2327
### Overview
2428

@@ -40,11 +44,14 @@ to the documentation.
4044
read model storage types.
4145
* In-memory - only for test
4246
* Microsoft SQL Server
43-
* [**Queries**](./Documentation/Queries.md): Value objects that represent
47+
* [**Queries:**](./Documentation/Queries.md): Value objects that represent
4448
a query without specifying how its executed, that is let to a query handler
45-
* [**Event upgrade**](./Documentation/EventUpgrade.md): As events committed to
46-
the event store is never changed, EventFlow uses the concept of event upgraders
47-
to deprecate events and replace them with new during aggregate load.
49+
* [**Event upgrade:**](./Documentation/EventUpgrade.md): As events committed to
50+
the event store is never changed, EventFlow uses the concept of event
51+
upgraders to deprecate events and replace them with new during aggregate load.
52+
* **Event publishing:** Sometimes you want other applications or services to
53+
consume and act on domains. For this EventFlow supports event publishing.
54+
* [RabbitMQ](./Documentation/RabbitMQ.md)
4855
* [**Metadata**](./Documentation/Metadata.md):
4956
Additional information for each aggregate event, e.g. the IP of
5057
the user behind the event being emitted. EventFlow ships with
@@ -128,3 +135,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
128135
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
129136
SOFTWARE.
130137
```
138+

RELEASE_NOTES.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,27 @@
1-
### New in 0.10 (not released yet)
1+
### New in 0.11 (not released yet)
2+
3+
* Breaking: `EventFlowOptions.AddDefaults(...)` now also adds event
4+
definitions
5+
* New: [RabbitMQ](http://www.rabbitmq.com/) is now supported through the new
6+
NuGet package called `EventFlow.RabbitMQ` which enables domain events to be
7+
published to the bus
8+
* New: If you want to subscribe to all domain events, you can implement
9+
and register a service that implements `ISubscribeSynchronousToAll`. Services
10+
that implement this will automatically be added using the
11+
`AddSubscribers(...)` or `AddDefaults(...)` extension to `EventFlowOptions`
12+
* New: Use `EventFlowOptions.UseAutofacAggregateRootFactory(...)` to use an
13+
Autofac aggregate root factory, enabling you to use services in your
14+
aggregate root constructor
15+
* New: Use `EventFlowOptions.UseResolverAggregateRootFactory()` to use the
16+
resolver to create aggregate roots. Same as
17+
`UseAutofacAggregateRootFactory(...)` but for when using the internal IoC
18+
container
19+
* New: Use `EventFlowOptions.AddAggregateRoots(...)` to register aggregate root
20+
types
21+
* New: Use `IServiceRegistration.RegisterType(...)` to register services by
22+
type
23+
24+
### New in 0.10.642 (released 2015-08-17)
225

326
* Breaking: Updated NuGet reference `Newtonsoft.Json` to v7.0.1
427
(up from v6.0.8)

0 commit comments

Comments
 (0)