# Domain Events Implementation Analysis & Plan

**Date:** 2025-11-03
**Module:** Identity Module (ColaFlow.Modules.Identity)
**Status:** Gap Analysis Complete - Implementation Required

---

## Executive Summary

### Current State
The Identity module has **partial domain events implementation**:
- ✅ Domain event infrastructure exists (base classes, AggregateRoot pattern)
- ✅ **11 domain events defined** in the domain layer
- ✅ Domain events are being **raised** in aggregates (Tenant, User)
- ❌ **Domain events are NOT being dispatched** (events are raised but never published)
- ❌ **No domain event handlers** implemented
- ❌ Repository pattern calls `SaveChangesAsync` directly, bypassing event dispatching

### Critical Finding
**Domain events are being collected but never published!** This means:
- Events like `TenantCreated`, `UserCreated`, `UserRoleAssigned` are raised but silently discarded
- No audit logging, no side effects, no cross-module notifications
- The infrastructure is 80% complete but missing the final critical piece

### Recommended Action
**Immediate implementation required** - Domain events are foundational for:
- Audit logging (required for compliance)
- Cross-module communication (required for modularity)
- Side effects (email notifications, cache invalidation, etc.)
- Event sourcing (future requirement)

---

## 1. Current State Assessment

### 1.1 Domain Event Infrastructure (✅ Complete)

#### Base Classes

**`ColaFlow.Shared.Kernel.Events.DomainEvent`**
```csharp
public abstract record DomainEvent
{
    public Guid EventId { get; init; } = Guid.NewGuid();
    public DateTime OccurredOn { get; init; } = DateTime.UtcNow;
}
```
- ✅ Properly designed as record (immutable)
- ✅ Auto-generates EventId and timestamp
- ✅ Follows best practices

**`ColaFlow.Shared.Kernel.Common.AggregateRoot`**
```csharp
public abstract class AggregateRoot : Entity
{
    private readonly List<DomainEvent> _domainEvents = new();

    public IReadOnlyCollection<DomainEvent> DomainEvents => _domainEvents.AsReadOnly();

    protected void AddDomainEvent(DomainEvent domainEvent)
    {
        _domainEvents.Add(domainEvent);
    }

    public void ClearDomainEvents()
    {
        _domainEvents.Clear();
    }
}
```
- ✅ Encapsulates domain events collection
- ✅ Provides AddDomainEvent method for aggregates
- ✅ Provides ClearDomainEvents for cleanup after dispatching
- ✅ Follows DDD best practices (encapsulation)

### 1.2 Domain Events Defined (✅ Complete)

#### Tenant Events (7 events)

| Event | File | Raised In | Purpose |
|-------|------|-----------|---------|
| `TenantCreatedEvent` | `Tenants/Events/` | `Tenant.Create()` | New tenant registration |
| `TenantActivatedEvent` | `Tenants/Events/` | `Tenant.Activate()` | Tenant reactivation |
| `TenantSuspendedEvent` | `Tenants/Events/` | `Tenant.Suspend()` | Tenant suspension |
| `TenantCancelledEvent` | `Tenants/Events/` | `Tenant.Cancel()` | Tenant cancellation |
| `TenantPlanUpgradedEvent` | `Tenants/Events/` | `Tenant.UpgradePlan()` | Plan upgrade |
| `SsoConfiguredEvent` | `Tenants/Events/` | `Tenant.ConfigureSso()` | SSO setup |
| `SsoDisabledEvent` | `Tenants/Events/` | `Tenant.DisableSso()` | SSO removal |

**Example:**
```csharp
public sealed record TenantCreatedEvent(Guid TenantId, string Slug) : DomainEvent;
```

#### User Events (4 events)

