Skip to content

Commit cbb45fa

Browse files
committed
Merge branch 'develop'
2 parents f2d58ac + 4e57c4d commit cbb45fa

138 files changed

Lines changed: 2007 additions & 422 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/Commands.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Commands
2+
3+
Commands are the basic value objects, or models, that represent write operations
4+
that you can perform in your domain.
5+
6+
As an example, one might implement create this command for updating user
7+
passwords.
8+
9+
```csharp
10+
public class UserUpdatePasswordCommand : Command<UserAggregate, UserId>
11+
{
12+
public Password NewPassword { get; private set; }
13+
public Password OldPassword { get; private set; }
14+
15+
public UserUpdatePasswordCommand(
16+
UserId id,
17+
Password newPassword,
18+
Password oldPassword)
19+
: base(id)
20+
{
21+
Username = username;
22+
Password = password;
23+
}
24+
}
25+
```
26+
27+
Note that the `Password` class is merely a value object created to hold the
28+
password and do basic validation. Read the article regarding
29+
[value objects](./ValueObjects.md) for more information. Also, you don't
30+
have to use the default EventFlow `Command<,>` implementation, you can create
31+
your own, it merely have to implement the `ICommand<,>` interface.
32+
33+
A command by itself doesn't do anything and will throw an exception if
34+
published. To make a command work, you need to implement one (and only one)
35+
command handler which is responsible for invoking the aggregate.
36+
37+
```csharp
38+
public class UserUpdatePasswordCommandHandler :
39+
CommandHandler<UserAggregate, UserId, UserUpdatePasswordCommand>
40+
{
41+
public override Task ExecuteAsync(
42+
UserAggregate aggregate,
43+
UserUpdatePasswordCommand command,
44+
CancellationToken cancellationToken)
45+
{
46+
aggregate.UpdatePassword(
47+
command.OldPassword,
48+
command.NewPassword);
49+
return Task.FromResult(0);
50+
}
51+
}
52+
```
53+
54+
## Ensure idempotency
55+
56+
Detecting duplicate operations can be hard, especially if you have a
57+
distributed application, or simply a web application. Consider the following
58+
simplified scenario.
59+
60+
1. The user wants to change his password
61+
1. The user fills in the "change password form"
62+
1. As user is impatient, or by accident, the user submits the for twice
63+
1. The first web request completes and the password is changed. However, as
64+
the browser is waiting on the first web request, this result is ignored
65+
1. The second web request throws a domain error as the "old password" doesn't
66+
match as the current password has already been changed
67+
1. The user is presented with a error on the web page
68+
69+
Handling this is simple, merely ensure that the aggregate is idempotent
70+
is regards to password changes. But instead of implementing this yourself,
71+
EventFlow has support for it and its simple to utilize and is done per
72+
command.
73+
74+
To use the functionality, merely ensure that commands that represent the
75+
same operation has the same `ISourceId` which implements `IIdentity` like
76+
the example blow.
77+
78+
```csharp
79+
public class UserUpdatePasswordCommand : Command<UserAggregate, UserId>
80+
{
81+
public Password NewPassword { get; private set; }
82+
public Password OldPassword { get; private set; }
83+
84+
public UserCreateCommand(
85+
UserId id,
86+
ISourceId sourceId,
87+
Password newPassword,
88+
Password oldPassword)
89+
: base(id, sourceId)
90+
{
91+
Username = username;
92+
Password = password;
93+
}
94+
}
95+
```
96+
97+
Note the use of the other `protected` constructor of `Command<,>` that
98+
takes a `ISourceId` in addition to the aggregate root identity.
99+
100+
If a duplicate command is detected, a `DuplicateOperationException` is thrown.
101+
The application could then ignore the exception or report the problem to the
102+
end user.
103+
104+
The default `ISourceId` history size of the aggregate root, is ten. But it can
105+
be configured using the `SetSourceIdHistory(...)` that must be called from
106+
within the aggregate root constructor.
107+
108+
109+
### Easier ISourceId calculation
110+
111+
Ensuring the correct calculation of the command `ISourceId` can be somewhat
112+
cumbersome, which is why EventFlow provides another base command you can use,
113+
the `DistinctCommand<,>`. By using the `DistinctCommand<,>` you merely have
114+
to implement the `GetSourceIdComponents()` and providing the
115+
`IEnumerable<byte[]>` that makes the command unique. The bytes is used to
116+
create a deterministic GUID to be used as an `ISourceId`.
117+
118+
```csharp
119+
public class UserUpdatePasswordCommand :
120+
DistinctCommand<UserAggregate, UserId>
121+
{
122+
public Password NewPassword { get; private set; }
123+
public Password OldPassword { get; private set; }
124+
125+
public UserUpdatePasswordCommand(
126+
UserId id,
127+
Password newPassword,
128+
Password oldPassword)
129+
: base(id)
130+
{
131+
Username = username;
132+
Password = password;
133+
}
134+
135+
protected override IEnumerable<byte[]> GetSourceIdComponents()
136+
{
137+
yield return NewPassword.GetBytes();
138+
yield return OldPassword.GetBytes();
139+
}
140+
}
141+
```
142+
143+
The `GetBytes()` merely returns the `Encoding.UTF8.GetBytes(...)` of the
144+
password.
145+
146+
Its important that you don't use the `GetHashCode()`, as the implementation
147+
is different for e.g. `string` on 32 bit and 64 bit .NET.

