Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rasmus committed May 5, 2015
2 parents 19a5577 + a253e5c commit e4e05a9
Show file tree
Hide file tree
Showing 89 changed files with 1,431 additions and 282 deletions.
21 changes: 19 additions & 2 deletions Documentation/Aggregates.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
# Aggregates

To create a new aggregate, simply inherit from `AggregateRoot<>` like
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 one EventFlow provides like
this.

```csharp
public class TestId : Identity<TestId>
{
public TestId(string value) : base(value)
{
}
}
```

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

Next, to create a new aggregate, simply inherit from `AggregateRoot<>` like
this, making sure to pass test aggregate own type as the generic
argument.

```csharp
public class TestAggregate : AggregateRoot<TestAggregate>
{
public TestAggregate(string id)
public TestAggregate(TestId id)
: base(id)
{
}
Expand Down
30 changes: 20 additions & 10 deletions Documentation/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,25 @@ Implementation notes

## Create an aggregate

Let us start by creating a aggregate to represent our users.
Initially you need to create the object representing the _identity_
of a user. Will use the class provided by EventFlow to help us to get
started.

```csharp
public class UserId : Identity<UserId>
{
public UserId(string value) : base(value)
{
}
}
```

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

```csharp
public class UserAggregate : AggregateRoot<UserAggregate>
{
public UserAggregate(string id)
public UserAggregate(UserId id)
: base(id)
{
}
Expand All @@ -26,8 +39,6 @@ public class UserAggregate : AggregateRoot<UserAggregate>

## Create event



```csharp
public class UserCreatedEvent : AggregateEvent<UserAggregate>
{
Expand Down Expand Up @@ -59,7 +70,7 @@ public class UserAggregate : AggregateRoot<UserAggregate>,
public string Username { get; private set; }
public string Password { get; private set; }

public UserAggregate(string id)
public UserAggregate(UserId id)
: base(id)
{
}
Expand Down Expand Up @@ -96,18 +107,17 @@ created `Create` method on our `UserAggregate`. The call must be
made from a command handler, and thus we first create the command.

```csharp
public class UserCreateCommand : ICommand<UserAggregate>
public class UserCreateCommand : Command<UserAggregate>
{
public string Id { get; private set; }
public string Username { get; private set; }
public string Password { get; private set; }

public UserCreateCommand(
string id,
UserId id,
string username,
string password)
: base(id)
{
Id = id;
Username = username;
Password = password;
}
Expand Down Expand Up @@ -140,7 +150,7 @@ Now all there is let is to create the user somewhere in your
application by publishing the command.

```csharp
var userId = GetNewRandomUserI();
var userId = UserId.New;
var username = GetUserEnteredUsername();
var password = GetUserEnteredPassword();

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ using (var resolver = EventFlowOptions.New
var readModelStore = resolver.Resolve<IInMemoryReadModelStore<
TestAggregate,
TestReadModel>>();
var id = Guid.NewGuid().ToString();
var id = TestId.New;

// Publish a command
await commandBus.PublishAsync(new TestACommand(id));
await commandBus.PublishAsync(new PingCommand(id));

// Load aggregate
var testAggregate = await eventStore.LoadAggregateAsync<TestAggregate>(id);
Expand Down
19 changes: 19 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### New in 0.4

* Breaking: `ValueObject` now uses public properties instead of both
private and public fields
- Braking: Aggregate IDs are no longer `string` but objects implementing
`IIdentity`
* Breaking: MSSQL transient exceptions are now retried
* Breaking: All methods on `IMsSqlConnection` has an extra `Label` argument
* New: `ITransientFaultHandler` added along with default retry strategies
for optimistic concurrency and MSSQL transient exceptions
* New: Release notes added to NuGet packages
* New: Better logging and more descriptive exceptions
* Fixed: Unchecked missing in `ValueObject` when claculating hash
* Fixed: `NullReferenceException` thrown if `null` was stored
in `SingleValueObject` and `ToString()` was called

### New in 0.3.292 (released 2015-04-30)

* First stable version of EventFlow
1 change: 1 addition & 0 deletions Source/EventFlow.Autofac/EventFlow.Autofac.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<copyright>Copyright (c) 2015 Rasmus Mikkelsen</copyright>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<tags>CQRS ES event sourceing autofac</tags>
<releaseNotes>@releaseNotes@</releaseNotes>
@dependencies@
@references@
</metadata>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<copyright>Copyright (c) 2015 Rasmus Mikkelsen</copyright>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<tags>CQRS ES event sourceing mssql eventstore</tags>
<releaseNotes>@releaseNotes@</releaseNotes>
@dependencies@
@references@
</metadata>
Expand Down
14 changes: 10 additions & 4 deletions Source/EventFlow.EventStores.MsSql/MssqlEventStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
using System.Threading;
using System.Threading.Tasks;
using EventFlow.Aggregates;
using EventFlow.Core;
using EventFlow.EventCaches;
using EventFlow.Exceptions;
using EventFlow.Logs;
Expand Down Expand Up @@ -63,7 +64,7 @@ public MsSqlEventStore(
}

protected override async Task<IReadOnlyCollection<ICommittedDomainEvent>> CommitEventsAsync<TAggregate>(
string id,
IIdentity id,
IReadOnlyCollection<SerializedEvent> serializedEvents,
CancellationToken cancellationToken)
{
Expand All @@ -78,7 +79,7 @@ protected override async Task<IReadOnlyCollection<ICommittedDomainEvent>> Commit
var eventDataModels = serializedEvents
.Select((e, i) => new EventDataModel
{
AggregateId = id,
AggregateId = id.Value,
AggregateName = aggregateName,
BatchId = batchId,
Data = e.Data,
Expand Down Expand Up @@ -108,6 +109,7 @@ OUTPUT CAST(INSERTED.GlobalSequenceNumber as bigint)
try
{
ids = await _connection.InsertMultipleAsync<long, EventDataModel>(
Label.Named("mssql-insert-events"),
cancellationToken,
sql,
eventDataModels)
Expand Down Expand Up @@ -141,11 +143,15 @@ OUTPUT CAST(INSERTED.GlobalSequenceNumber as bigint)
}

protected override async Task<IReadOnlyCollection<ICommittedDomainEvent>> LoadCommittedEventsAsync<TAggregate>(
string id,
IIdentity id,
CancellationToken cancellationToken)
{
const string sql = @"SELECT * FROM EventFlow WHERE AggregateId = @AggregateId ORDER BY AggregateSequenceNumber ASC";
var eventDataModels = await _connection.QueryAsync<EventDataModel>(cancellationToken, sql, new { AggregateId = id })
var eventDataModels = await _connection.QueryAsync<EventDataModel>(
Label.Named("mssql-fetch-events"),
cancellationToken,
sql,
new { AggregateId = id.Value })
.ConfigureAwait(false);
return eventDataModels;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
using System.Linq;
// The MIT License (MIT)
//
// Copyright (c) 2015 Rasmus Mikkelsen
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using EventFlow.Aggregates;
using EventFlow.Configuration;
using EventFlow.Core;
using EventFlow.EventStores.MsSql;
using EventFlow.Extensions;
using EventFlow.MsSql.Extensions;
using EventFlow.MsSql.Tests.Helpers;
using EventFlow.MsSql.Tests.ReadModels;
using EventFlow.ReadStores.MsSql;
using EventFlow.ReadStores.MsSql.Extensions;
using EventFlow.TestHelpers;
Expand Down Expand Up @@ -42,13 +65,14 @@ public override IRootResolver CreateRootResolver(EventFlowOptions eventFlowOptio
return resolver;
}

public override async Task<ITestAggregateReadModel> GetTestAggregateReadModel(string id)
public override async Task<ITestAggregateReadModel> GetTestAggregateReadModel(IIdentity id)
{
var sql = ReadModelSqlGenerator.CreateSelectSql<TestAggregateReadModel>();
var readModels = await MsSqlConnection.QueryAsync<TestAggregateReadModel>(
Label.Named("mssql-fetch-test-read-model"),
CancellationToken.None,
sql,
new {AggregateId = id})
new { AggregateId = id.Value })
.ConfigureAwait(false);
return readModels.SingleOrDefault();
}
Expand Down
2 changes: 2 additions & 0 deletions Source/EventFlow.MsSql/EventFlow.MsSql.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
<Compile Include="MsSqlConnection.cs" />
<Compile Include="MsSqlDatabaseMigrator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RetryStrategies\ISqlErrorRetryStrategy.cs" />
<Compile Include="RetryStrategies\SqlErrorRetryStrategy.cs" />
</ItemGroup>
<ItemGroup>
<None Include="EventFlow.MsSql.nuspec" />
Expand Down
1 change: 1 addition & 0 deletions Source/EventFlow.MsSql/EventFlow.MsSql.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<copyright>Copyright (c) 2015 Rasmus Mikkelsen</copyright>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<tags>CQRS ES event sourceing mssql</tags>
<releaseNotes>@releaseNotes@</releaseNotes>
@dependencies@
@references@
</metadata>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using EventFlow.Configuration.Registrations;
using EventFlow.MsSql.RetryStrategies;

namespace EventFlow.MsSql.Extensions
{
Expand All @@ -31,6 +32,7 @@ public static EventFlowOptions ConfigureMsSql(this EventFlowOptions eventFlowOpt
eventFlowOptions.RegisterServices(f =>
{
f.Register<IMsSqlConnection, MsSqlConnection>();
f.Register<ISqlErrorRetryStrategy, SqlErrorRetryStrategy>();
f.Register(_ => msSqlConfiguration, Lifetime.Singleton);
f.Register<IMsSqlDatabaseMigrator, MsSqlDatabaseMigrator>();
});
Expand Down
16 changes: 4 additions & 12 deletions Source/EventFlow.MsSql/IMssqlConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,17 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using EventFlow.Core;

namespace EventFlow.MsSql
{
public interface IMsSqlConnection
{
Task<int> ExecuteAsync(
CancellationToken cancellationToken,
string sql, object param = null);
Task<int> ExecuteAsync(Label label, CancellationToken cancellationToken, string sql, object param = null);

Task<IReadOnlyCollection<TResult>> QueryAsync<TResult>(
CancellationToken cancellationToken,
string sql,
object param = null);
Task<IReadOnlyCollection<TResult>> QueryAsync<TResult>(Label label, CancellationToken cancellationToken, string sql, object param = null);

Task<IReadOnlyCollection<TResult>> InsertMultipleAsync<TResult, TRow>(
CancellationToken cancellationToken,
string sql,
IEnumerable<TRow> rows,
object param = null)
Task<IReadOnlyCollection<TResult>> InsertMultipleAsync<TResult, TRow>(Label label, CancellationToken cancellationToken, string sql, IEnumerable<TRow> rows, object param = null)
where TRow : class, new();
}
}
Loading

0 comments on commit e4e05a9

Please sign in to comment.