| Event | File | Raised In | Purpose |
|-------|------|-----------|---------|
| `UserCreatedEvent` | `Users/Events/` | `User.CreateLocal()` | Local user registration |
| `UserCreatedFromSsoEvent` | `Users/Events/` | `User.CreateFromSso()` | SSO user registration |
| `UserPasswordChangedEvent` | `Users/Events/` | `User.UpdatePassword()` | Password change |
| `UserSuspendedEvent` | `Users/Events/` | `User.Suspend()` | User suspension |

**Example:**
```csharp
public sealed record UserCreatedEvent(
    Guid UserId,
    string Email,
    TenantId TenantId
) : DomainEvent;
```

### 1.3 Event Dispatching Infrastructure (❌ Missing in Identity Module)

#### ProjectManagement Module (Reference Implementation)

**`ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence.UnitOfWork`**
```csharp
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
    // Dispatch domain events before saving
    await DispatchDomainEventsAsync(cancellationToken);

    // Save changes to database
    return await _context.SaveChangesAsync(cancellationToken);
}

private async Task DispatchDomainEventsAsync(CancellationToken cancellationToken)
{
    // Get all entities with domain events
    var domainEntities = _context.ChangeTracker
        .Entries<AggregateRoot>()
        .Where(x => x.Entity.DomainEvents.Any())
        .Select(x => x.Entity)
        .ToList();

    // Get all domain events
    var domainEvents = domainEntities
        .SelectMany(x => x.DomainEvents)
        .ToList();

    // Clear domain events from entities
    domainEntities.ForEach(entity => entity.ClearDomainEvents());

    // TODO: Dispatch domain events to handlers
    // This will be implemented when we add MediatR
    await Task.CompletedTask;
}
```

**Status:** ✅ Infrastructure exists in ProjectManagement module, ❌ Not implemented in Identity module

#### Identity Module (Current Implementation)

**`IdentityDbContext`**
- ❌ No `SaveChangesAsync` override
- ❌ No domain event dispatching
- ❌ No UnitOfWork pattern

**Repositories (TenantRepository, UserRepository, etc.)**
```csharp
public async Task AddAsync(Tenant tenant, CancellationToken cancellationToken = default)
{
    await _context.Tenants.AddAsync(tenant, cancellationToken);
    await _context.SaveChangesAsync(cancellationToken); // ❌ Direct call, bypasses events
}
```

**Problem:** Repositories call `DbContext.SaveChangesAsync()` directly, so domain events are never dispatched.

### 1.4 Domain Event Handlers (❌ Missing)

**Current State:**
- ❌ No `INotificationHandler<TEvent>` implementations
- ❌ No event handler folder structure
- ❌ MediatR registered in Application layer but not configured for domain events

**Expected Structure (Not Present):**
```
ColaFlow.Modules.Identity.Application/
├── EventHandlers/
│   ├── Tenants/
│   │   ├── TenantCreatedEventHandler.cs        ❌ Missing
│   │   └── TenantPlanUpgradedEventHandler.cs   ❌ Missing
│   └── Users/
│       ├── UserCreatedEventHandler.cs          ❌ Missing
│       └── UserSuspendedEventHandler.cs        ❌ Missing
```

---

## 2. Gap Analysis

### 2.1 What's Working

| Component | Status | Notes |
|-----------|--------|-------|
| Domain Event Base Class | ✅ Complete | Well-designed record with EventId and timestamp |
| AggregateRoot Pattern | ✅ Complete | Proper encapsulation of domain events collection |
| Domain Events Defined | ✅ Complete | 11 events defined and raised in aggregates |
| MediatR Registration | ✅ Complete | MediatR registered in Application layer |

### 2.2 What's Missing

| Component | Status | Impact | Priority |
|-----------|--------|--------|----------|
| **Event Dispatching in DbContext** | ❌ Missing | HIGH - Events never published | **CRITICAL** |
| **UnitOfWork Pattern** | ❌ Missing | HIGH - No transaction boundary for events | **CRITICAL** |
| **Domain Event Handlers** | ❌ Missing | HIGH - No side effects, no audit logging | **HIGH** |
| **MediatR Integration for Events** | ❌ Missing | HIGH - Events not routed to handlers | **CRITICAL** |
| **Repository Pattern Refactoring** | ❌ Missing | MEDIUM - Repositories bypass UnitOfWork | **HIGH** |

