feat(backend): Implement Domain Events infrastructure and handlers

Add complete domain events dispatching infrastructure and critical event handlers for Identity module.

Changes:
- Added IMediator injection to IdentityDbContext
- Implemented SaveChangesAsync override to dispatch domain events before persisting
- Made DomainEvent base class implement INotification (added MediatR.Contracts dependency)
- Created 3 new domain events: UserRoleAssignedEvent, UserRemovedFromTenantEvent, UserLoggedInEvent
- Implemented 4 event handlers with structured logging:
  - UserRoleAssignedEventHandler (audit log, cache invalidation placeholder)
  - UserRemovedFromTenantEventHandler (notification placeholder)
  - UserLoggedInEventHandler (login tracking placeholder)
  - TenantCreatedEventHandler (welcome email placeholder)
- Updated unit tests to inject mock IMediator into IdentityDbContext

Technical Details:
- Domain events are now published via MediatR within the same transaction
- Events are dispatched BEFORE SaveChangesAsync to ensure atomicity
- Event handlers auto-registered by MediatR assembly scanning
- All handlers include structured logging for observability

Next Steps (Phase 3):
- Update command handlers to raise new events (UserLoggedInEvent, UserRoleAssignedEvent)
- Add event raising logic to User/Tenant aggregates
- Implement audit logging persistence (currently just logging)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-03 20:33:36 +01:00
parent 709068f68b
commit 0e503176c4
13 changed files with 1170 additions and 8 deletions

View File

@@ -3,6 +3,7 @@ using ColaFlow.Modules.Identity.Infrastructure.Persistence;
using ColaFlow.Modules.Identity.Infrastructure.Persistence.Repositories;
using ColaFlow.Modules.Identity.Infrastructure.Services;
using FluentAssertions;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Moq;
@@ -22,7 +23,9 @@ public class TenantRepositoryTests : IDisposable
var mockTenantContext = new Mock<ITenantContext>();
mockTenantContext.Setup(x => x.IsSet).Returns(false);
_context = new IdentityDbContext(options, mockTenantContext.Object);
var mockMediator = new Mock<IMediator>();
_context = new IdentityDbContext(options, mockTenantContext.Object, mockMediator.Object);
_repository = new TenantRepository(_context);
}