Documentation/GettingStarted.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Implementation notes
1212
* Make sure to read the comments about how this code should be improved at
1313
the bottom
1414

15+
1516
## Create an aggregate
1617

1718
Initially you need to create the object representing the _identity_
@@ -21,16 +22,14 @@ started.
2122
```csharp
2223
public class UserId : Identity<UserId>
2324
{
24-
public UserId(string value) : base(value)
25-
{
26-
}
25+
public UserId(string value) : base(value) { }
2726
}
2827
```
2928

3029
Next, let us start by creating a aggregate to represent our users.
3130

3231
```csharp
33-
public class UserAggregate : AggregateRoot<UserAggregate>
32+
public class UserAggregate : AggregateRoot<UserAggregate, UserId>
3433
{
3534
public UserAggregate(UserId id)
3635
: base(id)
@@ -39,10 +38,11 @@ public class UserAggregate : AggregateRoot<UserAggregate>
3938
}
4039
```
4140

41+
4242
## Create event
4343

4444
```csharp
45-
public class UserCreatedEvent : AggregateEvent<UserAggregate>
45+
public class UserCreatedEvent : AggregateEvent<UserAggregate, UserId>
4646
{
4747
public string Username { get; private set; }
4848
public string Password { get; private set; }
@@ -63,6 +63,7 @@ Important notes regarding events
6363
emitted a event, you should never change it. You can deprecate
6464
it, but you should never change the data stored in the event store
6565

66+
6667
## Update aggregate
6768

6869
We update our aggregate by creating a new method called `Create(...)` that
@@ -113,14 +114,15 @@ public class UserAggregate : AggregateRoot<UserAggregate, UserId>,
113114
}
114115
```
115116

117+
116118
## Create command
117119

118120
Even though it is possible, we are not allowed to call the newly
119121
created `Create` method on our `UserAggregate`. The call must be
120122
made from a command handler, and thus we first create the command.
121123

122124
```csharp
123-
public class UserCreateCommand : Command<UserAggregate>
125+
public class UserCreateCommand : Command<UserAggregate, UserId>
124126
{
125127
public string Username { get; private set; }
126128
public string Password { get; private set; }
@@ -137,13 +139,18 @@ public class UserCreateCommand : Command<UserAggregate>
137139
}
138140
```
139141

142+
Note that you can read the article regarding [commands](./Commands.md) for
143+
more details, e.g. on ensuring idempotency in a distributed application.
144+
145+
140146
## Create command handler
141147

142148
Next we create the command handler that invokes the aggregate with the command
143149
arguments.
144150