### 2.3 Missing Events (Day 6+ Features)

Based on Day 4-6 implementation, these events should exist but don't:

| Event | Scenario | Raised In | Priority |
|-------|----------|-----------|----------|
| `UserLoggedInEvent` | Login success | LoginCommandHandler | HIGH |
| `UserLoginFailedEvent` | Login failure | LoginCommandHandler | MEDIUM |
| `RefreshTokenGeneratedEvent` | Token refresh | RefreshTokenService | MEDIUM |
| `RefreshTokenRevokedEvent` | Token revocation | RefreshTokenService | MEDIUM |
| `UserRoleAssignedEvent` | Role assignment | AssignUserRoleCommand | **HIGH** |
| `UserRoleUpdatedEvent` | Role change | AssignUserRoleCommand | **HIGH** |
| `UserRemovedFromTenantEvent` | User removal | RemoveUserFromTenantCommand | **HIGH** |
| `UserTokensRevokedEvent` | Token revocation | RemoveUserFromTenantCommand | MEDIUM |

---

## 3. Recommended Architecture

### 3.1 Domain Event Dispatching Pattern

**Option A: Dispatch in DbContext.SaveChangesAsync (Recommended)**

**Pros:**
- ✅ Centralized event dispatching
- ✅ Consistent across all operations
- ✅ Events dispatched within transaction boundary
- ✅ Follows EF Core best practices

**Cons:**
- ⚠️ Requires overriding `SaveChangesAsync` in each module's DbContext
- ⚠️ Tight coupling to EF Core

**Implementation:**
```csharp
// IdentityDbContext.cs
public class IdentityDbContext : DbContext
{
    private readonly IMediator _mediator;

    public IdentityDbContext(
        DbContextOptions<IdentityDbContext> options,
        ITenantContext tenantContext,
        IMediator mediator) // ✅ Inject MediatR
        : base(options)
    {
        _tenantContext = tenantContext;
        _mediator = mediator;
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        // Dispatch domain events BEFORE saving
        await DispatchDomainEventsAsync(cancellationToken);

        // Save changes to database
        return await base.SaveChangesAsync(cancellationToken);
    }

    private async Task DispatchDomainEventsAsync(CancellationToken cancellationToken)
    {
        // Get all aggregate roots with domain events
        var domainEntities = ChangeTracker
            .Entries<AggregateRoot>()
            .Where(x => x.Entity.DomainEvents.Any())
            .Select(x => x.Entity)
            .ToList();

        // Get all domain events
        var domainEvents = domainEntities
            .SelectMany(x => x.DomainEvents)
            .ToList();

        // Clear domain events from entities
        domainEntities.ForEach(entity => entity.ClearDomainEvents());

        // Dispatch events to handlers via MediatR
        foreach (var domainEvent in domainEvents)
        {
            await _mediator.Publish(domainEvent, cancellationToken);
        }
    }
}
```

**Option B: Dispatch in UnitOfWork (Alternative)**

**Pros:**
- ✅ Decouples from DbContext
- ✅ Testable without EF Core
- ✅ Follows Clean Architecture more strictly

**Cons:**
- ⚠️ Requires UnitOfWork pattern implementation
- ⚠️ More boilerplate code
- ⚠️ Repositories must use UnitOfWork instead of direct SaveChangesAsync

**Not recommended for now** - Option A is simpler and sufficient for current needs.

### 3.2 MediatR Configuration

**Current Configuration:**
```csharp
// Application/DependencyInjection.cs
public static IServiceCollection AddIdentityApplication(this IServiceCollection services)
{
    // MediatR
    services.AddMediatR(config =>
    {
        config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly);
    });

    // FluentValidation
    services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly);

    return services;
}
```

