Skip to content
Léo.
All projects
Web developmentCompleted2026

Breezy

An X-like social network split into six independent services behind a single gateway.

CESI · Distributed applications · Team project

Illustration: networked services
Illustration: networked services
Role
Front end, Post service, Notification service
Team
4 developers, 417 commits
Services
6 microservices + gateway + front end
Databases
2 PostgreSQL, 4 MongoDB

Key points

  • Six independent services, each with its own database, behind a single Nginx gateway
  • Two database engines by need: relational for identities, documents for content
  • RabbitMQ event bus: publishing a post does not depend on the notification service
  • Real-time WebSockets for direct messages and notifications

The walkthrough

Walkthrough: publishing a post, another account receiving the notification live, direct messaging.

How it works

  1. 1

    A single front door

    Everything goes through an Nginx gateway on port 80: the interface as well as the APIs. It carries what should not be rewritten in six services, the CORS policy, with an allowlist of origins, and rate limiting, set to 30 requests per second in general but 2 on login and registration, where passwords get guessed.

  2. 2

    Proving who you are

    The Auth service checks the password, hashed with bcrypt, and signs a JWT. For protected pages the gateway guesses nothing: it delegates validation to the Auth service before serving the page. Each business service then re-checks the token on its own, none of them trusts its caller.

  3. 3

    The relevant service answers

    Six domains, six services, six databases: Auth and User on PostgreSQL, Post, Message, Media and Notification on MongoDB. A domain going down does not take the others with it, an unavailable Media service stops image uploads, not feed reading.

  4. 4

    The event propagates

    When a post is published or liked, the Post service does not call the Notification service: it drops an event onto RabbitMQ and moves on. Notification consumes it in its own time. Neither service needs to know the other, or to be up at the same moment.

  5. 5

    The screen updates

    Notification and Message each hold an open WebSocket to the browser. The notification shows up with no reload, the direct message arrives while you type, without the interface polling the server in a loop.

What the application does

Breezy does what a short-form social network is expected to do: publish posts and reply to them, like, follow accounts, search by content or tag, attach images and video, message privately, receive notifications. Rights are carried by four roles, visitor, user, moderator, administrator, and around twenty named permissions, which makes it possible to switch a feature on or off without touching the code.

The module's constraint

The brief called for a genuinely distributed architecture, not a monolith dressed up as services. The hard part is not writing six Express servers: it is choosing where the boundaries fall, then living with what that split costs, a request crossing three processes, state that is no longer shared, services starting in any order.

Why two database engines

The choice is not decorative; it follows the shape of the data.

  • PostgreSQL for Auth and User: accounts, roles, permissions and follow relations, tables, foreign keys and uniqueness constraints, exactly what a relational engine guarantees better than application code
  • MongoDB for Post, Message, Media and Notification: documents that vary case by case, a post with or without media, with or without replies, and that are almost always read whole
  • Each service owns its database and nobody else touches it: that rule, more than the code split, is what makes the services genuinely independent

The gateway does more than route

  • It resolves service names on every request rather than at startup: without that, a service still booting stays unreachable until the gateway restarts
  • It applies a different rate cap per route, a global limit, and a far lower one on login and registration
  • It centralises the CORS policy: an unauthorised origin gets no headers at all and is blocked by the browser
  • It delegates token validation to the Auth service for protected pages, instead of duplicating security logic in configuration

What I took away

  • Splitting a domain into services: where to draw boundaries, and the cost of getting it wrong
  • Asynchrony is not an implementation detail, going through a bus changes who depends on whom
  • A complete environment reproducible with one command, databases and message bus included
  • Working as a team of four on a shared repository, with code review and feature branches
All projects