> For the complete documentation index, see [llms.txt](https://docs.flexbase.in/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flexbase.in/sample-references-domain/chat-app.md).

# Chat App

## 📋 Overview

This comprehensive implementation guideline provides a complete chat application using Flexbase architecture patterns, following Single Responsibility Principle (SRP) and integrating seamlessly with your existing EBusiness project structure.

***

## 🏗️ Project Structure

### **Chat Module Organization**

```
EBusiness.Application/
├── Domain/
│   └── EBusiness.DomainModels/
│       └── Chat/
│           ├── Chat.cs
│           ├── Chat_SeedData.cs
│           ├── Message.cs
│           ├── Message_SeedData.cs
│           ├── ChatParticipant.cs
│           ├── ChatParticipant_SeedData.cs
│           ├── MessageReaction.cs
│           ├── MessageAttachment.cs
│           └── ChatSettings.cs
├── DomainHandler/
│   └── EBusiness.Handlers/
│       ├── CommandHandlers/
│       │   ├── Chat/
│       │   │   ├── CreateChatHandler.cs
│       │   │   ├── SendMessageHandler.cs
│       │   │   ├── AddParticipantHandler.cs
│       │   │   ├── RemoveParticipantHandler.cs
│       │   │   ├── UpdateChatSettingsHandler.cs
│       │   │   ├── ArchiveChatHandler.cs
│       │   │   ├── DeleteChatHandler.cs
│       │   │   ├── CreateGroupChatHandler.cs
│       │   │   ├── InviteToGroupChatHandler.cs
│       │   │   ├── LeaveGroupChatHandler.cs
│       │   │   ├── UpdateGroupChatInfoHandler.cs
│       │   │   └── PromoteToAdminHandler.cs
│       │   ├── Message/
│       │   │   ├── UpdateMessageStatusHandler.cs
│       │   │   ├── AddMessageReactionHandler.cs
│       │   │   ├── RemoveMessageReactionHandler.cs
│       │   │   ├── DeleteMessageHandler.cs
│       │   │   └── EditMessageHandler.cs
│       │   ├── Media/
│       │   │   ├── UploadMediaHandler.cs
│       │   │   ├── DeleteMediaHandler.cs
│       │   │   ├── GenerateThumbnailHandler.cs
│       │   │   ├── ProcessImageHandler.cs
│       │   │   ├── ProcessVideoHandler.cs
│       │   │   ├── ProcessAudioHandler.cs
│       │   │   ├── ProcessFileHandler.cs
│       │   │   ├── GenerateLinkPreviewHandler.cs
│       │   │   └── CompressMediaHandler.cs
│       │   └── RealTime/
│       │       ├── JoinChatHandler.cs
│       │       ├── LeaveChatHandler.cs
│       │       ├── UpdateTypingStatusHandler.cs
│       │       └── UpdateUserOnlineStatusHandler.cs
│       └── Subscribers/
│           ├── Chat/
│           │   ├── NotifyParticipantsOnChatCreated.cs
│           │   ├── NotifyParticipantsOnMessageSent.cs
│           │   ├── NotifyParticipantsOnParticipantAdded.cs
│           │   ├── NotifyParticipantsOnParticipantRemoved.cs
│           │   ├── UpdateChatLastMessageOnMessageSent.cs
│           │   ├── UpdateChatStatisticsOnMessageSent.cs
│           │   ├── NotifyMentionedUsersOnMessageSent.cs
│           │   ├── NotifyGroupChatCreated.cs
│           │   ├── NotifyGroupChatInvitation.cs
│           │   └── NotifyGroupChatLeft.cs
│           ├── Message/
│           │   ├── SendPushNotificationOnMessageSent.cs
│           │   ├── UpdateMessageDeliveryStatus.cs
│           │   ├── UpdateMessageReadStatus.cs
│           │   ├── ProcessMessageAnalytics.cs
│           │   └── ArchiveOldMessages.cs
│           ├── Media/
│           │   ├── ProcessMediaUpload.cs
│           │   ├── GenerateMediaThumbnail.cs
│           │   ├── CleanupUnusedMedia.cs
│           │   ├── ProcessImageUpload.cs
│           │   ├── ProcessVideoUpload.cs
│           │   ├── ProcessAudioUpload.cs
│           │   ├── ProcessFileUpload.cs
│           │   ├── GenerateLinkPreview.cs
│           │   ├── CompressMediaFiles.cs
│           │   └── ScanMediaForViruses.cs
│           └── RealTime/
│               ├── UpdateUserOnlineStatus.cs
│               ├── BroadcastTypingStatus.cs
│               ├── UpdateChatPresence.cs
│               └── ProcessRealTimeEvents.cs
├── ControlContracts/
│   └── EBusiness.DTOs/
│       ├── DomainDtos/
│       │   ├── Chat/
│       │   │   ├── ChatDto.cs
│       │   │   └── ChatDtoDtoWithId.cs
│       │   ├── Message/
│       │   │   ├── MessageDto.cs
│       │   │   └── MessageDtoDtoWithId.cs
│       │   ├── ChatParticipant/
│       │   │   ├── ChatParticipantDto.cs
│       │   │   └── ChatParticipantDtoDtoWithId.cs
│       │   └── MessageReaction/
│       │       ├── MessageReactionDto.cs
│       │       └── MessageReactionDtoDtoWithId.cs
│       └── FeatureDtos/
│           ├── Chat/
│           │   ├── Input/
│           │   │   ├── CreateChat/
│           │   │   │   └── CreateChatDto.cs
│           │   │   ├── SendMessage/
│           │   │   │   └── SendMessageDto.cs
│           │   │   ├── AddParticipant/
│           │   │   │   └── AddParticipantDto.cs
│           │   │   └── UpdateChatSettings/
│           │   │       └── UpdateChatSettingsDto.cs
│           │   └── Output/
│           │       ├── GetChatMessages/
│           │       │   └── GetChatMessagesDto.cs
│           │       ├── GetUserChats/
│           │       │   └── GetUserChatsDto.cs
│           │       └── GetChatParticipants/
│           │           └── GetChatParticipantsDto.cs
│           └── Message/
│               ├── Input/
│               │   ├── UpdateMessageStatus/
│               │   │   └── UpdateMessageStatusDto.cs
│               │   ├── AddMessageReaction/
│               │   │   └── AddMessageReactionDto.cs
│               │   └── EditMessage/
│               │       └── EditMessageDto.cs
│               └── Output/
│                   └── GetMessageHistory/
│                       └── GetMessageHistoryDto.cs
└── Infrastructure/
    └── EventHandlers/
        ├── IHybridEventService.cs
        ├── HybridEventService.cs
        ├── RedisStreamProcessor.cs
        └── ChatSignalRHub.cs

EBusiness.Framework/
└── EventHandlers/
    ├── ChatEventHandlers/
    │   ├── ChatCreatedEventHandler.cs
    │   ├── MessageSentEventHandler.cs
    │   ├── UserOnlineEventHandler.cs
    │   └── TypingStatusChangedEventHandler.cs
    └── BackgroundProcessors/
        ├── MessageArchivalProcessor.cs
        ├── MediaCleanupProcessor.cs
        └── AnalyticsProcessor.cs
```

***

## 🎯 Domain Models Implementation

### **1. Chat Domain Model**

```csharp
// EBusiness.Application/Domain/EBusiness.DomainModels/Chat/Chat.cs
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations;

namespace EBusiness
{
    public partial class Chat : DomainModelBridge
    {
        protected readonly ILogger<Chat> _logger;

        protected Chat()
        {
        }

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

        #region "Attributes"

        #region "Public"
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public ChatType ChatType { get; set; }
        public ChatStatus Status { get; set; } = ChatStatus.Active;
        public string CreatedBy { get; set; }
        public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
        public DateTime? LastMessageAt { get; set; }
        public ChatSettings Settings { get; set; } = new();
        #endregion

        #region "Protected"
        #endregion

        #region "Private"
        #endregion

        #endregion

        #region "Business Logic Methods"

        public virtual Chat CreateChat(CreateChatCommand cmd)
        {
            // Business Rules
            if (string.IsNullOrWhiteSpace(cmd.Dto.Name))
                throw new BusinessException("Chat name cannot be empty");
            
            if (cmd.Dto.ChatType == ChatType.Group && cmd.Dto.ParticipantIds.Count < 2)
                throw new BusinessException("Group chat must have at least 2 participants");

            // Create chat
            Id = Guid.NewGuid().ToString();
            Name = cmd.Dto.Name;
            Description = cmd.Dto.Description;
            ChatType = cmd.Dto.ChatType;
            CreatedBy = cmd.Dto.CreatedBy;
            CreatedAt = DateTime.UtcNow;
            Settings = new ChatSettings
            {
                AllowFileSharing = cmd.Dto.AllowFileSharing,
                AllowVoiceMessages = cmd.Dto.AllowVoiceMessages,
                MaxParticipants = cmd.Dto.MaxParticipants
            };

            return this;
        }

        public virtual Message SendMessage(SendMessageCommand cmd)
        {
            // Business Rules
            if (Status != ChatStatus.Active)
                throw new BusinessException("Cannot send message to inactive chat");

            // Check if user is participant
            var participant = Participants.FirstOrDefault(p => p.UserId == cmd.Dto.SenderId);
            if (participant == null)
                throw new BusinessException("User is not a participant in this chat");

            if (participant.Status != ChatParticipantStatus.Active)
                throw new BusinessException("User is not active in this chat");

            // Process @ mentions for group chats
            var processedContent = cmd.Dto.Content;
            var mentionedUserIds = new List<string>();
            
            if (ChatType == ChatType.Group && !string.IsNullOrEmpty(cmd.Dto.Content))
            {
                var mentionResult = ProcessMentions(cmd.Dto.Content, cmd.Dto.SenderId);
                processedContent = mentionResult.ProcessedContent;
                mentionedUserIds = mentionResult.MentionedUserIds;
            }

            // Create message
            var message = new Message
            {
                Id = Guid.NewGuid().ToString(),
                ChatId = Id,
                SenderId = cmd.Dto.SenderId,
                Content = processedContent,
                MessageType = cmd.Dto.MessageType,
                Status = MessageStatus.Sending,
                SentAt = DateTime.UtcNow,
                ReplyToMessageId = cmd.Dto.ReplyToMessageId,
                Metadata = cmd.Dto.Metadata
            };

            // Add mentions to metadata
            if (mentionedUserIds.Any())
            {
                message.Metadata["mentions"] = mentionedUserIds;
                message.Metadata["hasMentions"] = true;
            }

            LastMessageAt = DateTime.UtcNow;
            return message;
        }

        public virtual MentionResult ProcessMentions(string content, string senderId)
        {
            var mentionedUserIds = new List<string>();
            var processedContent = content;
            
            // Find @ mentions in content (e.g., @john.doe, @user123)
            var mentionPattern = @"@([a-zA-Z0-9._-]+)";
            var matches = Regex.Matches(content, mentionPattern);
            
            foreach (Match match in matches)
            {
                var mentionText = match.Groups[1].Value;
                
                // Find user by username or display name
                var mentionedUser = FindUserByMention(mentionText);
                if (mentionedUser != null && mentionedUser.Id != senderId)
                {
                    mentionedUserIds.Add(mentionedUser.Id);
                    
                    // Replace @username with @DisplayName for better UX
                    processedContent = processedContent.Replace(
                        match.Value, 
                        $"@{mentionedUser.DisplayName}");
                }
            }
            
            return new MentionResult
            {
                ProcessedContent = processedContent,
                MentionedUserIds = mentionedUserIds
            };
        }

        private User FindUserByMention(string mentionText)
        {
            // This would typically query a user service
            // For now, return a mock implementation
            return new User { Id = "user123", DisplayName = mentionText };
        }

        public virtual void AddParticipant(AddParticipantCommand cmd)
        {
            // Business Rules
            if (ChatType == ChatType.Individual && Participants.Count >= 2)
                throw new BusinessException("Individual chat cannot have more than 2 participants");

            if (ChatType == ChatType.Group && Participants.Count >= Settings.MaxParticipants)
                throw new BusinessException("Group chat has reached maximum participants");

            var participant = new ChatParticipant
            {
                Id = Guid.NewGuid().ToString(),
                ChatId = Id,
                UserId = cmd.Dto.UserId,
                Role = cmd.Dto.Role,
                Status = ChatParticipantStatus.Active,
                JoinedAt = DateTime.UtcNow
            };

            // Add to collection (handled by EF Core)
        }

        public virtual void UpdateSettings(UpdateChatSettingsCommand cmd)
        {
            // Business Rules
            if (cmd.Dto.UserId != CreatedBy)
                throw new BusinessException("Only chat creator can update settings");

            Settings.AllowFileSharing = cmd.Dto.AllowFileSharing;
            Settings.AllowVoiceMessages = cmd.Dto.AllowVoiceMessages;
            Settings.MaxParticipants = cmd.Dto.MaxParticipants;
        }

        #endregion

        #region "Navigation Properties"
        public virtual ICollection<Message> Messages { get; set; } = new List<Message>();
        public virtual ICollection<ChatParticipant> Participants { get; set; } = new List<ChatParticipant>();
        #endregion
    }

    public enum ChatType
    {
        Individual,
        Group,
        Channel
    }

    public enum ChatStatus
    {
        Active,
        Archived,
        Deleted
    }

    public class ChatSettings
    {
        public bool AllowFileSharing { get; set; } = true;
        public bool AllowVoiceMessages { get; set; } = true;
        public int MaxParticipants { get; set; } = 100;
        public bool AllowInvites { get; set; } = true;
        public bool AllowMentions { get; set; } = true;
        public bool AllowReactions { get; set; } = true;
        public bool AllowMessageEditing { get; set; } = true;
        public int MessageEditTimeLimitMinutes { get; set; } = 15;
    }

    public class MentionResult
    {
        public string ProcessedContent { get; set; }
        public List<string> MentionedUserIds { get; set; } = new();
    }

    public class User
    {
        public string Id { get; set; }
        public string DisplayName { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
        public bool IsOnline { get; set; }
        public DateTime? LastSeenAt { get; set; }
    }

    public class MessageAttachment
    {
        public string Id { get; set; }
        public string MessageId { get; set; }
        public string FileName { get; set; }
        public string FileType { get; set; }
        public long FileSize { get; set; }
        public string FilePath { get; set; }
        public string ThumbnailPath { get; set; }
        public string EncryptionKey { get; set; }
        public Dictionary<string, object> Metadata { get; set; } = new();
        public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    }

    public class HyperlinkInfo
    {
        public string Url { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string ImageUrl { get; set; }
        public string Domain { get; set; }
    }

    public class LinkPreview
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string ImageUrl { get; set; }
    }
}
```

### **2. Message Domain Model**

```csharp
// EBusiness.Application/Domain/EBusiness.DomainModels/Message/Message.cs
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations;

namespace EBusiness
{
    public partial class Message : DomainModelBridge
    {
        protected readonly ILogger<Message> _logger;

        protected Message()
        {
        }

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

        #region "Attributes"

        #region "Public"
        public string Id { get; set; }
        public string ChatId { get; set; }
        public string SenderId { get; set; }
        public string Content { get; set; }
        public MessageType MessageType { get; set; }
        public MessageStatus Status { get; set; } = MessageStatus.Sending;
        public DateTime SentAt { get; set; } = DateTime.UtcNow;
        public DateTime? DeliveredAt { get; set; }
        public DateTime? ReadAt { get; set; }
        public string ReplyToMessageId { get; set; }
        public Dictionary<string, object> Metadata { get; set; } = new();
        #endregion

        #region "Protected"
        #endregion

        #region "Private"
        #endregion

        #endregion

        #region "Business Logic Methods"

        public virtual void MarkAsDelivered()
        {
            if (Status == MessageStatus.Sending)
            {
                Status = MessageStatus.Delivered;
                DeliveredAt = DateTime.UtcNow;
            }
        }

        public virtual void MarkAsRead()
        {
            if (Status == MessageStatus.Delivered)
            {
                Status = MessageStatus.Read;
                ReadAt = DateTime.UtcNow;
            }
        }

        public virtual void AddReaction(AddMessageReactionCommand cmd)
        {
            var existingReaction = Reactions.FirstOrDefault(r => r.UserId == cmd.Dto.UserId);
            if (existingReaction != null)
            {
                existingReaction.Emoji = cmd.Dto.Emoji;
                existingReaction.UpdatedAt = DateTime.UtcNow;
            }
            else
            {
                Reactions.Add(new MessageReaction
                {
                    Id = Guid.NewGuid().ToString(),
                    MessageId = Id,
                    UserId = cmd.Dto.UserId,
                    Emoji = cmd.Dto.Emoji,
                    CreatedAt = DateTime.UtcNow
                });
            }
        }

        public virtual void EditMessage(EditMessageCommand cmd)
        {
            if (cmd.Dto.UserId != SenderId)
                throw new BusinessException("Only message sender can edit message");

            if (DateTime.UtcNow.Subtract(SentAt).TotalMinutes > 15)
                throw new BusinessException("Message can only be edited within 15 minutes");

            Content = cmd.Dto.NewContent;
            Metadata["edited"] = true;
            Metadata["editedAt"] = DateTime.UtcNow;
        }

        public virtual void AddMediaAttachment(AddMediaAttachmentCommand cmd)
        {
            // Business Rules
            if (MessageType != MessageType.Image && MessageType != MessageType.Video && 
                MessageType != MessageType.Audio && MessageType != MessageType.File)
                throw new BusinessException("Media attachments only allowed for media message types");

            var attachment = new MessageAttachment
            {
                Id = Guid.NewGuid().ToString(),
                MessageId = Id,
                FileName = cmd.Dto.FileName,
                FileType = cmd.Dto.FileType,
                FileSize = cmd.Dto.FileSize,
                FilePath = cmd.Dto.FilePath,
                ThumbnailPath = cmd.Dto.ThumbnailPath,
                EncryptionKey = cmd.Dto.EncryptionKey,
                Metadata = cmd.Dto.Metadata
            };

            Attachments.Add(attachment);
        }

        public virtual void ProcessHyperlinks()
        {
            if (MessageType != MessageType.Text || string.IsNullOrEmpty(Content))
                return;

            // Extract hyperlinks from content
            var hyperlinkPattern = @"(https?://[^\s]+)";
            var matches = Regex.Matches(Content, hyperlinkPattern);
            
            var hyperlinks = new List<HyperlinkInfo>();
            foreach (Match match in matches)
            {
                var url = match.Value;
                var preview = GenerateLinkPreview(url);
                
                hyperlinks.Add(new HyperlinkInfo
                {
                    Url = url,
                    Title = preview.Title,
                    Description = preview.Description,
                    ImageUrl = preview.ImageUrl,
                    Domain = new Uri(url).Host
                });
            }

            if (hyperlinks.Any())
            {
                Metadata["hyperlinks"] = hyperlinks;
                Metadata["hasHyperlinks"] = true;
            }
        }

        private LinkPreview GenerateLinkPreview(string url)
        {
            // This would typically call a link preview service
            // For now, return a mock implementation
            return new LinkPreview
            {
                Title = "Sample Link Title",
                Description = "Sample link description",
                ImageUrl = "https://example.com/preview.jpg"
            };
        }

        #endregion

        #region "Navigation Properties"
        public virtual ICollection<MessageReaction> Reactions { get; set; } = new List<MessageReaction>();
        public virtual ICollection<MessageAttachment> Attachments { get; set; } = new List<MessageAttachment>();
        #endregion
    }

    public enum MessageType
    {
        Text,
        Image,
        Video,
        Audio,
        File,
        Voice,
        Location,
        Contact
    }

    public enum MessageStatus
    {
        Sending,
        Delivered,
        Read,
        Failed
    }
}
```

***

## 🎯 Command Handlers Implementation

### **1. Chat Command Handlers**

#### **CreateChatHandler (SRP: Chat Creation)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/CommandHandlers/Chat/CreateChatHandler.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.ChatModule
{
    public partial class CreateChatHandler : ICreateChatHandler
    {
        protected string EventCondition = "";
        protected readonly ILogger<CreateChatHandler> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected readonly IHybridEventService _eventService;

        protected Chat? _model;
        protected FlexAppContextBridge? _flexAppContext;

        public CreateChatHandler(
            ILogger<CreateChatHandler> logger, 
            IFlexHost flexHost, 
            RepoFactory repoFactory,
            IHybridEventService eventService)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
            _eventService = eventService;
        }

        public virtual async Task Execute(CreateChatCommand cmd, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = cmd.Dto.GetAppContext();
                _repoFactory.Init(cmd.Dto);

                // Create chat using domain logic
                _model = _flexHost.GetDomainModel<Chat>().CreateChat(cmd);

                // Persist to database
                _repoFactory.GetRepo().InsertOrUpdate(_model);
                int records = await _repoFactory.GetRepo().SaveAsync();

                if (records > 0)
                {
                    _logger.LogDebug("Chat {ChatId} created successfully", _model.Id);
                    
                    // Publish to real-time streams
                    await _eventService.PublishChatCreatedAsync(_model);
                    
                    EventCondition = "ChatCreated";
                }
                else
                {
                    _logger.LogWarning("Failed to create chat");
                    EventCondition = "ChatCreationFailed";
                }

                await this.Fire(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating chat");
                EventCondition = "ChatCreationFailed";
                await this.Fire(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

#### **SendMessageHandler (SRP: Message Sending)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/CommandHandlers/Chat/SendMessageHandler.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.ChatModule
{
    public partial class SendMessageHandler : ISendMessageHandler
    {
        protected string EventCondition = "";
        protected readonly ILogger<SendMessageHandler> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected readonly IHybridEventService _eventService;

        protected Chat? _chat;
        protected Message? _message;
        protected FlexAppContextBridge? _flexAppContext;

        public SendMessageHandler(
            ILogger<SendMessageHandler> logger, 
            IFlexHost flexHost, 
            RepoFactory repoFactory,
            IHybridEventService eventService)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
            _eventService = eventService;
        }

        public virtual async Task Execute(SendMessageCommand cmd, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = cmd.Dto.GetAppContext();
                _repoFactory.Init(cmd.Dto);

                // Get chat and send message
                _chat = _flexHost.GetDomainModel<Chat>().GetById(cmd.Dto.ChatId);
                if (_chat == null)
                    throw new BusinessException("Chat not found");

                _message = _chat.SendMessage(cmd);

                // Persist to database
                _repoFactory.GetRepo().InsertOrUpdate(_message);
                int records = await _repoFactory.GetRepo().SaveAsync();

                if (records > 0)
                {
                    _logger.LogDebug("Message {MessageId} sent to chat {ChatId}", _message.Id, _chat.Id);
                    
                    // Publish to real-time streams
                    await _eventService.PublishRealtimeMessageAsync(_message);
                    
                    // Publish to NServiceBus for reliable processing
                    await _eventService.PublishMessageEventAsync(_message);
                    
                    EventCondition = "MessageSent";
                }
                else
                {
                    _logger.LogWarning("Failed to save message {MessageId}", _message.Id);
                    EventCondition = "MessageFailed";
                }

                await this.Fire(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending message to chat {ChatId}", cmd.Dto.ChatId);
                EventCondition = "MessageFailed";
                await this.Fire(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

### **2. Group Chat Command Handlers**

#### **CreateGroupChatHandler (SRP: Group Chat Creation)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/CommandHandlers/Chat/CreateGroupChatHandler.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.ChatModule
{
    public partial class CreateGroupChatHandler : ICreateGroupChatHandler
    {
        protected string EventCondition = "";
        protected readonly ILogger<CreateGroupChatHandler> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected readonly IHybridEventService _eventService;

        protected Chat? _model;
        protected FlexAppContextBridge? _flexAppContext;

        public CreateGroupChatHandler(
            ILogger<CreateGroupChatHandler> logger, 
            IFlexHost flexHost, 
            RepoFactory repoFactory,
            IHybridEventService eventService)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
            _eventService = eventService;
        }

        public virtual async Task Execute(CreateGroupChatCommand cmd, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = cmd.Dto.GetAppContext();
                _repoFactory.Init(cmd.Dto);

                // Create group chat using domain logic
                _model = _flexHost.GetDomainModel<Chat>().CreateGroupChat(cmd);

                // Persist to database
                _repoFactory.GetRepo().InsertOrUpdate(_model);
                int records = await _repoFactory.GetRepo().SaveAsync();

                if (records > 0)
                {
                    _logger.LogDebug("Group chat {ChatId} created successfully", _model.Id);
                    
                    // Publish to real-time streams
                    await _eventService.PublishGroupChatCreatedAsync(_model);
                    
                    EventCondition = "GroupChatCreated";
                }
                else
                {
                    _logger.LogWarning("Failed to create group chat");
                    EventCondition = "GroupChatCreationFailed";
                }

                await this.Fire(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating group chat");
                EventCondition = "GroupChatCreationFailed";
                await this.Fire(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

#### **InviteToGroupChatHandler (SRP: Group Chat Invitations)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/CommandHandlers/Chat/InviteToGroupChatHandler.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.ChatModule
{
    public partial class InviteToGroupChatHandler : IInviteToGroupChatHandler
    {
        protected string EventCondition = "";
        protected readonly ILogger<InviteToGroupChatHandler> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected readonly IHybridEventService _eventService;

        protected Chat? _chat;
        protected FlexAppContextBridge? _flexAppContext;

        public InviteToGroupChatHandler(
            ILogger<InviteToGroupChatHandler> logger, 
            IFlexHost flexHost, 
            RepoFactory repoFactory,
            IHybridEventService eventService)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
            _eventService = eventService;
        }

        public virtual async Task Execute(InviteToGroupChatCommand cmd, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = cmd.Dto.GetAppContext();
                _repoFactory.Init(cmd.Dto);

                // Get group chat
                _chat = _flexHost.GetDomainModel<Chat>().GetById(cmd.Dto.ChatId);
                if (_chat == null)
                    throw new BusinessException("Group chat not found");

                if (_chat.ChatType != ChatType.Group)
                    throw new BusinessException("Chat is not a group chat");

                // Check if inviter is admin
                var inviter = _chat.Participants.FirstOrDefault(p => p.UserId == cmd.Dto.InviterId);
                if (inviter == null || inviter.Role != ChatParticipantRole.Admin)
                    throw new BusinessException("Only admins can invite users to group chat");

                // Add participants
                foreach (var userId in cmd.Dto.InvitedUserIds)
                {
                    _chat.AddParticipant(new AddParticipantCommand
                    {
                        Dto = new AddParticipantDto
                        {
                            UserId = userId,
                            Role = ChatParticipantRole.Member
                        }
                    });
                }

                // Persist to database
                _repoFactory.GetRepo().InsertOrUpdate(_chat);
                int records = await _repoFactory.GetRepo().SaveAsync();

                if (records > 0)
                {
                    _logger.LogDebug("Users invited to group chat {ChatId}", _chat.Id);
                    
                    // Publish to real-time streams
                    await _eventService.PublishGroupChatInvitationAsync(_chat, cmd.Dto.InvitedUserIds);
                    
                    EventCondition = "GroupChatInvitationSent";
                }
                else
                {
                    _logger.LogWarning("Failed to invite users to group chat {ChatId}", _chat.Id);
                    EventCondition = "GroupChatInvitationFailed";
                }

                await this.Fire(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error inviting users to group chat {ChatId}", cmd.Dto.ChatId);
                EventCondition = "GroupChatInvitationFailed";
                await this.Fire(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

### **3. Media Command Handlers**

#### **UploadMediaHandler (SRP: Media Upload)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/CommandHandlers/Media/UploadMediaHandler.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.MediaModule
{
    public partial class UploadMediaHandler : IUploadMediaHandler
    {
        protected string EventCondition = "";
        protected readonly ILogger<UploadMediaHandler> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected readonly IHybridEventService _eventService;
        protected readonly IMediaStorageService _mediaStorageService;

        protected Message? _message;
        protected MessageAttachment? _attachment;
        protected FlexAppContextBridge? _flexAppContext;

        public UploadMediaHandler(
            ILogger<UploadMediaHandler> logger, 
            IFlexHost flexHost, 
            RepoFactory repoFactory,
            IHybridEventService eventService,
            IMediaStorageService mediaStorageService)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
            _eventService = eventService;
            _mediaStorageService = mediaStorageService;
        }

        public virtual async Task Execute(UploadMediaCommand cmd, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = cmd.Dto.GetAppContext();
                _repoFactory.Init(cmd.Dto);

                // Validate file type and size
                ValidateMediaFile(cmd.Dto);

                // Upload file to storage
                var uploadResult = await _mediaStorageService.UploadMediaAsync(cmd.Dto.FileStream, cmd.Dto.FileName, cmd.Dto.ContentType);

                // Create message with media
                _message = new Message
                {
                    Id = Guid.NewGuid().ToString(),
                    ChatId = cmd.Dto.ChatId,
                    SenderId = cmd.Dto.SenderId,
                    Content = cmd.Dto.Caption ?? "",
                    MessageType = GetMessageTypeFromContentType(cmd.Dto.ContentType),
                    Status = MessageStatus.Sending,
                    SentAt = DateTime.UtcNow
                };

                // Add media attachment
                _message.AddMediaAttachment(new AddMediaAttachmentCommand
                {
                    Dto = new AddMediaAttachmentDto
                    {
                        FileName = cmd.Dto.FileName,
                        FileType = cmd.Dto.ContentType,
                        FileSize = cmd.Dto.FileSize,
                        FilePath = uploadResult.FilePath,
                        ThumbnailPath = uploadResult.ThumbnailPath,
                        EncryptionKey = uploadResult.EncryptionKey,
                        Metadata = new Dictionary<string, object>
                        {
                            ["originalFileName"] = cmd.Dto.OriginalFileName,
                            ["uploadedAt"] = DateTime.UtcNow,
                            ["fileHash"] = uploadResult.FileHash
                        }
                    }
                });

                // Process hyperlinks if any
                _message.ProcessHyperlinks();

                // Persist to database
                _repoFactory.GetRepo().InsertOrUpdate(_message);
                int records = await _repoFactory.GetRepo().SaveAsync();

                if (records > 0)
                {
                    _logger.LogDebug("Media uploaded successfully: {FileName}", cmd.Dto.FileName);
                    
                    // Publish to real-time streams
                    await _eventService.PublishRealtimeMessageAsync(_message);
                    
                    // Publish to NServiceBus for reliable processing
                    await _eventService.PublishMessageEventAsync(_message);
                    
                    EventCondition = "MediaUploaded";
                }
                else
                {
                    _logger.LogWarning("Failed to save media message");
                    EventCondition = "MediaUploadFailed";
                }

                await this.Fire(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error uploading media: {FileName}", cmd.Dto.FileName);
                EventCondition = "MediaUploadFailed";
                await this.Fire(EventCondition, serviceBusContext);
                throw;
            }
        }

        private void ValidateMediaFile(UploadMediaDto dto)
        {
            // File size validation
            var maxSize = GetMaxFileSize(dto.ContentType);
            if (dto.FileSize > maxSize)
                throw new BusinessException($"File size exceeds maximum allowed size of {maxSize / (1024 * 1024)}MB");

            // File type validation
            var allowedTypes = GetAllowedFileTypes();
            if (!allowedTypes.Contains(dto.ContentType))
                throw new BusinessException($"File type {dto.ContentType} is not allowed");

            // File name validation
            if (string.IsNullOrWhiteSpace(dto.FileName) || dto.FileName.Length > 255)
                throw new BusinessException("Invalid file name");
        }

        private long GetMaxFileSize(string contentType)
        {
            return contentType switch
            {
                "image/jpeg" or "image/png" or "image/gif" => 10 * 1024 * 1024, // 10MB
                "video/mp4" or "video/avi" or "video/mov" => 100 * 1024 * 1024, // 100MB
                "audio/mp3" or "audio/wav" or "audio/ogg" => 50 * 1024 * 1024, // 50MB
                _ => 25 * 1024 * 1024 // 25MB default
            };
        }

        private List<string> GetAllowedFileTypes()
        {
            return new List<string>
            {
                // Images
                "image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml",
                // Videos
                "video/mp4", "video/avi", "video/mov", "video/wmv", "video/webm",
                // Audio
                "audio/mp3", "audio/wav", "audio/ogg", "audio/aac", "audio/m4a",
                // Documents
                "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                "text/plain", "text/csv"
            };
        }

        private MessageType GetMessageTypeFromContentType(string contentType)
        {
            return contentType switch
            {
                var type when type.StartsWith("image/") => MessageType.Image,
                var type when type.StartsWith("video/") => MessageType.Video,
                var type when type.StartsWith("audio/") => MessageType.Audio,
                _ => MessageType.File
            };
        }
    }
}
```

#### **GenerateLinkPreviewHandler (SRP: Link Preview Generation)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/CommandHandlers/Media/GenerateLinkPreviewHandler.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.MediaModule
{
    public partial class GenerateLinkPreviewHandler : IGenerateLinkPreviewHandler
    {
        protected string EventCondition = "";
        protected readonly ILogger<GenerateLinkPreviewHandler> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected readonly IHybridEventService _eventService;
        protected readonly ILinkPreviewService _linkPreviewService;

        protected Message? _message;
        protected FlexAppContextBridge? _flexAppContext;

        public GenerateLinkPreviewHandler(
            ILogger<GenerateLinkPreviewHandler> logger, 
            IFlexHost flexHost, 
            RepoFactory repoFactory,
            IHybridEventService eventService,
            ILinkPreviewService linkPreviewService)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
            _eventService = eventService;
            _linkPreviewService = linkPreviewService;
        }

        public virtual async Task Execute(GenerateLinkPreviewCommand cmd, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = cmd.Dto.GetAppContext();
                _repoFactory.Init(cmd.Dto);

                // Get message
                _message = _flexHost.GetDomainModel<Message>().GetById(cmd.Dto.MessageId);
                if (_message == null)
                    throw new BusinessException("Message not found");

                // Generate link previews for all URLs in the message
                var hyperlinks = new List<HyperlinkInfo>();
                var urlPattern = @"(https?://[^\s]+)";
                var matches = Regex.Matches(_message.Content, urlPattern);

                foreach (Match match in matches)
                {
                    var url = match.Value;
                    try
                    {
                        var preview = await _linkPreviewService.GeneratePreviewAsync(url);
                        hyperlinks.Add(new HyperlinkInfo
                        {
                            Url = url,
                            Title = preview.Title,
                            Description = preview.Description,
                            ImageUrl = preview.ImageUrl,
                            Domain = new Uri(url).Host
                        });
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, "Failed to generate preview for URL: {Url}", url);
                        // Continue with other URLs
                    }
                }

                // Update message metadata with hyperlinks
                if (hyperlinks.Any())
                {
                    _message.Metadata["hyperlinks"] = hyperlinks;
                    _message.Metadata["hasHyperlinks"] = true;
                    _message.Metadata["linkPreviewGeneratedAt"] = DateTime.UtcNow;

                    // Persist to database
                    _repoFactory.GetRepo().InsertOrUpdate(_message);
                    int records = await _repoFactory.GetRepo().SaveAsync();

                    if (records > 0)
                    {
                        _logger.LogDebug("Link previews generated for message {MessageId}", _message.Id);
                        
                        // Publish to real-time streams
                        await _eventService.PublishLinkPreviewGeneratedAsync(_message);
                        
                        EventCondition = "LinkPreviewGenerated";
                    }
                    else
                    {
                        _logger.LogWarning("Failed to save link previews for message {MessageId}", _message.Id);
                        EventCondition = "LinkPreviewGenerationFailed";
                    }
                }
                else
                {
                    _logger.LogDebug("No URLs found in message {MessageId}", _message.Id);
                    EventCondition = "NoUrlsFound";
                }

                await this.Fire(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error generating link preview for message {MessageId}", cmd.Dto.MessageId);
                EventCondition = "LinkPreviewGenerationFailed";
                await this.Fire(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

### **4. Message Command Handlers**

#### **UpdateMessageStatusHandler (SRP: Status Updates)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/CommandHandlers/Message/UpdateMessageStatusHandler.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.MessageModule
{
    public partial class UpdateMessageStatusHandler : IUpdateMessageStatusHandler
    {
        protected string EventCondition = "";
        protected readonly ILogger<UpdateMessageStatusHandler> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected readonly IHybridEventService _eventService;

        protected Message? _model;
        protected FlexAppContextBridge? _flexAppContext;

        public UpdateMessageStatusHandler(
            ILogger<UpdateMessageStatusHandler> logger, 
            IFlexHost flexHost, 
            RepoFactory repoFactory,
            IHybridEventService eventService)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
            _eventService = eventService;
        }

        public virtual async Task Execute(UpdateMessageStatusCommand cmd, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = cmd.Dto.GetAppContext();
                _repoFactory.Init(cmd.Dto);

                // Get message and update status
                _model = _flexHost.GetDomainModel<Message>().GetById(cmd.Dto.MessageId);
                if (_model == null)
                    throw new BusinessException("Message not found");

                switch (cmd.Dto.Status)
                {
                    case MessageStatus.Delivered:
                        _model.MarkAsDelivered();
                        break;
                    case MessageStatus.Read:
                        _model.MarkAsRead();
                        break;
                    default:
                        throw new BusinessException($"Invalid status update: {cmd.Dto.Status}");
                }

                // Persist to database
                _repoFactory.GetRepo().InsertOrUpdate(_model);
                int records = await _repoFactory.GetRepo().SaveAsync();

                if (records > 0)
                {
                    _logger.LogDebug("Message {MessageId} status updated to {Status}", _model.Id, cmd.Dto.Status);
                    
                    // Publish to real-time streams
                    await _eventService.PublishMessageStatusUpdateAsync(_model);
                    
                    EventCondition = "MessageStatusUpdated";
                }
                else
                {
                    _logger.LogWarning("Failed to update message {MessageId} status", _model.Id);
                    EventCondition = "MessageStatusUpdateFailed";
                }

                await this.Fire(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating message status for message {MessageId}", cmd.Dto.MessageId);
                EventCondition = "MessageStatusUpdateFailed";
                await this.Fire(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

***

## 📡 Event Subscribers Implementation

### **1. Chat Event Subscribers**

#### **NotifyParticipantsOnChatCreated (SRP: Participant Notifications)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/Subscribers/Chat/NotifyParticipantsOnChatCreated.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.ChatModule
{
    public partial class NotifyParticipantsOnChatCreated : INotifyParticipantsOnChatCreated
    {
        protected readonly ILogger<NotifyParticipantsOnChatCreated> _logger;
        protected readonly IHybridEventService _eventService;
        protected readonly INotificationService _notificationService;
        protected string EventCondition = "";

        protected FlexAppContextBridge? _flexAppContext;

        public NotifyParticipantsOnChatCreated(
            ILogger<NotifyParticipantsOnChatCreated> logger,
            IHybridEventService eventService,
            INotificationService notificationService)
        {
            _logger = logger;
            _eventService = eventService;
            _notificationService = notificationService;
        }

        public virtual async Task Execute(ChatCreatedEvent @event, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = @event.AppContext;

                _logger.LogInformation("Notifying participants about chat creation {ChatId}", @event.ChatId);

                // Send push notifications to all participants
                await _notificationService.SendChatCreatedNotificationAsync(@event);

                // Publish to real-time streams for immediate UI updates
                await _eventService.PublishChatCreatedToParticipantsAsync(@event);

                _logger.LogInformation("Successfully notified participants about chat {ChatId}", @event.ChatId);
                EventCondition = "ParticipantsNotified";

                await this.Fire<NotifyParticipantsOnChatCreated>(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error notifying participants about chat {ChatId}", @event.ChatId);
                EventCondition = "NotificationFailed";
                await this.Fire<NotifyParticipantsOnChatCreated>(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

#### **UpdateChatLastMessageOnMessageSent (SRP: Chat Metadata Updates)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/Subscribers/Chat/UpdateChatLastMessageOnMessageSent.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.ChatModule
{
    public partial class UpdateChatLastMessageOnMessageSent : IUpdateChatLastMessageOnMessageSent
    {
        protected readonly ILogger<UpdateChatLastMessageOnMessageSent> _logger;
        protected readonly IFlexHost _flexHost;
        protected readonly RepoFactory _repoFactory;
        protected string EventCondition = "";

        protected FlexAppContextBridge? _flexAppContext;

        public UpdateChatLastMessageOnMessageSent(
            ILogger<UpdateChatLastMessageOnMessageSent> logger,
            IFlexHost flexHost,
            RepoFactory repoFactory)
        {
            _logger = logger;
            _flexHost = flexHost;
            _repoFactory = repoFactory;
        }

        public virtual async Task Execute(MessageSentEvent @event, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = @event.AppContext;

                _logger.LogInformation("Updating chat last message for chat {ChatId}", @event.ChatId);

                // Get chat and update last message timestamp
                var chat = _flexHost.GetDomainModel<Chat>().GetById(@event.ChatId);
                if (chat != null)
                {
                    chat.LastMessageAt = @event.SentAt;
                    _repoFactory.GetRepo().InsertOrUpdate(chat);
                    await _repoFactory.GetRepo().SaveAsync();
                }

                _logger.LogInformation("Successfully updated chat last message for chat {ChatId}", @event.ChatId);
                EventCondition = "ChatUpdated";

                await this.Fire<UpdateChatLastMessageOnMessageSent>(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating chat last message for chat {ChatId}", @event.ChatId);
                EventCondition = "ChatUpdateFailed";
                await this.Fire<UpdateChatLastMessageOnMessageSent>(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

### **2. @ Mentions Event Subscribers**

#### **NotifyMentionedUsersOnMessageSent (SRP: @ Mentions Notifications)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/Subscribers/Chat/NotifyMentionedUsersOnMessageSent.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.ChatModule
{
    public partial class NotifyMentionedUsersOnMessageSent : INotifyMentionedUsersOnMessageSent
    {
        protected readonly ILogger<NotifyMentionedUsersOnMessageSent> _logger;
        protected readonly IHybridEventService _eventService;
        protected readonly INotificationService _notificationService;
        protected readonly IUserService _userService;
        protected string EventCondition = "";

        protected FlexAppContextBridge? _flexAppContext;

        public NotifyMentionedUsersOnMessageSent(
            ILogger<NotifyMentionedUsersOnMessageSent> logger,
            IHybridEventService eventService,
            INotificationService notificationService,
            IUserService userService)
        {
            _logger = logger;
            _eventService = eventService;
            _notificationService = notificationService;
            _userService = userService;
        }

        public virtual async Task Execute(MessageSentEvent @event, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = @event.AppContext;

                // Check if message has mentions
                if (@event.Metadata?.ContainsKey("mentions") == true)
                {
                    var mentionedUserIds = @event.Metadata["mentions"] as List<string>;
                    if (mentionedUserIds?.Any() == true)
                    {
                        _logger.LogInformation("Notifying mentioned users for message {MessageId}", @event.MessageId);

                        // Send special notifications to mentioned users
                        foreach (var userId in mentionedUserIds)
                        {
                            await _notificationService.SendMentionNotificationAsync(userId, @event);
                        }

                        // Publish to real-time streams for immediate UI updates
                        await _eventService.PublishMentionNotificationAsync(@event.ChatId, mentionedUserIds, @event);

                        _logger.LogInformation("Successfully notified {Count} mentioned users for message {MessageId}", 
                            mentionedUserIds.Count, @event.MessageId);
                    }
                }

                EventCondition = "MentionedUsersNotified";
                await this.Fire<NotifyMentionedUsersOnMessageSent>(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error notifying mentioned users for message {MessageId}", @event.MessageId);
                EventCondition = "MentionNotificationFailed";
                await this.Fire<NotifyMentionedUsersOnMessageSent>(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

### **3. Message Event Subscribers**

#### **SendPushNotificationOnMessageSent (SRP: Push Notifications)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/Subscribers/Message/SendPushNotificationOnMessageSent.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.MessageModule
{
    public partial class SendPushNotificationOnMessageSent : ISendPushNotificationOnMessageSent
    {
        protected readonly ILogger<SendPushNotificationOnMessageSent> _logger;
        protected readonly INotificationService _notificationService;
        protected readonly IUserService _userService;
        protected string EventCondition = "";

        protected FlexAppContextBridge? _flexAppContext;

        public SendPushNotificationOnMessageSent(
            ILogger<SendPushNotificationOnMessageSent> logger,
            INotificationService notificationService,
            IUserService userService)
        {
            _logger = logger;
            _notificationService = notificationService;
            _userService = userService;
        }

        public virtual async Task Execute(MessageSentEvent @event, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = @event.AppContext;

                _logger.LogInformation("Sending push notification for message {MessageId}", @event.MessageId);

                // Get chat participants (excluding sender)
                var participants = await _userService.GetChatParticipantsAsync(@event.ChatId);
                var offlineParticipants = participants.Where(p => p.UserId != @event.SenderId && !p.IsOnline);

                // Send push notifications to offline participants
                foreach (var participant in offlineParticipants)
                {
                    await _notificationService.SendMessageNotificationAsync(participant.UserId, @event);
                }

                _logger.LogInformation("Successfully sent push notifications for message {MessageId}", @event.MessageId);
                EventCondition = "NotificationsSent";

                await this.Fire<SendPushNotificationOnMessageSent>(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending push notification for message {MessageId}", @event.MessageId);
                EventCondition = "NotificationFailed";
                await this.Fire<SendPushNotificationOnMessageSent>(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

#### **ProcessMessageAnalytics (SRP: Analytics Processing)**

```csharp
// EBusiness.Application/DomainHandler/EBusiness.Handlers/Subscribers/Message/ProcessMessageAnalytics.cs
using Microsoft.Extensions.Logging;
using Sumeru.Flex;

namespace EBusiness.MessageModule
{
    public partial class ProcessMessageAnalytics : IProcessMessageAnalytics
    {
        protected readonly ILogger<ProcessMessageAnalytics> _logger;
        protected readonly IAnalyticsService _analyticsService;
        protected readonly IHybridEventService _eventService;
        protected string EventCondition = "";

        protected FlexAppContextBridge? _flexAppContext;

        public ProcessMessageAnalytics(
            ILogger<ProcessMessageAnalytics> logger,
            IAnalyticsService analyticsService,
            IHybridEventService eventService)
        {
            _logger = logger;
            _analyticsService = analyticsService;
            _eventService = eventService;
        }

        public virtual async Task Execute(MessageSentEvent @event, IFlexServiceBusContext serviceBusContext)
        {
            try
            {
                _flexAppContext = @event.AppContext;

                _logger.LogInformation("Processing analytics for message {MessageId}", @event.MessageId);

                // Process message analytics
                await _analyticsService.ProcessMessageAnalyticsAsync(@event);

                // Publish analytics event for further processing
                await _eventService.PublishAnalyticsEventAsync("MessageAnalytics", new
                {
                    MessageId = @event.MessageId,
                    ChatId = @event.ChatId,
                    SenderId = @event.SenderId,
                    MessageType = @event.MessageType,
                    SentAt = @event.SentAt,
                    ProcessedAt = DateTime.UtcNow
                });

                _logger.LogInformation("Successfully processed analytics for message {MessageId}", @event.MessageId);
                EventCondition = "AnalyticsProcessed";

                await this.Fire<ProcessMessageAnalytics>(EventCondition, serviceBusContext);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error processing analytics for message {MessageId}", @event.MessageId);
                EventCondition = "AnalyticsFailed";
                await this.Fire<ProcessMessageAnalytics>(EventCondition, serviceBusContext);
                throw;
            }
        }
    }
}
```

***

## 🔧 Infrastructure Implementation

### **1. Hybrid Event Service**

```csharp
// EBusiness.Application/Infrastructure/EventHandlers/IHybridEventService.cs
using EBusiness;

namespace EBusiness.Infrastructure.EventHandlers
{
    public interface IHybridEventService
    {
        // Redis Streams for real-time
        Task PublishRealtimeMessageAsync(Message message);
        Task PublishUserStatusAsync(string userId, bool isOnline);
        Task PublishTypingStatusAsync(string chatId, string userId, bool isTyping);
        Task PublishMessageDeliveryAsync(string messageId, string userId);
        
        // NServiceBus for reliable processing
        Task PublishMessageEventAsync(Message message);
        Task PublishChatCreatedAsync(Chat chat);
        Task PublishUserOnlineEventAsync(string userId);
        Task PublishAnalyticsEventAsync(string eventType, object data);
        
        // NServiceBus for background tasks
        Task PublishBackgroundTaskAsync(string taskType, object data);
        
        // Processing methods
        Task ProcessRealtimeEventsAsync(CancellationToken cancellationToken);
        Task ProcessNsbEventsAsync(CancellationToken cancellationToken);
    }
}
```

### **2. Chat SignalR Hub**

```csharp
// EBusiness.Application/Infrastructure/EventHandlers/ChatSignalRHub.cs
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using EBusiness.ChatModule;

namespace EBusiness.Infrastructure.EventHandlers
{
    public class ChatSignalRHub : Hub
    {
        private readonly ILogger<ChatSignalRHub> _logger;
        private readonly ISendMessageHandler _sendMessageHandler;
        private readonly IHybridEventService _eventService;

        public ChatSignalRHub(
            ILogger<ChatSignalRHub> logger,
            ISendMessageHandler sendMessageHandler,
            IHybridEventService eventService)
        {
            _logger = logger;
            _sendMessageHandler = sendMessageHandler;
            _eventService = eventService;
        }

        public async Task JoinChat(string chatId)
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, $"chat_{chatId}");
            _logger.LogInformation("User {UserId} joined chat {ChatId}", Context.UserIdentifier, chatId);
        }

        public async Task LeaveChat(string chatId)
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"chat_{chatId}");
            _logger.LogInformation("User {UserId} left chat {ChatId}", Context.UserIdentifier, chatId);
        }

        public async Task SendMessage(string chatId, string content, string messageType = "text")
        {
            try
            {
                var command = new SendMessageCommand
                {
                    Dto = new SendMessageDto
                    {
                        ChatId = chatId,
                        SenderId = Context.UserIdentifier,
                        Content = content,
                        MessageType = messageType,
                        SentAt = DateTime.UtcNow
                    }
                };

                await _sendMessageHandler.Execute(command, null);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send message to chat {ChatId}", chatId);
                await Clients.Caller.SendAsync("MessageFailed", new { Error = "Failed to send message" });
            }
        }

        public async Task StartTyping(string chatId)
        {
            await _eventService.PublishTypingStatusAsync(chatId, Context.UserIdentifier, true);
            await Clients.GroupExcept($"chat_{chatId}", Context.ConnectionId)
                .SendAsync("UserTyping", new
                {
                    UserId = Context.UserIdentifier,
                    ChatId = chatId,
                    IsTyping = true
                });
        }

        public async Task StopTyping(string chatId)
        {
            await _eventService.PublishTypingStatusAsync(chatId, Context.UserIdentifier, false);
            await Clients.GroupExcept($"chat_{chatId}", Context.ConnectionId)
                .SendAsync("UserTyping", new
                {
                    UserId = Context.UserIdentifier,
                    ChatId = chatId,
                    IsTyping = false
                });
        }

        public override async Task OnConnectedAsync()
        {
            _logger.LogInformation("User {UserId} connected", Context.UserIdentifier);
            await _eventService.PublishUserOnlineEventAsync(Context.UserIdentifier);
            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            _logger.LogInformation("User {UserId} disconnected", Context.UserIdentifier);
            await _eventService.PublishUserStatusAsync(Context.UserIdentifier, false);
            await base.OnDisconnectedAsync(exception);
        }
    }
}
```

***

## 🎯 DTOs Implementation

### **1. Domain DTOs**

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/DomainDtos/Chat/ChatDto.cs
using System;

namespace EBusiness
{
    public partial class ChatDto : DtoBridge 
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public ChatType ChatType { get; set; }
        public ChatStatus Status { get; set; }
        public string CreatedBy { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime? LastMessageAt { get; set; }
        public ChatSettings Settings { get; set; }
    }
}
```

### **2. Feature DTOs**

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/FeatureDtos/Chat/Input/CreateChat/CreateChatDto.cs
using System;
using System.ComponentModel.DataAnnotations;

namespace EBusiness.ChatModule
{
    public partial class CreateChatDto : DtoBridge 
    {
        [Required]
        [StringLength(255)]
        public string Name { get; set; }

        [StringLength(1000)]
        public string Description { get; set; }

        [Required]
        public ChatType ChatType { get; set; }

        [Required]
        public string CreatedBy { get; set; }

        public List<string> ParticipantIds { get; set; } = new();

        public bool AllowFileSharing { get; set; } = true;
        public bool AllowVoiceMessages { get; set; } = true;
        public int MaxParticipants { get; set; } = 100;
    }
}
```

### **2. Group Chat Feature DTOs**

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/FeatureDtos/Chat/Input/CreateGroupChat/CreateGroupChatDto.cs
using System;
using System.ComponentModel.DataAnnotations;

namespace EBusiness.ChatModule
{
    public partial class CreateGroupChatDto : DtoBridge 
    {
        [Required]
        [StringLength(255)]
        public string Name { get; set; }

        [StringLength(1000)]
        public string Description { get; set; }

        [Required]
        public string CreatedBy { get; set; }

        [Required]
        [MinLength(1, ErrorMessage = "At least one participant is required")]
        public List<string> ParticipantIds { get; set; } = new();

        public bool AllowFileSharing { get; set; } = true;
        public bool AllowVoiceMessages { get; set; } = true;
        public bool AllowMentions { get; set; } = true;
        public bool AllowReactions { get; set; } = true;
        public int MaxParticipants { get; set; } = 100;
        public string GroupImageUrl { get; set; }
    }
}
```

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/FeatureDtos/Chat/Input/InviteToGroupChat/InviteToGroupChatDto.cs
using System;
using System.ComponentModel.DataAnnotations;

namespace EBusiness.ChatModule
{
    public partial class InviteToGroupChatDto : DtoBridge 
    {
        [Required]
        public string ChatId { get; set; }

        [Required]
        public string InviterId { get; set; }

        [Required]
        [MinLength(1, ErrorMessage = "At least one user must be invited")]
        public List<string> InvitedUserIds { get; set; } = new();

        public string InvitationMessage { get; set; }
    }
}
```

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/FeatureDtos/Chat/Input/SendMessage/SendMessageDto.cs
using System;
using System.ComponentModel.DataAnnotations;

namespace EBusiness.ChatModule
{
    public partial class SendMessageDto : DtoBridge 
    {
        [Required]
        public string ChatId { get; set; }

        [Required]
        public string SenderId { get; set; }

        [Required]
        [StringLength(4000)]
        public string Content { get; set; }

        public MessageType MessageType { get; set; } = MessageType.Text;
        public string ReplyToMessageId { get; set; }
        public Dictionary<string, object> Metadata { get; set; } = new();
        public DateTime SentAt { get; set; } = DateTime.UtcNow;
    }
}
```

### **3. Media Feature DTOs**

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/FeatureDtos/Media/Input/UploadMedia/UploadMediaDto.cs
using System;
using System.ComponentModel.DataAnnotations;

namespace EBusiness.MediaModule
{
    public partial class UploadMediaDto : DtoBridge 
    {
        [Required]
        public string ChatId { get; set; }

        [Required]
        public string SenderId { get; set; }

        [Required]
        public string FileName { get; set; }

        public string OriginalFileName { get; set; }

        [Required]
        public string ContentType { get; set; }

        [Required]
        public long FileSize { get; set; }

        [Required]
        public Stream FileStream { get; set; }

        public string Caption { get; set; }

        public Dictionary<string, object> Metadata { get; set; } = new();
    }
}
```

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/FeatureDtos/Media/Input/GenerateLinkPreview/GenerateLinkPreviewDto.cs
using System;
using System.ComponentModel.DataAnnotations;

namespace EBusiness.MediaModule
{
    public partial class GenerateLinkPreviewDto : DtoBridge 
    {
        [Required]
        public string MessageId { get; set; }

        public List<string> Urls { get; set; } = new();
    }
}
```

```csharp
// EBusiness.Application/ControlContracts/EBusiness.DTOs/FeatureDtos/Media/Input/AddMediaAttachment/AddMediaAttachmentDto.cs
using System;
using System.ComponentModel.DataAnnotations;

namespace EBusiness.MediaModule
{
    public partial class AddMediaAttachmentDto : DtoBridge 
    {
        [Required]
        public string FileName { get; set; }

        [Required]
        public string FileType { get; set; }

        [Required]
        public long FileSize { get; set; }

        [Required]
        public string FilePath { get; set; }

        public string ThumbnailPath { get; set; }

        public string EncryptionKey { get; set; }

        public Dictionary<string, object> Metadata { get; set; } = new();
    }
}
```

```

---

## 🚀 Implementation Phases

### **Phase 1: Core Infrastructure (Week 1)**
1. **Domain Models**: Create Chat, Message, ChatParticipant models
2. **DTOs**: Create all required DTOs
3. **Hybrid Event Service**: Implement Redis + NServiceBus integration
4. **SignalR Hub**: Basic real-time communication

### **Phase 2: Command Handlers (Week 2)**
1. **Chat Handlers**: CreateChat, SendMessage, AddParticipant
2. **Group Chat Handlers**: CreateGroupChat, InviteToGroupChat, LeaveGroupChat, UpdateGroupChatInfo
3. **Message Handlers**: UpdateStatus, AddReaction, EditMessage
4. **Media Handlers**: UploadMedia, DeleteMedia
5. **Real-time Handlers**: JoinChat, LeaveChat, TypingStatus

### **Phase 3: Event Subscribers (Week 3)**
1. **Chat Subscribers**: Notifications, metadata updates
2. **Group Chat Subscribers**: Group notifications, invitations, admin promotions
3. **@ Mentions Subscribers**: Mention notifications, real-time updates
4. **Message Subscribers**: Push notifications, analytics
5. **Media Subscribers**: Processing, cleanup
6. **Real-time Subscribers**: Status updates, presence

### **Phase 4: Advanced Features (Week 4)**
1. **End-to-End Encryption**: Message encryption/decryption
2. **File Sharing**: Media upload and processing
3. **Analytics**: Message analytics and reporting
4. **Background Processing**: Message archival, cleanup

### **Phase 5: Testing & Optimization (Week 5)**
1. **Unit Tests**: All handlers and subscribers
2. **Integration Tests**: End-to-end flows
3. **Performance Testing**: Load and stress testing
4. **Monitoring**: Logging and metrics

---

## 🎯 Group Chat, @ Mentions & Media Features

### **Group Chat Capabilities:**
1. **✅ Create Group Chats**: Multiple participants with admin/member roles
2. **✅ Invite Users**: Admins can invite new members to group chats
3. **✅ Leave Group Chats**: Users can leave group chats (with admin restrictions)
4. **✅ Group Settings**: Customizable permissions and settings
5. **✅ Admin Management**: Promote/demote users, manage permissions
6. **✅ Group Info Updates**: Change name, description, image

### **@ Mentions System:**
1. **✅ Real-time @ Mentions**: Type @username to mention users
2. **✅ Mention Notifications**: Special notifications for mentioned users
3. **✅ Mention Processing**: Automatic user lookup and validation
4. **✅ UI Integration**: Real-time mention highlighting and suggestions
5. **✅ Group Chat Mentions**: Works specifically in group chats
6. **✅ Mention Analytics**: Track mention usage and engagement

### **Media & File Sharing:**
1. **✅ Image Upload**: JPEG, PNG, GIF, WebP, SVG support
2. **✅ Video Upload**: MP4, AVI, MOV, WMV, WebM support
3. **✅ Audio Upload**: MP3, WAV, OGG, AAC, M4A support
4. **✅ Document Upload**: PDF, Word, Excel, Text files
5. **✅ File Validation**: Size limits, type checking, virus scanning
6. **✅ Thumbnail Generation**: Automatic thumbnails for images/videos
7. **✅ Media Compression**: Optimize file sizes for storage
8. **✅ Encryption**: Secure file storage with encryption keys

### **Hyperlink & Link Preview:**
1. **✅ Automatic URL Detection**: Extract URLs from text messages
2. **✅ Link Preview Generation**: Title, description, image previews
3. **✅ Real-time Preview**: Generate previews as messages are sent
4. **✅ Multiple URL Support**: Handle multiple links in one message
5. **✅ Preview Caching**: Cache previews to avoid re-generation
6. **✅ Domain Validation**: Validate URLs before generating previews

### **Event Flow for @ Mentions:**
```

User types @john in group chat ↓ SendMessageHandler processes message ↓ ProcessMentions() extracts @john ↓ FindUserByMention() resolves to user ID ↓ Message metadata includes mentions ↓ NotifyMentionedUsersOnMessageSent ↓ SendMentionNotificationAsync() to @john ↓ PublishMentionNotificationAsync() for real-time UI

```

### **Event Flow for Media Upload:**
```

User uploads image/video/file ↓ UploadMediaHandler validates file ↓ MediaStorageService uploads to storage ↓ Message created with media attachment ↓ ProcessHyperlinks() for any URLs in caption ↓ PublishRealtimeMessageAsync() for immediate display ↓ PublishMessageEventAsync() for reliable processing ↓ ProcessMediaUpload subscriber generates thumbnails ↓ CompressMediaFiles subscriber optimizes file size

```

### **Event Flow for Link Preview:**
```

User sends message with URL ↓ SendMessageHandler processes message ↓ ProcessHyperlinks() extracts URLs ↓ GenerateLinkPreviewHandler triggered ↓ LinkPreviewService generates preview ↓ Message metadata updated with preview ↓ PublishLinkPreviewGeneratedAsync() for real-time UI

```

## 🎯 Key Benefits

1. **✅ Single Responsibility Principle**: Each handler/subscriber has one clear purpose
2. **✅ Flexbase Compatible**: Follows existing patterns and conventions
3. **✅ Event-Driven Architecture**: Real-time + reliable processing
4. **✅ Scalable**: Handles millions of messages with proper partitioning
5. **✅ Maintainable**: Clean separation of concerns
6. **✅ Testable**: Easy to unit test individual components
7. **✅ Extensible**: Easy to add new features and handlers
8. **✅ Group Chat Ready**: Full group chat functionality with admin controls
9. **✅ @ Mentions Support**: Complete mention system with notifications
10. **✅ Media Rich**: Comprehensive media upload and processing
11. **✅ Link Previews**: Automatic URL detection and preview generation
12. **✅ File Security**: Encryption, validation, and virus scanning
13. **✅ Performance Optimized**: Thumbnail generation and media compression

## 📊 Media Support Summary

| Media Type | Supported Formats | Max Size | Features |
|------------|------------------|----------|----------|
| **Images** | JPEG, PNG, GIF, WebP, SVG | 10MB | Thumbnails, Compression |
| **Videos** | MP4, AVI, MOV, WMV, WebM | 100MB | Thumbnails, Compression |
| **Audio** | MP3, WAV, OGG, AAC, M4A | 50MB | Waveform, Compression |
| **Documents** | PDF, Word, Excel, Text | 25MB | Preview, Search |
| **Links** | HTTP/HTTPS URLs | N/A | Auto-preview, Caching |

This implementation provides a complete, production-ready chat application with advanced group chat, @ mentions, media sharing, and link preview functionality that integrates seamlessly with your existing Flexbase infrastructure while maintaining clean architecture and following SOLID principles!


```