**Status:** ✅ Already configured for commands/queries, will automatically handle domain events

**How MediatR Works:**
1. Domain events inherit from `DomainEvent` (which is a record)
2. Event handlers implement `INotificationHandler<TEvent>`
3. `_mediator.Publish(event)` dispatches to ALL handlers

**Key Point:** MediatR treats domain events as notifications (pub-sub pattern), so multiple handlers can react to the same event.

### 3.3 Domain Event Handler Pattern

**Handler Structure:**
```csharp
// Application/EventHandlers/Users/UserCreatedEventHandler.cs
public class UserCreatedEventHandler : INotificationHandler<UserCreatedEvent>
{
    private readonly IAuditLogRepository _auditLogRepository;
    private readonly ILogger<UserCreatedEventHandler> _logger;

    public UserCreatedEventHandler(
        IAuditLogRepository auditLogRepository,
        ILogger<UserCreatedEventHandler> logger)
    {
        _auditLogRepository = auditLogRepository;
        _logger = logger;
    }

    public async Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "User {UserId} created in tenant {TenantId}",
            notification.UserId,
            notification.TenantId);

        // Example: Log to audit trail
        var auditLog = AuditLog.Create(
            entityType: "User",
            entityId: notification.UserId,
            action: "Created",
            performedBy: notification.UserId, // Self-registration
            timestamp: notification.OccurredOn);

        await _auditLogRepository.AddAsync(auditLog, cancellationToken);
    }
}
```

**Multiple Handlers for Same Event:**
```csharp
// Application/EventHandlers/Users/UserCreatedEmailNotificationHandler.cs
public class UserCreatedEmailNotificationHandler : INotificationHandler<UserCreatedEvent>
{
    private readonly IEmailService _emailService;

    public async Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
    {
        // Send welcome email
        await _emailService.SendWelcomeEmailAsync(
            notification.Email,
            notification.UserId,
            cancellationToken);
    }
}
```

**Key Benefits:**
- ✅ Single Responsibility Principle (each handler does one thing)
- ✅ Decoupled side effects (audit, email, cache, etc.)
- ✅ Easy to add new handlers without modifying existing code

---

## 4. Implementation Plan

### Option A: Implement Now (Recommended)

**Reasoning:**
- Domain events are fundamental to the architecture
- Required for Day 6 features (role management audit)
- Critical for audit logging and compliance
- Relatively small implementation effort (2-4 hours)

**Timeline:** Day 6 (Today) - Implement alongside role management features

---

### Option B: Implement in Day 7

**Reasoning:**
- Can defer if Day 6 deadline is tight
- Focus on completing role management first
- Implement events as cleanup/refactoring task

**Timeline:** Day 7 (Tomorrow) - Dedicated domain events implementation day

---

### Option C: Incremental Implementation

**Reasoning:**
- Implement infrastructure first (dispatching in DbContext)
- Add event handlers incrementally as needed
- Start with critical events (UserCreated, TenantCreated, UserRoleAssigned)

**Timeline:** Days 6-8 - Spread across multiple days

---

### ✅ RECOMMENDED: Option C (Incremental Implementation)

**Phase 1: Infrastructure (Day 6, ~1 hour)**
1. Override `SaveChangesAsync` in `IdentityDbContext`
2. Implement `DispatchDomainEventsAsync` method
3. Inject `IMediator` into DbContext
4. Test that events are being published (add logging)

**Phase 2: Critical Event Handlers (Day 6-7, ~2 hours)**
1. `UserCreatedEventHandler` - Audit logging
2. `TenantCreatedEventHandler` - Audit logging
3. `UserRoleAssignedEventHandler` - Audit logging + cache invalidation

