Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
ardalis committed Oct 18, 2024
2 parents ffd1153 + 2ea322e commit 54235e2
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 29 deletions.
6 changes: 3 additions & 3 deletions src/Ardalis.SharedKernel/HasDomainEventsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace Ardalis.SharedKernel;

public abstract class HasDomainEventsBase
public abstract class HasDomainEventsBase : IHasDomainEvents
{
private List<DomainEventBase> _domainEvents = new();
private readonly List<DomainEventBase> _domainEvents = new();
[NotMapped]
public IEnumerable<DomainEventBase> DomainEvents => _domainEvents.AsReadOnly();
public IReadOnlyCollection<DomainEventBase> DomainEvents => _domainEvents.AsReadOnly();

protected void RegisterDomainEvent(DomainEventBase domainEvent) => _domainEvents.Add(domainEvent);
internal void ClearDomainEvents() => _domainEvents.Clear();
Expand Down
3 changes: 1 addition & 2 deletions src/Ardalis.SharedKernel/IDomainEventDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
/// </summary>
public interface IDomainEventDispatcher
{
Task DispatchAndClearEvents(IEnumerable<EntityBase> entitiesWithEvents);
Task DispatchAndClearEvents<TId>(IEnumerable<EntityBase<TId>> entitiesWithEvents) where TId : struct, IEquatable<TId>;
Task DispatchAndClearEvents(IEnumerable<IHasDomainEvents> entitiesWithEvents);
}
6 changes: 6 additions & 0 deletions src/Ardalis.SharedKernel/IHasDomainEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Ardalis.SharedKernel;

public interface IHasDomainEvents
{
IReadOnlyCollection<DomainEventBase> DomainEvents { get; }
}
40 changes: 18 additions & 22 deletions src/Ardalis.SharedKernel/MediatRDomainEventDispatcher.cs
Original file line number Diff line number Diff line change
@@ -1,42 +1,38 @@
using System;
using MediatR;
using MediatR;
using Microsoft.Extensions.Logging;

namespace Ardalis.SharedKernel;

public class MediatRDomainEventDispatcher : IDomainEventDispatcher
{
private readonly IMediator _mediator;
private readonly ILogger<MediatRDomainEventDispatcher> _logger;

public MediatRDomainEventDispatcher(IMediator mediator)
public MediatRDomainEventDispatcher(IMediator mediator, ILogger<MediatRDomainEventDispatcher> logger)
{
_mediator = mediator;
_logger = logger;
}

public async Task DispatchAndClearEvents(IEnumerable<EntityBase> entitiesWithEvents)
public async Task DispatchAndClearEvents(IEnumerable<IHasDomainEvents> entitiesWithEvents)
{
foreach (var entity in entitiesWithEvents)
foreach (IHasDomainEvents entity in entitiesWithEvents)
{
var events = entity.DomainEvents.ToArray();
entity.ClearDomainEvents();
foreach (var domainEvent in events)
if (entity is HasDomainEventsBase hasDomainEvents)
{
await _mediator.Publish(domainEvent).ConfigureAwait(false);
DomainEventBase[] events = hasDomainEvents.DomainEvents.ToArray();
hasDomainEvents.ClearDomainEvents();

foreach (DomainEventBase domainEvent in events)
await _mediator.Publish(domainEvent).ConfigureAwait(false);
}
}
}

public async Task DispatchAndClearEvents<TId>(IEnumerable<EntityBase<TId>> entitiesWithEvents)
where TId : struct, IEquatable<TId>
{
foreach (var entity in entitiesWithEvents)
{
var events = entity.DomainEvents.ToArray();
entity.ClearDomainEvents();
foreach (var domainEvent in events)
else
{
await _mediator.Publish(domainEvent).ConfigureAwait(false);
_logger.LogError(
"Entity of type {EntityType} does not inherit from {BaseType}. Unable to clear domain events.",
entity.GetType().Name,
nameof(HasDomainEventsBase));
}
}
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using FluentAssertions;
using MediatR;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;

Expand All @@ -22,7 +23,7 @@ public async Task CallsPublishAndClearDomainEvents()
{
// Arrange
var mediatorMock = new Mock<IMediator>();
var domainEventDispatcher = new MediatRDomainEventDispatcher(mediatorMock.Object);
var domainEventDispatcher = new MediatRDomainEventDispatcher(mediatorMock.Object, NullLogger<MediatRDomainEventDispatcher>.Instance);
var entity = new TestEntity();
entity.AddTestDomainEvent();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using FluentAssertions;
using MediatR;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;

Expand All @@ -22,7 +23,7 @@ public async Task CallsPublishAndClearDomainEvents()
{
// Arrange
var mediatorMock = new Mock<IMediator>();
var domainEventDispatcher = new MediatRDomainEventDispatcher(mediatorMock.Object);
var domainEventDispatcher = new MediatRDomainEventDispatcher(mediatorMock.Object, NullLogger<MediatRDomainEventDispatcher>.Instance);
var entity = new TestEntity();
entity.AddTestDomainEvent();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using FluentAssertions;
using MediatR;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;

namespace Ardalis.SharedKernel.UnitTests.MediatRDomainEventDispatcherTests;

public class DispatchAndClearEventsWithMixedIds
{
private class TestDomainEvent : DomainEventBase { }
public readonly record struct StronglyTyped { }

private class TestEntity : EntityBase
{
public void AddTestDomainEvent()
{
TestDomainEvent domainEvent = new();
RegisterDomainEvent(domainEvent);
}
}
private class TestEntityGuid : EntityBase<Guid>
{
public void AddTestDomainEvent()
{
TestDomainEvent domainEvent = new();
RegisterDomainEvent(domainEvent);
}
}
private class TestEntityStronglyTyped : EntityBase<StronglyTyped>
{
public void AddTestDomainEvent()
{
TestDomainEvent domainEvent = new();
RegisterDomainEvent(domainEvent);
}
}

[Fact]
public async void CallsPublishAndClearDomainEventsWithStronglyTypedId()

Check failure on line 40 in tests/Ardalis.SharedKernel.UnitTests/MediatRDomainEventDispatcherTests/DispatchAndClearEventsWithMixedIds.cs

View workflow job for this annotation

GitHub Actions / build

Support for 'async void' unit tests is being removed from xUnit.net v3. To simplify upgrading, convert the test to 'async Task' instead. (https://xunit.net/xunit.analyzers/rules/xUnit1048)

Check failure on line 40 in tests/Ardalis.SharedKernel.UnitTests/MediatRDomainEventDispatcherTests/DispatchAndClearEventsWithMixedIds.cs

View workflow job for this annotation

GitHub Actions / build

Support for 'async void' unit tests is being removed from xUnit.net v3. To simplify upgrading, convert the test to 'async Task' instead. (https://xunit.net/xunit.analyzers/rules/xUnit1048)
{
// Arrange
var mediatorMock = new Mock<IMediator>();
var domainEventDispatcher = new MediatRDomainEventDispatcher(mediatorMock.Object, NullLogger<MediatRDomainEventDispatcher>.Instance);
var entity = new TestEntity();
var entityGuid = new TestEntityGuid();
var entityStronglyTyped = new TestEntityStronglyTyped();
entity.AddTestDomainEvent();
entityGuid.AddTestDomainEvent();
entityStronglyTyped.AddTestDomainEvent();

// Act
await domainEventDispatcher.DispatchAndClearEvents(new List<IHasDomainEvents> { entity, entityGuid, entityStronglyTyped });

// Assert
mediatorMock.Verify(m => m.Publish(It.IsAny<DomainEventBase>(), It.IsAny<CancellationToken>()), Times.Exactly(3));
entity.DomainEvents.Should().BeEmpty();
entityGuid.DomainEvents.Should().BeEmpty();
entityStronglyTyped.DomainEvents.Should().BeEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using FluentAssertions;
using MediatR;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;

namespace Ardalis.SharedKernel.UnitTests.MediatRDomainEventDispatcherTests;

public class DispatchAndClearEventsWithStronglyTypedIds
{
private class TestDomainEvent : DomainEventBase { }

public readonly record struct StronglyTyped { }

private class TestEntityStronglyTyped : EntityBase<StronglyTyped>
{
public void AddTestDomainEvent()
{
TestDomainEvent domainEvent = new();
RegisterDomainEvent(domainEvent);
}
}

[Fact]
public async void CallsPublishAndClearDomainEventsWithStronglyTypedId()

Check failure on line 25 in tests/Ardalis.SharedKernel.UnitTests/MediatRDomainEventDispatcherTests/DispatchAndClearEventsWithStronglyTypedIds.cs

View workflow job for this annotation

GitHub Actions / build

Support for 'async void' unit tests is being removed from xUnit.net v3. To simplify upgrading, convert the test to 'async Task' instead. (https://xunit.net/xunit.analyzers/rules/xUnit1048)

Check failure on line 25 in tests/Ardalis.SharedKernel.UnitTests/MediatRDomainEventDispatcherTests/DispatchAndClearEventsWithStronglyTypedIds.cs

View workflow job for this annotation

GitHub Actions / build

Support for 'async void' unit tests is being removed from xUnit.net v3. To simplify upgrading, convert the test to 'async Task' instead. (https://xunit.net/xunit.analyzers/rules/xUnit1048)
{
// Arrange
Mock<IMediator> mediatorMock = new Mock<IMediator>();
MediatRDomainEventDispatcher domainEventDispatcher =
new(mediatorMock.Object, NullLogger<MediatRDomainEventDispatcher>.Instance);
TestEntityStronglyTyped entity = new();
entity.AddTestDomainEvent();

// Act
await domainEventDispatcher.DispatchAndClearEvents(new List<IHasDomainEvents> { entity });

// Assert
mediatorMock.Verify(m => m.Publish(It.IsAny<DomainEventBase>(), It.IsAny<CancellationToken>()), Times.Once);
entity.DomainEvents.Should().BeEmpty();
}
}

0 comments on commit 54235e2

Please sign in to comment.