Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rasmus committed Aug 24, 2015
2 parents f97b05e + 6e4dc6a commit f2d58ac
Show file tree
Hide file tree
Showing 79 changed files with 2,716 additions and 105 deletions.
16 changes: 13 additions & 3 deletions Documentation/Aggregates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

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

```csharp
public class TestId : Identity<TestId>
Expand All @@ -14,7 +14,17 @@ public class TestId : Identity<TestId>
}
```

Note that its important to call the constructor argument for `value` as
The `Identity<>` value object provides generic functionality to create and
validate aggregate root IDs.

- IDs follow the form `{class without "Id"}-{guid}` e.g.
`test-c93fdb8c-5c9a-4134-bbcd-87c0644ca34f` for the above `TestId`
- IDs can be generated using the static `New` property
- IDs can be validated using the static `bool IsValid(string)` method
- ID validation errors (if any) can be gathered using the static
`IEnumerable<string> Validate(string)` method

Note that its important to _name_ the constructor argument `value` as
its significant if you serialize the ID.

Next, to create a new aggregate, simply inherit from `AggregateRoot<,>` like
Expand Down
29 changes: 28 additions & 1 deletion Documentation/Customize.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
When ever EventFlow doesn't meet your needs, e.g. if you want to collect
statistics on each command execution time, you can customize EventFlow.

You have two options
Basically EventFlow relies on an IoC container to allow developers to customize
the different parts of EventFlow.

Note: Read the section "Changing IoC container" for details on how to change
the IoC container used if you have specific needs like e.g. integrating
EventFlow into an Owin application.

You have two options for when you want to customize EventFlow

* Decorate an implementation
* Replace an implementation
Expand Down Expand Up @@ -53,3 +60,23 @@ A example of a service that you might be interested in creating your own
custom implementation of is `IAggregateFactory` which handles all aggregate
creation, enabling you to pass additional services to a aggregate upon
creation before events are applied.

## Changing IoC container

EventFlow provides the NuGet package `EventFlow.Autofac` that allows you
to set the internal `ContainerBuilder` used during EventFlow initialization.

Pass the `ContainerBuilder` to EventFlow and call `CreateContainer()` when
configuration is done to create the container.

```csharp
var containerBuilder = new ContainerBuilder();

var container = EventFlowOptions.With
.UseAutofacContainerBuilder(containerBuilder) // Must be the first line!
...
.CreateContainer();
```

Maybe call `UseAutofacAggregateRootFactory()` just before the
`CreateContainer()` to use the Autofac aggregate root factory.
34 changes: 34 additions & 0 deletions Documentation/DoesAndDonts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Does and Don'ts
Whenever creating an application that uses CQRS+ES there are several things
you need to keep in mind to make it easier and minimize the potential bugs.
This guide will give you some details on typical problems and how EventFlow
can help you minimize the risk.

## Events

#### Produce clean JSON
Make sure that when your aggregate events are JSON serialized, they produce
clean JSON as it makes it easier to work with and enable you to easier
deserialize the events in the future.

- No type information
- No hints of value objects (see [value objects](ValueObjects.md))

Here's an example of good clean event JSON produced from a create user event.

```JSON
{
"Username": "root",
"PasswordHash": "1234567890ABCDEF",
"EMail": "[email protected]",
}
```

#### Keep old event types
Keep in mind, that you need to keep the event types in your code for as long as
these events are in the event source, which in most cases are _forever_ as
storage is cheap and information, i.e., your domain events, is expensive.

However, you should still clear your code, have a look at how you can
[upgrade and version your events](./EventUpgrade.md) for details on how
EventFlow supports you in this.
29 changes: 29 additions & 0 deletions Documentation/FAQ.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# FAQ - frequently asked questions

#### Why isn't there a "global sequence number" on domain events?

While this is easy to support in some event stores like MSSQL, it doesn't
really make sense from a domain perspective. Greg Young also has this to say
on the subject:

