Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rasmus committed Nov 19, 2015
2 parents 876228f + 0a40942 commit e0251c7
Show file tree
Hide file tree
Showing 478 changed files with 5,732 additions and 3,147 deletions.
3 changes: 0 additions & 3 deletions Documentation/Images/logo_resharper.png

This file was deleted.

31 changes: 0 additions & 31 deletions Documentation/ReadStores-Elasticsearch.md

This file was deleted.

170 changes: 170 additions & 0 deletions Documentation/ReadStores.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Read model stores

In order to create query handlers that perform and enable them search across
multiple fields, read models or projections are used.

Read models are a flattened views of a subset or all aggregate domain events
created specifically for efficient queries.

Here's a simple example of how a read model for doing searches for usernames
could look. The read model handles the `UserCreated` domain event event to get
the username and user ID.

```csharp
public class UserReadModel : IReadModel,
IAmReadModelFor<UserAggregate, UserId, UserCreated>
{
public string UserId { get; set; }
public string Username { get; set; }

public void Apply(
IReadModelContext context,
IDomainEvent<UserAggregate, UserId, UserCreated> domainEvent)
{
UserId = domainEvent.AggregateIdentity.Value;
Username = domainEvent.AggregateEvent.Username.Value;
}
}
```

## Read model locators

Typically the ID of read models are the aggregate identity, but sometimes this
isn't the case. Here are some examples.

- Items from a collection on the aggregate root
- Deterministic ID created from event data
- Entity within the aggregate

To create read models in these cases, use the EventFlow concept of read model
locators, which is basically a mapping from a domain event to a read model ID.

As an example, consider if we could add several nicknames to a user. We might
have a domain event called `UserNicknameAdded` similar to this.

```csharp
public class UserNicknameAdded : AggregateEvent<UserAggregate, UserId>
{
public Nickname Nickname { get; set; }
}
```

We could then create a read model locator that would return the ID for each
nickname we add via the event like this.

```csharp
public class UserNicknameReadModelLocator : IReadModelLocator
{
public IEnumerable<string> GetReadModelIds(IDomainEvent domainEvent)
{
var userNicknameAdded = domainEvent as
IDomainEvent<UserAggregate, UserId, UserNicknameAdded>;
if (userNicknameAdded == null)
{
yield break;
}

yield return userNicknameAdded.Nickname.Id;
}
}
```

And then use a read model similar to this that represent each nickname.

```csharp
public class UserNicknameReadModel : IReadModel,
IAmReadModelFor<UserAggregate, UserId, UserNicknameAdded>
{
public string UserId { get; set; }
public string Nickname { get; set; }

public void Apply(
IReadModelContext context,
IDomainEvent<UserAggregate, UserId, UserCreated> domainEvent)
{
UserId = domainEvent.AggregateIdentity.Value;
Nickname = domainEvent.AggregateEvent.Nickname.Value;
}
}
```

We could then use this nickname read model to query all the nicknames for a
given user by search for read models that have a specific `UserId`.

## Read store implementations

EventFlow has built-in support for several different read model stores.

### In-memory

The in-memory read store is easy to use and easy to configure. All read models
are stored in-memory, so if EventFlow is restarted all read models are lost.

To configure the in-memory read model store, simply call
`UseInMemoryReadStoreFor<>` or `UseInMemoryReadStoreFor<,>` with your read
model as the generic argument.

```csharp
var resolver = EventFlowOptions.New
...
.UseInMemoryReadStoreFor<UserReadModel>()
.UseInMemoryReadStoreFor<UserNicknameReadModel,UserNicknameReadModelLocator>()
...
.CreateResolver();
```

### Microsoft SQL Server

To configure the MSSQL read model store, simply call
`UseMssqlReadModel<>` or `UseMssqlReadModel<,>` with your read
model as the generic argument.

```csharp
var resolver = EventFlowOptions.New
...
.UseMssqlReadModel<UserReadModel>()
.UseMssqlReadModel<UserNicknameReadModel,UserNicknameReadModelLocator>()
...
.CreateResolver();
```

By convention, EventFlow uses the table named `ReadModel-[CLASS NAME]` as the
table to store the read models rows in. If you need to change this, use the
`Table` from the `System.ComponentModel.DataAnnotations.Schema` namespace. So
in the above example, the read model `UserReadModel` would be stored in
a table called `ReadModel-UserReadModel` unless stated otherwise.

To allow EventFlow to find the read models stored, a single column is required
to have the `MsSqlReadModelIdentityColumn` attribute. This will be used to
store the read model ID.

You should also create a `int` column that has the `MsSqlReadModelVersionColumn`
attribute to tell EventFlow which column is used to store the read model version
in.

### Elasticsearch

