add optimistic concurrency

This commit is contained in:
meysamhadeli 2022-06-12 23:05:23 +04:30
parent ed931ae0f2
commit 3d3f46f0b1
8 changed files with 73 additions and 25 deletions

View File

@ -25,9 +25,7 @@ namespace BuildingBlocks.Domain.Model
return dequeuedEvents; return dequeuedEvents;
} }
public virtual void When(object @event) { } public long Version { get; set; } = -1;
public long Version { get; protected set; } = -1;
public TId Id { get; protected set; } public TId Id { get; protected set; }
} }

View File

@ -1,19 +1,15 @@
using BuildingBlocks.Domain.Event; using BuildingBlocks.Domain.Event;
using BuildingBlocks.EventStoreDB.Events;
namespace BuildingBlocks.Domain.Model namespace BuildingBlocks.Domain.Model;
{
public interface IAggregate : IProjection, IEntity public interface IAggregate : IEntity
{ {
IReadOnlyList<IDomainEvent> DomainEvents { get; } IReadOnlyList<IDomainEvent> DomainEvents { get; }
IEvent[] ClearDomainEvents(); IEvent[] ClearDomainEvents();
long Version { get; } long Version { get; set; }
} }
public interface IAggregate<out T> : IAggregate public interface IAggregate<out T> : IAggregate
{ {
T Id { get; } T Id { get; }
} }
}

View File

@ -111,11 +111,13 @@ public abstract class AppDbContextBase : DbContext, IDbContext
case EntityState.Added: case EntityState.Added:
entry.Entity.CreatedBy = userId; entry.Entity.CreatedBy = userId;
entry.Entity.CreatedAt = DateTime.Now; entry.Entity.CreatedAt = DateTime.Now;
entry.Entity.Version++;
break; break;
case EntityState.Modified: case EntityState.Modified:
entry.Entity.LastModifiedBy = userId; entry.Entity.LastModifiedBy = userId;
entry.Entity.LastModified = DateTime.Now; entry.Entity.LastModified = DateTime.Now;
entry.Entity.Version++;
break; break;
case EntityState.Deleted: case EntityState.Deleted:

View File

@ -0,0 +1,32 @@
using BuildingBlocks.Domain.Event;
using BuildingBlocks.Domain.Model;
namespace BuildingBlocks.EventStoreDB.Events
{
public abstract class AggregateEventSourcing<TId> : Entity, IAggregateEventSourcing<TId>
{
private readonly List<IDomainEvent> _domainEvents = new();
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
public void AddDomainEvent(IDomainEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
public IEvent[] ClearDomainEvents()
{
IEvent[] dequeuedEvents = _domainEvents.ToArray();
_domainEvents.Clear();
return dequeuedEvents;
}
public virtual void When(object @event) { }
public long Version { get; protected set; } = -1;
public TId Id { get; protected set; }
}
}

View File

@ -0,0 +1,19 @@
using BuildingBlocks.Domain.Event;
using BuildingBlocks.Domain.Model;
namespace BuildingBlocks.EventStoreDB.Events
{
public interface IAggregateEventSourcing : IProjection, IEntity
{
IReadOnlyList<IDomainEvent> DomainEvents { get; }
IEvent[] ClearDomainEvents();
long Version { get; }
}
public interface IAggregateEventSourcing<out T> : IAggregateEventSourcing
{
T Id { get; }
}
}

View File

@ -1,11 +1,10 @@
using BuildingBlocks.Domain.Model; using BuildingBlocks.EventStoreDB.Events;
using BuildingBlocks.EventStoreDB.Events;
using BuildingBlocks.EventStoreDB.Serialization; using BuildingBlocks.EventStoreDB.Serialization;
using EventStore.Client; using EventStore.Client;
namespace BuildingBlocks.EventStoreDB.Repository; namespace BuildingBlocks.EventStoreDB.Repository;
public interface IEventStoreDBRepository<T> where T : class, IAggregate<long> public interface IEventStoreDBRepository<T> where T : class, IAggregateEventSourcing<long>
{ {
Task<T?> Find(long id, CancellationToken cancellationToken); Task<T?> Find(long id, CancellationToken cancellationToken);
Task<ulong> Add(T aggregate, CancellationToken cancellationToken); Task<ulong> Add(T aggregate, CancellationToken cancellationToken);
@ -13,7 +12,7 @@ public interface IEventStoreDBRepository<T> where T : class, IAggregate<long>
Task<ulong> Delete(T aggregate, long? expectedRevision = null, CancellationToken cancellationToken = default); Task<ulong> Delete(T aggregate, long? expectedRevision = null, CancellationToken cancellationToken = default);
} }
public class EventStoreDBRepository<T>: IEventStoreDBRepository<T> where T : class, IAggregate<long> public class EventStoreDBRepository<T>: IEventStoreDBRepository<T> where T : class, IAggregateEventSourcing<long>
{ {
private readonly EventStoreClient eventStore; private readonly EventStoreClient eventStore;

View File

@ -1,4 +1,5 @@
using BuildingBlocks.Domain.Model; using BuildingBlocks.Domain.Model;
using BuildingBlocks.EventStoreDB.Events;
using BuildingBlocks.Exception; using BuildingBlocks.Exception;
namespace BuildingBlocks.EventStoreDB.Repository; namespace BuildingBlocks.EventStoreDB.Repository;
@ -9,7 +10,7 @@ public static class RepositoryExtensions
this IEventStoreDBRepository<T> repository, this IEventStoreDBRepository<T> repository,
long id, long id,
CancellationToken cancellationToken CancellationToken cancellationToken
) where T : class, IAggregate<long> ) where T : class, IAggregateEventSourcing<long>
{ {
var entity = await repository.Find(id, cancellationToken); var entity = await repository.Find(id, cancellationToken);
@ -22,7 +23,7 @@ public static class RepositoryExtensions
Action<T> action, Action<T> action,
long? expectedVersion = null, long? expectedVersion = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) where T : class, IAggregate<long> ) where T : class, IAggregateEventSourcing<long>
{ {
var entity = await repository.Get(id, cancellationToken); var entity = await repository.Get(id, cancellationToken);

View File

@ -1,10 +1,11 @@
using Booking.Booking.Events.Domain; using Booking.Booking.Events.Domain;
using Booking.Booking.Models.ValueObjects; using Booking.Booking.Models.ValueObjects;
using BuildingBlocks.Domain.Model; using BuildingBlocks.Domain.Model;
using BuildingBlocks.EventStoreDB.Events;
namespace Booking.Booking.Models; namespace Booking.Booking.Models;
public class Booking : Aggregate<long> public class Booking : AggregateEventSourcing<long>
{ {
public Booking() public Booking()
{ {