Day 12 implementation - Complete CRUD operations with tenant isolation and SignalR integration.
**Domain Layer**:
- Added TenantId value object for strong typing
- Updated Project entity to include TenantId field
- Modified Project.Create factory method to require tenantId parameter
- Updated ProjectCreatedEvent to include TenantId
**Application Layer**:
- Created UpdateProjectCommand, Handler, and Validator for project updates
- Created ArchiveProjectCommand, Handler, and Validator for archiving projects
- Updated CreateProjectCommand to include TenantId
- Modified CreateProjectCommandValidator to remove OwnerId validation (set from JWT)
- Created IProjectNotificationService interface for SignalR abstraction
- Implemented ProjectCreatedEventHandler with SignalR notifications
- Implemented ProjectUpdatedEventHandler with SignalR notifications
- Implemented ProjectArchivedEventHandler with SignalR notifications
**Infrastructure Layer**:
- Updated PMDbContext to inject IHttpContextAccessor
- Configured Global Query Filter for automatic tenant isolation
- Added TenantId property mapping in ProjectConfiguration
- Created TenantId index for query performance
**API Layer**:
- Updated ProjectsController with [Authorize] attribute
- Implemented PUT /api/v1/projects/{id} for updates
- Implemented DELETE /api/v1/projects/{id} for archiving
- Added helper methods to extract TenantId and UserId from JWT claims
- Extended IRealtimeNotificationService with Project-specific methods
- Implemented RealtimeNotificationService with tenant-aware SignalR groups
- Created ProjectNotificationServiceAdapter to bridge layers
- Registered IProjectNotificationService in Program.cs
**Features Implemented**:
- Complete CRUD operations (Create, Read, Update, Archive)
- Multi-tenant isolation via EF Core Global Query Filter
- JWT-based authorization on all endpoints
- SignalR real-time notifications for all Project events
- Clean Architecture with proper layer separation
- Domain Event pattern with MediatR
**Database Migration**:
- Migration created (not applied yet): AddTenantIdToProject
**Test Scripts**:
- Created comprehensive test scripts (test-project-simple.ps1)
- Tests cover full CRUD lifecycle and tenant isolation
**Note**: API hot reload required to apply CreateProjectCommandValidator fix.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
56 lines
2.0 KiB
C#
56 lines
2.0 KiB
C#
using MediatR;
|
|
using Microsoft.Extensions.Logging;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.Events;
|
|
using ColaFlow.Modules.ProjectManagement.Domain.Repositories;
|
|
using ColaFlow.Modules.ProjectManagement.Application.Services;
|
|
|
|
namespace ColaFlow.Modules.ProjectManagement.Application.EventHandlers;
|
|
|
|
/// <summary>
|
|
/// Handler for ProjectUpdatedEvent - sends SignalR notification
|
|
/// </summary>
|
|
public class ProjectUpdatedEventHandler : INotificationHandler<ProjectUpdatedEvent>
|
|
{
|
|
private readonly IProjectNotificationService _notificationService;
|
|
private readonly IProjectRepository _projectRepository;
|
|
private readonly ILogger<ProjectUpdatedEventHandler> _logger;
|
|
|
|
public ProjectUpdatedEventHandler(
|
|
IProjectNotificationService notificationService,
|
|
IProjectRepository projectRepository,
|
|
ILogger<ProjectUpdatedEventHandler> logger)
|
|
{
|
|
_notificationService = notificationService;
|
|
_projectRepository = projectRepository;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task Handle(ProjectUpdatedEvent notification, CancellationToken cancellationToken)
|
|
{
|
|
_logger.LogInformation("Handling ProjectUpdatedEvent for project {ProjectId}", notification.ProjectId);
|
|
|
|
// Get full project to obtain TenantId
|
|
var project = await _projectRepository.GetByIdAsync(notification.ProjectId, cancellationToken);
|
|
if (project == null)
|
|
{
|
|
_logger.LogWarning("Project {ProjectId} not found for update notification", notification.ProjectId);
|
|
return;
|
|
}
|
|
|
|
var projectData = new
|
|
{
|
|
Id = notification.ProjectId.Value,
|
|
Name = notification.Name,
|
|
Description = notification.Description,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
await _notificationService.NotifyProjectUpdated(
|
|
project.TenantId.Value,
|
|
notification.ProjectId.Value,
|
|
projectData);
|
|
|
|
_logger.LogInformation("SignalR notification sent for updated project {ProjectId}", notification.ProjectId);
|
|
}
|
|
}
|