**Phase 3: Additional Event Handlers (Day 7-8, ~2 hours)**
1. `UserLoggedInEvent` + handler - Login audit trail
2. `RefreshTokenRevokedEvent` + handler - Security audit
3. `TenantSuspendedEvent` + handler - Notify users, revoke tokens

**Phase 4: Future Events (Day 9+)**
1. Email verification events
2. Password reset events
3. SSO events
4. Cross-module integration events

---

## 5. Step-by-Step Implementation Guide

### Step 1: Add Domain Event Dispatching to DbContext

**File:** `src/Modules/Identity/ColaFlow.Modules.Identity.Infrastructure/Persistence/IdentityDbContext.cs`

**Changes:**
```csharp
using ColaFlow.Shared.Kernel.Common;
using MediatR;

public class IdentityDbContext : DbContext
{
    private readonly ITenantContext _tenantContext;
    private readonly IMediator _mediator; // ✅ Add

    public IdentityDbContext(
        DbContextOptions<IdentityDbContext> options,
        ITenantContext tenantContext,
        IMediator mediator) // ✅ Add
        : base(options)
    {
        _tenantContext = tenantContext;
        _mediator = mediator; // ✅ Add
    }

    // ✅ Add SaveChangesAsync override
    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        await DispatchDomainEventsAsync(cancellationToken);
        return await base.SaveChangesAsync(cancellationToken);
    }

    // ✅ Add DispatchDomainEventsAsync method
    private async Task DispatchDomainEventsAsync(CancellationToken cancellationToken)
    {
        var domainEntities = ChangeTracker
            .Entries<AggregateRoot>()
            .Where(x => x.Entity.DomainEvents.Any())
            .Select(x => x.Entity)
            .ToList();

        var domainEvents = domainEntities
            .SelectMany(x => x.DomainEvents)
            .ToList();

        domainEntities.ForEach(entity => entity.ClearDomainEvents());

        foreach (var domainEvent in domainEvents)
        {
            await _mediator.Publish(domainEvent, cancellationToken);
        }
    }
}
```

**Estimated Time:** 15 minutes

---

### Step 2: Create Missing Domain Events

**File:** `src/Modules/Identity/ColaFlow.Modules.Identity.Domain/Aggregates/Users/Events/UserRoleAssignedEvent.cs`

```csharp
using ColaFlow.Shared.Kernel.Events;
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;

namespace ColaFlow.Modules.Identity.Domain.Aggregates.Users.Events;

public sealed record UserRoleAssignedEvent(
    Guid UserId,
    TenantId TenantId,
    TenantRole Role,
    Guid AssignedBy
) : DomainEvent;
```

**File:** `src/Modules/Identity/ColaFlow.Modules.Identity.Domain/Aggregates/Users/Events/UserRemovedFromTenantEvent.cs`

```csharp
public sealed record UserRemovedFromTenantEvent(
    Guid UserId,
    TenantId TenantId,
    Guid RemovedBy
) : DomainEvent;
```

**File:** `src/Modules/Identity/ColaFlow.Modules.Identity.Domain/Aggregates/Users/Events/UserLoggedInEvent.cs`

```csharp
public sealed record UserLoggedInEvent(
    Guid UserId,
    TenantId TenantId,
    string IpAddress,
    string UserAgent
) : DomainEvent;
```

**Estimated Time:** 30 minutes

---

### Step 3: Raise Events in Aggregates

**Update:** `AssignUserRoleCommandHandler` to raise `UserRoleAssignedEvent`

```csharp
// AssignUserRoleCommandHandler.cs
public async Task<Unit> Handle(AssignUserRoleCommand request, CancellationToken cancellationToken)
{
    // ... existing validation logic ...

    // Create or update role assignment
    var userTenantRole = UserTenantRole.Create(userId, tenantId, request.Role);
    await _userTenantRoleRepository.AddOrUpdateAsync(userTenantRole, cancellationToken);

    // ✅ Raise domain event (if we make UserTenantRole an AggregateRoot)
    // OR raise event from User aggregate
    var user = await _userRepository.GetByIdAsync(userId, cancellationToken);
    if (user != null)
    {
        user.AddDomainEvent(new UserRoleAssignedEvent(
            userId.Value,
            tenantId,
            request.Role,
            currentUserId)); // From JWT claims
    }

    return Unit.Value;
}
```

