Skip to content
bitzorcas
中EN

Concept

Chat Module Architecture: WebSockets, Redis Broadcasts & Message Streams

Deep dive into the BitzOrcas.Modern real-time chat architecture. Learn ChatSession aggregates, WebSocket connections, Redis Pub/Sub backplanes, and offline sequence sync.

Last updated

In enterprise collaboration and instant messaging systems, the Chat Subsystem balances high-throughput real-time streaming with strict delivery guarantees:

  • Multi-Node Backplane Routing: User A connects to Web Node 1 while User B connects to Node 2; Node 1 must route outbound messages to Node 2 in sub-milliseconds;
  • Offline Reconnection Gaps: Mobile clients reconnecting after network dropouts must sync unread messages via sequence numbers without message loss;
  • Channel Access Security: Callers must be prevented from eavesdropping on private channels by spoofing channel identifiers.

BitzOrcas.Modern Chat Module implements “WebSocket Gateways + Redis Pub/Sub Backplane + Monotonic Sequence Persistence”:

Real-Time Chat & Broadcast Topology

1. Client A (Sends Message)

2. Web Cluster Node 1 (WebSocket Gateway)

3. ChatMessage Persistence (Monotonic SequenceNo)

4. Redis Pub/Sub Backplane (Channel: chat:tenant:channel_01)

5. Web Cluster Node 2 (Listening on Backplane)

6. Client B (WebSocket Real-Time Dispatch)


Step 1: Monotonic Sequence Entity ChatMessage

ChatMessage.cs: Chat Message Entity
using BitzOrcas.Domain.Entities;
using BitzOrcas.Persistence.Metadata;
namespace BitzOrcas.Chat.Domain;
// 1. Table mapping and composite unique channel sequence index
[BitzTable("SysChatMessage", IsTenant = true, IsSoftDelete = false, Description = "Chat Message Stream")]
[BitzIndex("IX_SysChatMessage_Channel_Seq", nameof(ChannelId), nameof(SequenceNo), IsUnique = true)]
public sealed class ChatMessage : Entity<string>
{
[BitzColumn(Length = 64, IsRequired = true)]
public string ChannelId { get; set; } = string.Empty;
[BitzColumn(Length = 64, IsRequired = true)]
public string SenderUserId { get; set; } = string.Empty;
// 2. Monotonically increasing sequence number (1, 2, 3... for offline sync)
[BitzColumn(IsRequired = true)]
public long SequenceNo { get; set; }
[BitzColumn(Length = 4000, IsRequired = true)]
public string Content { get; set; } = string.Empty;
[BitzColumn(IsRequired = true)]
public DateTimeOffset SentAt { get; set; }
}

Step 2: Send Message Command Slice & Cluster Broadcast

SendChatMessageCommandHandler.cs: Chat Message Handler
using BitzOrcas.Application.Abstractions.Realtime;
using BitzOrcas.Domain.Results;
using BitzOrcas.Chat.Domain;
public sealed class SendChatMessageCommandHandler(
IChatMessageRepository messageRepo,
IRealtimePublisher realtimePublisher,
IAppClock clock)
{
public async ValueTask<Result> Handle(SendChatMessageCommand command, CancellationToken ct)
{
// 1. Acquire next sequence number and persist message
var nextSeq = await messageRepo.GetNextSequenceNoAsync(command.ChannelId, ct);
var msg = new ChatMessage
{
ChannelId = command.ChannelId,
SenderUserId = command.SenderUserId,
SequenceNo = nextSeq,
Content = command.Content,
SentAt = clock.UtcNow
};
await messageRepo.SaveAsync(msg, ct);
// 2. Broadcast across cluster via Redis Backplane
await realtimePublisher.PublishToChannelAsync(
channel: $"chat:{command.ChannelId}",
payload: new ChatMessageBroadcastDto(msg.Id, msg.SenderUserId, msg.Content, msg.SequenceNo),
ct);
return Result.Success();
}
}

Summary

The Chat Module powers seamless enterprise collaboration:

  • Monotonic Sequence Sync: Enables gapless recovery upon mobile reconnection;
  • Distributed Backplane: Linear horizontal cluster scaling for thousands of concurrent sockets;
  • Tenant Channel Scoping: Channels are strictly partitioned by tenant namespaces.

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%