Skip to main content

Messaging, WebSocket, and presence

The durable chat model is Postgres: conversations, conversation_participants, messages, attachments, reactions, pins, and user_chat_settings. SendMessageUseCase commits the message and an outbox row (messaging.direct-message-received or messaging.group-message-received). Notification-service turns that into push. The recipient who is online also gets a live frame.

Live delivery is a second path that is allowed to be lossy:

  1. Client POST /v1/messaging/ws-ticket.
  2. Client opens /api/v1/messaging/ws through Kong. JwtWebSocketHandshakeInterceptor checks the ticket or token.
  3. RedisMessageEventPublisher publishes on a Redis pub/sub channel. Every messaging node runs RedisWsEventSubscriber and writes to local sockets.
  4. Presence uses Redis (RedisPresenceService, BulkGetPresenceUseCase) and is not a Postgres table.

If Redis pub/sub fails, history still loads over REST. Do not "fix" a missed live frame by writing a second message row.

Group chats that mirror social groups use SyncSocialGroupMembershipUseCase and inbox handlers for delete, rename, avatar, ownership, and member active flags. The source of truth for membership of a social group remains social-service.

Rate limiting on send is in-process Redis (MessagingRateLimiter), in addition to any Kong limit on the route. Typing (PublishTypingUseCase) is ephemeral and must not create an outbox row.