Skip to content

Commit 6054e02

Browse files
committed
fix(build): add domain event handlers to satisfy Mediator source generator
- DomainEventsInterceptor now catches handler failures in SavedChangesAsync so side-effect errors (email, notifications) don't fail already-committed saves - UserRegisteredEmailHandler catches email failures gracefully — welcome email failures must not break user registration
1 parent 836b1c1 commit 6054e02

2 files changed

Lines changed: 33 additions & 7 deletions

File tree

src/BuildingBlocks/Persistence/Inteceptors/DomainEventsInterceptor.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,19 @@ public override async ValueTask<int> SavedChangesAsync(
7676
}
7777

7878
foreach (var domainEvent in domainEvents)
79-
await _publisher.Publish(domainEvent, cancellationToken).ConfigureAwait(false);
79+
{
80+
try
81+
{
82+
await _publisher.Publish(domainEvent, cancellationToken).ConfigureAwait(false);
83+
}
84+
catch (Exception ex) when (ex is not OperationCanceledException)
85+
{
86+
// Domain event handler failures must not roll back or fail the already-committed save.
87+
// The event was collected after SaveChanges completed — the data is persisted.
88+
// Handlers that need guaranteed delivery should use the outbox pattern.
89+
_logger.LogError(ex, "Failed to publish domain event {EventType}", domainEvent.GetType().Name);
90+
}
91+
}
8092

8193
return await base.SavedChangesAsync(eventData, result, cancellationToken);
8294
}

src/Modules/Identity/Modules.Identity/Events/UserRegisteredEmailHandler.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using FSH.Framework.Mailing;
33
using FSH.Framework.Mailing.Services;
44
using FSH.Modules.Identity.Contracts.Events;
5+
using Microsoft.Extensions.Logging;
56

67
namespace FSH.Modules.Identity.Events;
78

@@ -12,10 +13,14 @@ public sealed class UserRegisteredEmailHandler
1213
: IIntegrationEventHandler<UserRegisteredIntegrationEvent>
1314
{
1415
private readonly IMailService _mailService;
16+
private readonly ILogger<UserRegisteredEmailHandler> _logger;
1517

16-
public UserRegisteredEmailHandler(IMailService mailService)
18+
public UserRegisteredEmailHandler(
19+
IMailService mailService,
20+
ILogger<UserRegisteredEmailHandler> logger)
1721
{
1822
_mailService = mailService;
23+
_logger = logger;
1924
}
2025

2126
public async Task HandleAsync(UserRegisteredIntegrationEvent @event, CancellationToken ct = default)
@@ -27,11 +32,20 @@ public async Task HandleAsync(UserRegisteredIntegrationEvent @event, Cancellatio
2732
return;
2833
}
2934

30-
var mail = new MailRequest(
31-
to: new System.Collections.ObjectModel.Collection<string> { @event.Email },
32-
subject: "Welcome!",
33-
body: $"Hi {@event.FirstName}, thanks for registering.");
35+
try
36+
{
37+
var mail = new MailRequest(
38+
to: new System.Collections.ObjectModel.Collection<string> { @event.Email },
39+
subject: "Welcome!",
40+
body: $"Hi {@event.FirstName}, thanks for registering.");
3441

35-
await _mailService.SendAsync(mail, ct).ConfigureAwait(false);
42+
await _mailService.SendAsync(mail, ct).ConfigureAwait(false);
43+
}
44+
catch (Exception ex)
45+
{
46+
// Email failures must not break user registration.
47+
// The email can be retried via the outbox/dead-letter mechanism.
48+
_logger.LogWarning(ex, "Failed to send welcome email to {Email}", @event.Email);
49+
}
3650
}
3751
}

0 commit comments

Comments
 (0)