To configure the [Elasticsearch](https://www.elastic.co/products/elasticsearch)
read model store, simply call `UseElasticsearchReadModel<>` or
`UseElasticsearchReadModel<,>` with your read model as the generic argument.

```csharp
var resolver = EventFlowOptions.New
...
.ConfigureElasticsearch(new Uri("http://localhost:9200/"))
...
.UseElasticsearchReadModel<UserReadModel>()
.UseElasticsearchReadModel<UserNicknameReadModel,UserNicknameReadModelLocator>()
...
.CreateResolver();
```

Overloads of `ConfigureElasticsearch(...)` is available for alternative
Elasticsearch configurations.

Make sure to create any mapping the read model requires in Elasticsearch
_before_ using the read model in EventFlow.

If you want to control the index a specific read model is stored in, create
create an implementation of `IReadModelDescriptionProvider` and register it
in the [EventFlow IoC](./Customize.md).
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The MIT License (MIT)

Copyright (c) 2015 Rasmus Mikkelsen
Copyright (c) 2015 eBay Software Foundation
https://github.com/rasmus/EventFlow

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ the [dos and don'ts](./Documentation/DoesAndDonts.md) and the

### Examples

* **[Simple](#simple example):** Shows the key concepts of EventFlow in a few
* **[Simple](#simple-example):** Shows the key concepts of EventFlow in a few
lines of code
* **Shipping:** To get a more complete example of how EventFlow _could_ be used,
have a look at the shipping example found here in the code base. The example
Expand All @@ -69,12 +69,12 @@ to the documentation.
* Files - only for test
* [Microsoft SQL Server](./Documentation/EventStores-MSSQL.md)
* EventStore - only for test (for now) [home page](https://geteventstore.com/)
* **Read models:** Denormalized representation of aggregate events
optimized for reading fast. Currently there is support for these
read model storage types.
* [Elasticsearch](./Documentation/ReadStores-Elasticsearch.md)
* In-memory - only for test
* Microsoft SQL Server
* [**Read models:**](./Documentation/ReadStores.md) Denormalized representation
of aggregate events optimized for reading fast. Currently there is support for
these read model storage types.
* [Elasticsearch](./Documentation/ReadStores.md#elasticsearch)
* [In-memory](./Documentation/ReadStores.md#in-memory) - only for test
* [Microsoft SQL Server](Documentation/ReadStores.md#microsoft-sql-server)
* [**Queries:**](./Documentation/Queries.md) Value objects that represent
a query without specifying how its executed, that is let to a query handler
* [**Jobs:**](./Documentation/Jobs.md) Perform scheduled tasks at a later time,
Expand Down Expand Up @@ -125,7 +125,8 @@ using (var resolver = EventFlowOptions.New
}
```

Note: `.ConfigureAwait(false)` omitted in above example.
Note: `.ConfigureAwait(false)` and use of `CancellationToken` is omitted in
the above example to ease reading.

## State of EventFlow

Expand Down Expand Up @@ -197,15 +198,16 @@ several areas that you could help out with.

## Thanks

[![ReSharper](./Documentation/Images/logo_resharper.png)](https://www.jetbrains.com/resharper/)

* [JetBrains](https://www.jetbrains.com/resharper/): OSS licenses
* [iconmonstr](http://iconmonstr.com/network-6-icon/): Free icons for EventFlow

## License

```
The MIT License (MIT)
Copyright (c) 2015 Rasmus Mikkelsen
Copyright (c) 2015 eBay Software Foundation
https://github.com/rasmus/EventFlow
Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
26 changes: 25 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
### New in 0.21 (not released yet)
### New in 0.22 (not released yet)

* New: To customize how a specific read model is initially created, implement
a specific `IReadModelFactory<>` that can bootstrap that read model
* New: How EventFlow handles MSSQL read models has been refactored to allow
significantly more freedom to developers. MSSQL read models are no longer
required to implement `IMssqlReadModel`, only the empty `IReadModel`
interface. Effectively, this means that no specific columns are required,
meaning that the following columns are no longer enforced on MSSQL read
models. Use the new required `MsSqlReadModelIdentityColumn` attribute to mark
the identity column and the optional (but recommended)
`MsSqlReadModelVersionColumn` to mark the version column.
- `string AggregateId`
- `DateTimeOffset CreateTime`
- `DateTimeOffset UpdatedTime`
- `int LastAggregateSequenceNumber`
* Obsolete: `IMssqlReadModel` and `MssqlReadModel`. Developers should instead
use the `MsSqlReadModelIdentityColumn` and `MsSqlReadModelVersionColumn`
attributes to mark the identity and version columns (read above).
EventFlow will continue to support `IMssqlReadModel`, but it _will_ be
removed at some point in the future
* Fixed: Added missing `UseElasticsearchReadModel<TReadModel, TReadModelLocator>()`
extension

### New in 0.21.1312 (released 2015-10-26)

* New: Added `Identity<>.NewComb()` that creates sequential unique IDs which can
be used to minimize database fragmentation
Expand Down
25 changes: 24 additions & 1 deletion Source/EventFlow.Autofac.Tests/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
using System.Reflection;
// The MIT License (MIT)
//
// Copyright (c) 2015 Rasmus Mikkelsen
// Copyright (c) 2015 eBay Software Foundation
// https://github.com/rasmus/EventFlow
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.
//
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

Expand Down
Loading

0 comments on commit e0251c7

Please sign in to comment.