> Order is only assured per a handler within an aggregate root
> boundary. There is no assurance of order between handlers or
> between aggregates. Trying to provide those things leads to
> the dark side.
>> [Greg Young](https://groups.yahoo.com/neo/groups/domaindrivendesign/conversations/topics/18453)
#### Why doesn't EventFlow have a unit of work concept?

Short answer, you shouldn't need it. But Mike has a way better answer:

> In the Domain, everything flows in one direction: forward. When something bad
> happens, a correction is applied. The Domain doesn't care about the database
> and UoW is very coupled to the db. In my opinion, it's a pattern which is
> usable only with data access objects, and in probably 99% of the cases you
> won't be needing it. As with the Singleton, there are better ways but
> everything depends on proper domain design.
>> [Mike Mogosanu](http://blog.sapiensworks.com/post/2014/06/04/Unit-Of-Work-is-the-new-Singleton.aspx/)
If your case falls within the 1% case, write an decorator for the `ICommandBus`
that starts a transaction, use MSSQL as event store and make sure your read
models are stored in MSSQL as well.
35 changes: 35 additions & 0 deletions Documentation/RabbitMQ.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# RabbitMQ

Configuring EventFlow to publish events to [RabbitMQ](http://www.rabbitmq.com/)
is simple, just install the NuGet package `EventFlow.RabbitMQ` and add this to
your EventFlow setup.

```csharp
var uri = new Uri("amqp://localhost");

var resolver = EventFlowOptions.with
.PublishToRabbitMq(RabbitMqConfiguration.With(uri))
...
.CreateResolver();
```

Events are published to a exchange named `eventflow` with routing keys in the
following format.

```
eventflow.domainevent.[Aggregate name].[Event name].[Event version]
```

Which will be the following for an event named `CreateUser` version `1` for the
`MyUserAggregate`.

```
eventflow.domainevent.my-user.create-user.1
```

Note the lowercasing and adding of `-` whenever there's a capital letter.

All the above is the default behavior, if you don't like it replace e.g. the
service `IRabbitMqMessageFactory` to customize what routing key or exchange to
use. Have a look at how [EventFlow](https://github.com/rasmus/EventFlow) has
done its implementation to get started.
30 changes: 28 additions & 2 deletions EventFlow.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow", "Source\EventFlow\EventFlow.csproj", "{11131251-778D-4D2E-BDD1-4844A789BCA9}"
EndProject
Expand Down Expand Up @@ -33,6 +33,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.EventStores.Event
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RabbitMQ", "RabbitMQ", "{7951DC73-5DAF-4322-9AF0-099BF5C90837}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.RabbitMQ", "Source\EventFlow.RabbitMQ\EventFlow.RabbitMQ.csproj", "{4B06F01F-ACE6-489D-A92A-012F533EFA3C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.RabbitMQ.Tests", "Source\EventFlow.RabbitMQ.Tests\EventFlow.RabbitMQ.Tests.csproj", "{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.Autofac.Tests", "Source\EventFlow.Autofac.Tests\EventFlow.Autofac.Tests.csproj", "{EDCD8854-6224-4329-87C2-9ADD7D153070}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Autofac", "Autofac", "{980EEDAA-1FEF-4D7C-8811-5EF1D9729773}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -87,6 +97,18 @@ Global
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B}.Release|Any CPU.Build.0 = Release|Any CPU
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B06F01F-ACE6-489D-A92A-012F533EFA3C}.Release|Any CPU.Build.0 = Release|Any CPU
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A}.Release|Any CPU.Build.0 = Release|Any CPU
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EDCD8854-6224-4329-87C2-9ADD7D153070}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -98,7 +120,11 @@ Global
{A6F6232B-764F-4428-9EB5-CC98BE4F5E90} = {E4FC24C0-3EB3-4203-B4F2-0B534B42574A}
{EE6F7B78-3EF1-488F-B90A-8E7F350B7D51} = {9876C758-0A72-400E-A1B1-685E1C22ACB2}
{2F3A5BCA-5336-4BB1-BA3D-0FEEA78C0415} = {9876C758-0A72-400E-A1B1-685E1C22ACB2}
{26F06682-3364-4C22-B9B2-2F2653D0BE0D} = {980EEDAA-1FEF-4D7C-8811-5EF1D9729773}
{E42A253D-2011-4799-B55D-1D0C61E171C2} = {F6D62A27-50EA-4846-8F36-F3D36F52DCA6}
{BC4F0E41-6659-4D6D-9D25-1558CBA1649B} = {F6D62A27-50EA-4846-8F36-F3D36F52DCA6}
{4B06F01F-ACE6-489D-A92A-012F533EFA3C} = {7951DC73-5DAF-4322-9AF0-099BF5C90837}
{BC96BEAE-E84E-4C51-B66D-DA1F43EAD54A} = {7951DC73-5DAF-4322-9AF0-099BF5C90837}
{EDCD8854-6224-4329-87C2-9ADD7D153070} = {980EEDAA-1FEF-4D7C-8811-5EF1D9729773}
EndGlobalSection
EndGlobal
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# EventFlow

[![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)

[![NuGet Status](http://img.shields.io/nuget/v/EventFlow.svg?style=flat)](https://www.nuget.org/packages/EventFlow/)
[![Build status](https://ci.appveyor.com/api/projects/status/51yvhvbd909e4o82/branch/develop?svg=true)](https://ci.appveyor.com/project/rasmusnu/eventflow)
[![License](https://img.shields.io/github/license/rasmus/eventflow.svg)](./LICENSE)

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

Have a look at our [Getting started guide](./Documentation/GettingStarted.md).
Have a look at our [getting started guide](./Documentation/GettingStarted.md),
the [dos and don'ts](./Documentation/DoesAndDonts.md) and the
[FAQ](./Documentation/FAQ.md).

### Features

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

### Overview

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

25 changes: 24 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
### New in 0.10 (not released yet)
### New in 0.11 (not released yet)

* Breaking: `EventFlowOptions.AddDefaults(...)` now also adds event
definitions
* New: [RabbitMQ](http://www.rabbitmq.com/) is now supported through the new
NuGet package called `EventFlow.RabbitMQ` which enables domain events to be
published to the bus
* New: If you want to subscribe to all domain events, you can implement
and register a service that implements `ISubscribeSynchronousToAll`. Services
that implement this will automatically be added using the
`AddSubscribers(...)` or `AddDefaults(...)` extension to `EventFlowOptions`
* New: Use `EventFlowOptions.UseAutofacAggregateRootFactory(...)` to use an
Autofac aggregate root factory, enabling you to use services in your
aggregate root constructor
* New: Use `EventFlowOptions.UseResolverAggregateRootFactory()` to use the
resolver to create aggregate roots. Same as
`UseAutofacAggregateRootFactory(...)` but for when using the internal IoC
container
* New: Use `EventFlowOptions.AddAggregateRoots(...)` to register aggregate root
types
* New: Use `IServiceRegistration.RegisterType(...)` to register services by
type

### New in 0.10.642 (released 2015-08-17)

* Breaking: Updated NuGet reference `Newtonsoft.Json` to v7.0.1
(up from v6.0.8)
Expand Down
Loading

0 comments on commit f2d58ac

Please sign in to comment.