Skip to content
Back to projects
Project · Case study

ChatNET

A real-time, multi-user chat app I'm building to go deep on SignalR, Redis and real-time systems that scale across server instances.

Role
Solo project
Status
In active development
Type
Direct & group chat

01The problem

Chat looks simple until you run more than one server. A message has to reach users connected to a different instance, clients drop and reconnect mid-conversation, and unread counts and presence have to stay correct throughout.

I wanted a project that forces those problems into the open, and that is built and tested the way I'd want a production service to be.

02My role

I designed and built the whole thing: the ASP.NET Core backend, the React frontend, the data model, the tests and the CI pipeline.

Direct (1:1) and group conversations share a single data model.

03Architecture

Write

  1. React clientTanStack Query
  2. REST endpointvertical slice · MediatR · validation
  3. PostgreSQLEF Core

Live

  1. API instancemessage saved
  2. SignalR hubone group per conversation
  3. Redis backplanefan-out · presence
  4. React clientZustand store merges sources
REST for writes, SignalR for live pushes, with a Redis backplane so events reach clients on any instance.

04Key decisions

  • REST for writes, SignalR for pushes

    Sending a message is a normal, validated, testable HTTP request. SignalR is only used to push events out to connected clients, which keeps the hub thin and the write path easy to reason about.

  • Redis backplane and presence

    SignalR's Redis backplane fans events out across server instances, and Redis also tracks who is online, so a user on one instance sees messages and presence from users on any other.

  • Reconnection catch-up

    When a client reconnects it fetches what it missed, and the frontend store merges messages arriving from REST history and the live stream without duplicates.

  • Correctness in the database

    A unique index guarantees there is only ever one direct conversation between two users, so the invariant holds even under concurrent requests.

  • Vertical slices

    The backend is organised by feature (Auth, Users, Conversations, Messaging) with MediatR and FluentValidation, and every error comes back as an RFC 7807 problem response.

  • Production habits from day one

    Rate limiting and account lockout on login, security headers, dependency health checks, integration tests against real Postgres and Redis containers, tests that assert database query cost, and a CI pipeline that lints, builds, tests and audits dependencies.

05Outcome

  • Shipped: registration and login, conversations and user search, live messaging, paginated history, reconnection catch-up, unread counts, read receipts and online presence.
  • Next up: refresh tokens, notifications, file uploads, structured logging, distributed tracing and deployment.

06Stack

  • .NET 10
  • ASP.NET Core Minimal APIs
  • SignalR
  • MediatR
  • FluentValidation
  • EF Core
  • PostgreSQL 16
  • Redis 7
  • React 19
  • TypeScript
  • Zustand
  • TanStack Query
  • Tailwind CSS
  • Docker Compose
Next case studyZero-loss audit pipeline