145151
```csharp
146-
public class UserCreateCommand : ICommand<UserAggregate, UserCreateCommand>
152+
public class UserCreateCommand :
153+
ICommand<UserAggregate, UserId, UserCreateCommand>
147154
{
148155
public Task ExecuteAsync(
149156
UserAggregate aggregate,
@@ -179,5 +186,5 @@ await _commandBus.PublishAsync(command, cancellationToken);
179186

180187
There are several areas the code can be improved.
181188

182-
- Use value objects for e.g. username and password that validate the value,
183-
i.e., ensure that the username isn't the empty string
189+
- Use [value objects](ValueObjects.md) for e.g. username and password that
190+
validate the value, i.e., ensure that the username isn't the empty string

README.md

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ the [dos and don'ts](./Documentation/DoesAndDonts.md) and the
2929
Here is a list of the EventFlow concepts. Use the links to navigate
3030
to the documentation.
3131

32-
* [**Aggregates**](./Documentation/Aggregates.md): Domains object
32+
* [**Aggregates:**](./Documentation/Aggregates.md) Domains object
3333
that guarantees the consistency of changes being made within
3434
each aggregate
35-
* **Command bus:** Entry point for all command execution.
35+
* [**Command bus and commands:**](./Documentation/Commands.md)
36+
Entry point for all command/operation execution.
3637
* **Event store:** Storage of the event stream for aggregates.
3738
Currently there is support for these storage types.
3839
* In-memory - only for test
@@ -44,21 +45,21 @@ to the documentation.
4445
read model storage types.
4546
* In-memory - only for test
4647
* Microsoft SQL Server
47-
* [**Queries:**](./Documentation/Queries.md): Value objects that represent
48+
* [**Queries:**](./Documentation/Queries.md) Value objects that represent
4849
a query without specifying how its executed, that is let to a query handler
49-
* [**Event upgrade:**](./Documentation/EventUpgrade.md): As events committed to
50+
* [**Event upgrade:**](./Documentation/EventUpgrade.md) As events committed to
5051
the event store is never changed, EventFlow uses the concept of event
5152
upgraders to deprecate events and replace them with new during aggregate load.
5253
* **Event publishing:** Sometimes you want other applications or services to
5354
consume and act on domains. For this EventFlow supports event publishing.
5455
* [RabbitMQ](./Documentation/RabbitMQ.md)
55-
* [**Metadata**](./Documentation/Metadata.md):
56+
* [**Metadata:**](./Documentation/Metadata.md)
5657
Additional information for each aggregate event, e.g. the IP of
5758
the user behind the event being emitted. EventFlow ships with
5859
several providers ready to use used.
59-
* [**Value objects**](./Documentation/ValueObjects.md): Data containing classes
60+
* [**Value objects:**](./Documentation/ValueObjects.md) Data containing classes
6061
used to validate and hold domain data, e.g. a username or e-mail.
61-
* [**Customize**](./Documentation/Customize.md): Almost every single part of
62+
* [**Customize:**](./Documentation/Customize.md) Almost every single part of
6263
EventFlow can be swapped with a custom implementation through the embedded
6364
IoC container.
6465

@@ -105,9 +106,31 @@ EventFlow is greatly opinionated, but its possible to create new implementations
105106
for almost every part of EventFlow by registering a different implementation of a
106107
a interface.
107108

108-
## Useful links
109-
110-
* [CQRS Journey by Microsoft](https://msdn.microsoft.com/en-us/library/jj554200.aspx)
109+
## Useful articles related to EventFlow and DDD
110+
111+
Many of the technical design decisions in EventFlow is based on articles. This
112+
section lists some of them. If you have a link with a relevant article, please
113+
share it by creating an issue with the link.
114+
115+
* **General CQRS+ES**
116+
- [CQRS Journey by Microsoft](https://msdn.microsoft.com/en-us/library/jj554200.aspx)
117+
published by Microsoft
118+
- [An In-Depth Look At CQRS](http://blog.sapiensworks.com/post/2015/09/01/In-Depth-CQRS/)
119+
by Mike Mogosanu
120+
- [CQRS, Task Based UIs, Event Sourcing agh!](http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/)
121+
by Greg Young
122+
- [Busting some CQRS myths](https://lostechies.com/jimmybogard/2012/08/22/busting-some-cqrs-myths/)
123+
by Jimmy Bogard
124+
- [CQRS applied](https://lostechies.com/gabrielschenker/2015/04/12/cqrs-applied/)
125+
by Gabriel Schenker
126+
* **Eventual consistency**
127+
- [How To Ensure Idempotency In An Eventual Consistent DDD/CQRS Application](http://blog.sapiensworks.com/post/2015/08/26/How-To-Ensure-Idempotency/)
128+
by Mike Mogosanu
129+
* **Why _not_ to implement "unit of work" in DDD**
130+
- [Unit Of Work is the new Singleton](http://blog.sapiensworks.com/post/2014/06/04/Unit-Of-Work-is-the-new-Singleton.aspx/)
131+
by Mike Mogosanu
132+
- [The Unit of Work and Transactions In Domain Driven Design](http://blog.sapiensworks.com/post/2015/09/02/DDD-and-UoW/)
133+
by Mike Mogosanu
111134

112135
## License
113136

@@ -135,4 +158,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
135158
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
136159
SOFTWARE.
137160
```
138-