**Estimated Time:** 1 hour (refactor command handlers)

---

### Step 4: Create Event Handlers

**File:** `src/Modules/Identity/ColaFlow.Modules.Identity.Application/EventHandlers/Users/UserRoleAssignedEventHandler.cs`

```csharp
using ColaFlow.Modules.Identity.Domain.Aggregates.Users.Events;
using MediatR;
using Microsoft.Extensions.Logging;

namespace ColaFlow.Modules.Identity.Application.EventHandlers.Users;

public class UserRoleAssignedEventHandler : INotificationHandler<UserRoleAssignedEvent>
{
    private readonly ILogger<UserRoleAssignedEventHandler> _logger;

    public UserRoleAssignedEventHandler(ILogger<UserRoleAssignedEventHandler> logger)
    {
        _logger = logger;
    }

    public Task Handle(UserRoleAssignedEvent notification, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "User {UserId} assigned role {Role} in tenant {TenantId} by user {AssignedBy}",
            notification.UserId,
            notification.Role,
            notification.TenantId,
            notification.AssignedBy);

        // TODO: Add to audit log
        // TODO: Invalidate user's cached permissions
        // TODO: Send notification to user

        return Task.CompletedTask;
    }
}
```

**Estimated Time:** 30 minutes per handler (create 3-5 handlers)

---

### Step 5: Test Domain Events

**Test Script:**
```csharp
// Integration test
[Fact]
public async Task AssignUserRole_Should_Raise_UserRoleAssignedEvent()
{
    // Arrange
    var command = new AssignUserRoleCommand(userId, tenantId, TenantRole.Admin);

    // Act
    await _mediator.Send(command);

    // Assert
    // Verify event was raised and handled
    _mockLogger.Verify(
        x => x.LogInformation(
            It.Is<string>(s => s.Contains("User") && s.Contains("assigned role")),
            It.IsAny<object[]>()),
        Times.Once);
}
```

**Manual Test:**
1. Assign a role to a user via API
2. Check logs for "User {UserId} assigned role {Role}"
3. Verify event was published and handler executed

**Estimated Time:** 30 minutes

---

## 6. Priority Assessment

### Critical Events (Implement in Day 6)

| Event | Scenario | Handler Actions | Priority |
|-------|----------|----------------|----------|
| `UserRoleAssignedEvent` | Role assignment | Audit log, cache invalidation, notification | **CRITICAL** |
| `UserRemovedFromTenantEvent` | User removal | Audit log, revoke tokens, cleanup | **CRITICAL** |
| `TenantCreatedEvent` | Tenant registration | Audit log, send welcome email | **HIGH** |
| `UserCreatedEvent` | User registration | Audit log, send welcome email | **HIGH** |

### High Priority Events (Implement in Day 7)

| Event | Scenario | Handler Actions | Priority |
|-------|----------|----------------|----------|
| `UserLoggedInEvent` | Login success | Audit log, update LastLoginAt | **HIGH** |
| `RefreshTokenRevokedEvent` | Token revocation | Audit log, security notification | **HIGH** |
| `TenantSuspendedEvent` | Tenant suspension | Notify users, revoke all tokens | **HIGH** |
| `UserSuspendedEvent` | User suspension | Revoke tokens, audit log | **HIGH** |

### Medium Priority Events (Implement in Day 8+)

| Event | Scenario | Handler Actions | Priority |
|-------|----------|----------------|----------|
| `UserPasswordChangedEvent` | Password change | Audit log, revoke old tokens, email notification | MEDIUM |
| `TenantPlanUpgradedEvent` | Plan upgrade | Update limits, audit log, send invoice | MEDIUM |
| `SsoConfiguredEvent` | SSO setup | Audit log, notify admins | MEDIUM |