RELEASE_NOTES.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,47 @@
1-
### New in 0.11 (not released yet)
1+
### New in 0.12 (not released yet)
2+
3+
* Breaking: Aggregate root no longer have `Aggregate` removed from their
4+
when name, i.e., the metadata property with key `aggregate_name` (or
5+
`MetadataKeys.AggregateName`). If you are dependent on the previous naming,
6+
use the new `AggregateName` attribute and apply it to your aggregates
7+
* Breaking: Moved `Identity<>` and `IIdentity` from the `EventFlow.Aggregates`
8+
namespace to `EventFlow.Core` as the identities are not specific for aggregates
9+
* Breaking: `ICommand.Id` is renamed to `ICommand.AggregateId` to make "room"
10+
for the new `ICommand.SourceId` property. If commands are serialized, then
11+
it _might_ be important verify that the serialization still works. EventFlow
12+
_does not_ serialize commands, so no mitigation is provided. If the
13+
`Command<,>` is used, make sure to use the correct protected constructor
14+
* Breaking: `IEventStore.StoreAsync(...)` now requires an additional
15+
`ISourceId` argument. To create a random one, use `SourceId.New`, but it
16+
should be e.g. the command ID that resulted in the events. Note, this method
17+
isn't typically used by developers
18+
* New: Added `ICommand.SourceId`, which contains the ID of the source. The
19+
default (if your commands inherit from `Command<,>`) will be a new
20+
`CommandId` each time the a `Command<,>` instance is created. You can pass
21+
specific value, merely use the newly added constructor taking the ID.
22+
Alternatively you commands could inherit from the new
23+
`DistinctCommand`, enabling commands with the same state to have the
24+
same `SourceId`
25+
* New: Duplicate commands can be detected using the new `ISourceId`. Read the
26+
EventFlow article regarding commands for more details
27+
* New: Aggregate names can now be configured using the attribute
28+
`AggregateName`. The name can be accessed using the new `IAggregateRoot.Name`
29+
property
30+
* New: Added `Identity<>.NewDeterministic(Guid, string)` enabling creation of
31+
[deterministic GUIDs](http://code.logos.com/blog/2011/04/generating_a_deterministic_guid.html)
32+
* New: Added new metadata key `source_id` (`MetadataKeys.SourceId`) containing
33+
the source ID, typically the ID of the command from which the event
34+
originated
35+
* New: Added new metadata key `event_id` (`MetadataKeys.EventId`) containing a
36+
deterministic ID for the event. Events with the same aggregate sequence
37+
number and from aggregates with the same identity, will have the same event
38+
identity
39+
* Fixed: `Identity<>.With(string)` now throws an `ArgumentException` instead of
40+
a `TargetInvocationException` when passed an invalid identity
41+
* Fixed: Aggregate roots now build the cache of `Apply` methods once, instead
42+
of when the method is requested the first time
43+
44+
### New in 0.11.751 (released 2015-08-24)
245

346
* Breaking: `EventFlowOptions.AddDefaults(...)` now also adds event
447
definitions

Source/EventFlow.Autofac/Extensions/EventFlowOptionsAutofacExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using System;
2424
using Autofac;
2525
using EventFlow.Aggregates;
26+
using EventFlow.Configuration;
2627
using EventFlow.Configuration.Registrations;
2728
using EventFlow.Configuration.Registrations.Services;
2829

Source/EventFlow.EventStores.EventStore.Tests/IntegrationTests/EventStoreEventStoreTestConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
using System.Threading.Tasks;
2626
using EventFlow.Aggregates;
2727
using EventFlow.Configuration;
28+
using EventFlow.Core;
2829
using EventFlow.EventStores.EventStore.Extensions;
2930
using EventFlow.Extensions;
3031
using EventFlow.MetadataProviders;

Source/EventFlow.EventStores.EventStore/Extensions/EventFlowOptionsExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2222

2323
using System.Net;
24+
using EventFlow.Configuration;
2425
using EventFlow.Configuration.Registrations;
2526
using EventFlow.Core;
2627
using EventFlow.Extensions;

0 commit comments

Comments
 (0)