---

## 7. Risks & Mitigation

### Risk 1: Performance Impact
**Risk:** Dispatching many events could slow down SaveChangesAsync
**Mitigation:**
- Domain events are published in-process (fast)
- Consider async background processing for non-critical events (future)
- Monitor performance with logging

### Risk 2: Event Handler Failures
**Risk:** Event handler throws exception, entire transaction rolls back
**Mitigation:**
- Wrap event dispatching in try-catch
- Log exceptions but don't fail transaction
- Consider eventual consistency for non-critical handlers

### Risk 3: Event Ordering
**Risk:** Events might be processed out of order
**Mitigation:**
- Events are dispatched in the order they were raised (in single transaction)
- Use OccurredOn timestamp for ordering if needed
- Consider event sequence numbers (future)

### Risk 4: Missing Events
**Risk:** Forgetting to raise events in new features
**Mitigation:**
- Document event-raising conventions
- Code review checklist
- Integration tests to verify events are raised

---

## 8. Success Metrics

### Implementation Success Criteria

**Phase 1: Infrastructure (Day 6)**
- ✅ `SaveChangesAsync` override implemented in IdentityDbContext
- ✅ Domain events are being published (verified via logging)
- ✅ No breaking changes to existing functionality
- ✅ Unit tests pass

**Phase 2: Critical Handlers (Day 6-7)**
- ✅ 3-5 event handlers implemented and tested
- ✅ Audit logs are being created for critical operations
- ✅ Events are visible in application logs
- ✅ Integration tests verify event handling

**Phase 3: Full Coverage (Day 8+)**
- ✅ All 15+ events have at least one handler
- ✅ Audit logging complete for all major operations
- ✅ Cross-module events work correctly
- ✅ Performance impact is acceptable (<10ms per event)

---

## 9. Example: Complete Event Flow

### Scenario: User Role Assignment

**1. Domain Event Definition**
```csharp
// Domain/Aggregates/Users/Events/UserRoleAssignedEvent.cs
public sealed record UserRoleAssignedEvent(
    Guid UserId,
    TenantId TenantId,
    TenantRole Role,
    Guid AssignedBy
) : DomainEvent;
```

**2. Raise Event in Aggregate**
```csharp
// Domain/Aggregates/Users/User.cs
public class User : AggregateRoot
{
    public void AssignRole(TenantRole role, Guid assignedBy)
    {
        // Business logic validation
        if (Status == UserStatus.Deleted)
            throw new InvalidOperationException("Cannot assign role to deleted user");

        // Raise domain event
        AddDomainEvent(new UserRoleAssignedEvent(
            Id,
            TenantId,
            role,
            assignedBy));
    }
}
```

**3. Event Handler (Audit Logging)**
```csharp
// Application/EventHandlers/Users/UserRoleAssignedAuditHandler.cs
public class UserRoleAssignedAuditHandler : INotificationHandler<UserRoleAssignedEvent>
{
    private readonly IAuditLogRepository _auditLogRepository;

    public async Task Handle(UserRoleAssignedEvent notification, CancellationToken cancellationToken)
    {
        var auditLog = AuditLog.Create(
            entityType: "User",
            entityId: notification.UserId,
            action: $"RoleAssigned:{notification.Role}",
            performedBy: notification.AssignedBy,
            timestamp: notification.OccurredOn,
            tenantId: notification.TenantId);

        await _auditLogRepository.AddAsync(auditLog, cancellationToken);
    }
}
```

**4. Event Handler (Cache Invalidation)**
```csharp
// Application/EventHandlers/Users/UserRoleAssignedCacheHandler.cs
public class UserRoleAssignedCacheHandler : INotificationHandler<UserRoleAssignedEvent>
{
    private readonly IDistributedCache _cache;

    public async Task Handle(UserRoleAssignedEvent notification, CancellationToken cancellationToken)
    {
        // Invalidate user's permissions cache
        var cacheKey = $"user:permissions:{notification.UserId}";
        await _cache.RemoveAsync(cacheKey, cancellationToken);
    }
}
```

**5. Event Handler (Notification)**
```csharp
// Application/EventHandlers/Users/UserRoleAssignedNotificationHandler.cs
public class UserRoleAssignedNotificationHandler : INotificationHandler<UserRoleAssignedEvent>
{
    private readonly INotificationService _notificationService;

    public async Task Handle(UserRoleAssignedEvent notification, CancellationToken cancellationToken)
    {
        // Send notification to user
        await _notificationService.SendAsync(
            userId: notification.UserId,
            title: "Role Updated",
            message: $"Your role has been changed to {notification.Role}",
            cancellationToken);
    }
}
```

**6. Dispatching Flow**
```
User calls: POST /api/tenants/{tenantId}/users/{userId}/role

→ AssignUserRoleCommandHandler
  → user.AssignRole(role, currentUserId)
    → user.AddDomainEvent(new UserRoleAssignedEvent(...))
  → _userRepository.UpdateAsync(user)
    → _context.SaveChangesAsync()
      → DispatchDomainEventsAsync()
        → _mediator.Publish(UserRoleAssignedEvent)
          → UserRoleAssignedAuditHandler.Handle()
          → UserRoleAssignedCacheHandler.Handle()
          → UserRoleAssignedNotificationHandler.Handle()
      → base.SaveChangesAsync() // Commit transaction
```

---

## 10. Next Steps

### Immediate Actions (Day 6)

1. **Implement Domain Event Dispatching**
   - Override `SaveChangesAsync` in `IdentityDbContext`
   - Inject `IMediator` into DbContext
   - Test event dispatching with logging

2. **Create Missing Events**
   - `UserRoleAssignedEvent`
   - `UserRemovedFromTenantEvent`
   - `UserLoggedInEvent`

3. **Implement Critical Handlers**
   - `UserRoleAssignedEventHandler` (audit logging)
   - `TenantCreatedEventHandler` (audit logging)
   - `UserCreatedEventHandler` (audit logging)

### Follow-up Actions (Day 7-8)

4. **Expand Event Coverage**
   - Add handlers for all existing 11 domain events
   - Implement audit logging for all major operations
   - Add cache invalidation handlers where needed

5. **Testing & Validation**
   - Integration tests for event handling
   - Performance testing (event dispatching overhead)
   - Audit log verification

6. **Documentation**
   - Update architecture documentation
   - Document event-raising conventions
   - Create event handler development guide

---

## 11. Conclusion

### Summary

**Current State:**
- Domain event infrastructure: 80% complete
- Domain events defined: 11 events (sufficient for Day 1-6)
- Critical gap: Event dispatching not implemented

**Recommended Action:**
- Implement domain event dispatching in Day 6 (1 hour)
- Add critical event handlers alongside Day 6 features (2 hours)
- Complete event coverage in Day 7-8 (2-4 hours)

**Total Effort:** 5-7 hours spread across Days 6-8

**Value:**
- Complete audit trail for compliance
- Foundation for cross-module communication
- Side effects (notifications, cache invalidation)
- Event sourcing ready (future)

### Decision

**Proceed with Option C (Incremental Implementation)**
- Phase 1 (Day 6): Infrastructure + critical handlers
- Phase 2 (Day 7-8): Complete event coverage
- Phase 3 (Day 9+): Advanced features (background processing, event sourcing)

---

**Document Status:** ✅ Analysis Complete
**Recommended Decision:** Implement domain events incrementally starting Day 6
**Next Review:** After Phase 1 implementation
**Owner:** Backend Team
**Last Updated:** 2025-11-03
