Skip to main content

Understanding the Architecture

This document walks you through Bellamy Book’s overall architecture so you can see how all the pieces fit together. It is the recommended starting point for developers and operators who need a clear mental model of the system.


What is Bellamy Book?​

Bellamy Book is a self-hosted social network (one deployment = one community) with:

  • A social frontend (React + Vite) for users: feed, posts, comments, reactions, stories, messaging, blogs, hashtags, notifications, friends.
  • A native mobile app (Expo Router) for the same member features against the same API (Mobile App).
  • An admin panel (React + CoreUI) for administrators: dashboard, users, content/blogs, notifications, support tickets, backup, license, AI agent, email campaigns.
  • A single Backend API (ASP.NET Core .NET 8) that serves both frontend and admin via REST.
  • Realtime SignalR hubs hosted on ChatWorker and WebSocketWorker (not inside the API process).
  • Background workers: Kafka consumers (feed/graph/search), RabbitMQ consumers (chat, notifications, push, moderation, AI blog), and MediaProcessingWorker (polls PostgreSQL pending media).
  • Scheduled (cron) jobs inside the API (Quartz) and inside the BlogAutoGenerationWorker.
  • Multiple data stores: PostgreSQL (core data), MongoDB (comments, chat, notifications), Redis (cache, presence), Neo4j (social graph), Elasticsearch (full-text search).

The architecture is modular but not “microservices” in the sense of many small APIs: there is one API, many workers, and polyglot persistence.


High-Level Picture​

Everything starts with clients (browser, admin app, or native mobile) talking to the Backend API over HTTP. The API reads and writes databases, publishes Kafka events for social/analytics/search, and publishes RabbitMQ messages for chat, notifications, push, moderation, and AI blog generation. Workers consume those buses (or poll media). Clients open SignalR connections to ChatWorker / WebSocketWorker for realtime.

┌─────────────────────────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ Social Frontend (React + Vite) · Admin Panel (CoreUI) · Mobile (Expo) │
└────────────────────────────────────────┬────────────────────────────────────────┘
│ HTTP/REST
▼
┌───────────────────────────────────────────────────────────────────────────────────┐
│ BACKEND API (ASP.NET Core .NET 8) │
│ Controllers + Quartz jobs (cleanup, sync, campaigns, backup, license, …) │
└───┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────────────┘
▼ ▼ ▼ ▼ ▼
PostgreSQL MongoDB Redis Neo4j Elasticsearch
│ │
│ ┌──────┴──────────────┐
│ │ Kafka │ RabbitMQ │
│ └───┬───┴──────┬──────┘
▼ ▼ ▼
Kafka workers (scoring, graph, hashtag, ES sync, …)
RabbitMQ workers (ChatWorker + SignalR, WebSocketWorker + SignalR,
Web/Expo push, ContentModeration, BlogAutoGen)
MediaProcessingWorker (polls PostgreSQL MediaFile)

Takeaway: One API, many data stores, Kafka for social/analytics/search, RabbitMQ for chat/notify/push/moderation/AI blog, SignalR on workers, media via polling, Quartz in API (+ blog worker).


How a Request Flows (Synchronous Path)​

When a user or admin does something in the UI, the flow is:

  1. Client (frontend, admin, or native mobile) sends an HTTP request (e.g. POST /api/Post/users/{userId} to create a post) with a JWT in the header.
  2. Backend API receives the request:
    • Middleware runs: error handling, host validation, license enforcement (some routes require a valid license/feature flag), rate limiting, then authentication (JWT) and authorization (roles: User, Moderator, Admin).
  3. Controller (e.g. PostController) receives the request, maps the body to a DTO, and sends a command or query via MediatR to the Application layer.
  4. Application (handlers) runs business logic: load/validate entities, update Domain objects, call Infrastructure (repositories) to read/write PostgreSQL or MongoDB, and optionally use Redis (cache) or call external services.
  5. For write operations, the handler often publishes one or more events to Kafka (e.g. “post created”, “comment added”). The API does not wait for workers; it returns as soon as the primary write and event publish succeed.
  6. Response is returned to the client (e.g. 201 Created with the new post).

Real-time: Clients open SignalR connections to ChatWorker (/hubs/chat) and WebSocketWorker (/hubs/notification, /hubs/onlineusers) — not to the API process. See Workers and Data Flow.

Takeaway: The synchronous path is “client → API → databases (+ optional Kafka publish)”. Workers are not in the critical path of a single request.


Data Stores: Who Stores What?​

Bellamy Book uses polyglot persistence: the right store for the right kind of data.

StoreUsed forWritten by
PostgreSQLUsers, posts, stories, media metadata, friendships metadata, hashtags, blogs, tickets, email campaigns, license, AI agent settings, scores/trends, etc.API (primary); many workers
MongoDBComments, chat messages/conversations, notifications, push subscriptions/tokensAPI and workers (ChatWorker, push workers, …)
RedisCache (user, post, feed, story), sessions, rate limiting, real-time stateAPI and workers
Neo4jSocial graph (friends, suggestions, recommendations)GraphWorker, ScoringWorker (driven by Kafka events from API)
Elasticsearch/OpenSearchFull-text search (posts, blogs)ElasticsearchSyncWorker (syncs from PostgreSQL after events)

The API is the main writer for PostgreSQL and MongoDB for user-facing actions; workers keep Neo4j, Elasticsearch, scores, and trends in sync and write back to PostgreSQL/MongoDB/Redis where needed.

Details: Database Architecture.


Event Flow: What Happens After the API Publishes?​

Many user actions cause the API to publish events to Kafka (e.g. post created, comment added, reaction added). Those events are consumed by workers that run in separate processes:

  1. GraphWorker — Updates Neo4j (social graph, friend suggestions).
  2. ScoringWorker — Updates content scores, relevance, rankings (PostgreSQL/Neo4j).
  3. TrendingWorker — Updates trending content (PostgreSQL).
  4. HashtagWorker — Extracts and indexes hashtags from content (PostgreSQL).
  5. ElasticsearchSyncWorker — Syncs posts/blogs (and optionally users) to Elasticsearch for search.
  6. InteractionWorker — Processes engagement and interaction metrics.
  7. MediaProcessingWorker — Processes media (images, video) and writes to MongoDB/storage.
  8. WebSocketWorker — Pushes real-time notifications to connected clients (uses Redis for connection state).
  9. WebPushNotificationWorker — Sends browser push notifications.
  10. ChatWorker — Real-time chat (SignalR hub), online presence, offline messages (MongoDB).

So: API writes primary data and publishes to Kafka → Workers consume and update other stores (Neo4j, Elasticsearch, Redis, MongoDB) or trigger side effects (notifications, media processing).

RabbitMQ (not Kafka) is used for: chat delivery hooks, notification/friendship messages, Web/Expo push, content moderation, and AI blog generation. The BlogAutoGenerationWorker also runs a Quartz BlogGenerationJob on a cron from AI Agent settings.

Details: Data Flow, Workers, Microservices.


Scheduled Jobs (Cron)​

Besides Kafka-driven workers, the system uses scheduled jobs that run on a timer:

  • Inside the API process (Quartz):
    Refresh token cleanup, user sync (PostgreSQL → Neo4j), Elasticsearch catch-up sync, temp file cleanup, story cleanup, birthday notifications, email campaign sending, poll expiration, license validation, scheduled backup.
    Schedules are configured via Quartz:{JobName} in config; backup schedule can also be set in Admin → Backup.

  • Inside BlogAutoGenerationWorker (Quartz):
    BlogGenerationJob — runs on a cron (e.g. daily at 2 AM) when AI blog generation is enabled; schedule is stored in the database (AI Agent settings) and can be changed from the admin panel.

So: two places run cron jobs — the main API (most of them) and the BlogAutoGenerationWorker (blog generation only).

Details: Scheduled Jobs.


Deployment: What Runs Where?​

  • Frontend / Admin — Static builds (e.g. served by Nginx or a CDN). Two separate apps: social frontend and admin panel.
  • Backend API — One or more instances. Contains REST + Quartz jobs listed above (same process). Does not host chat/notification SignalR hubs.
  • Workers — Separate deployables: Kafka workers, MediaProcessingWorker (polling), ChatWorker & WebSocketWorker (SignalR + RabbitMQ), push workers, ContentModerationWorker, BlogAutoGenerationWorker.
  • BlogAutoGenerationWorker — Needs RabbitMQ, DB (settings/blogs), AI provider config, and its own Quartz schedule.
  • Databases — PostgreSQL, MongoDB, Redis, Neo4j, Elasticsearch/OpenSearch; typically separate containers or managed services.
  • Message brokers — Kafka (social/analytics/search events), RabbitMQ (chat, notify, push, moderation, AI blog).

Details: Deployment, Scaling.


Security and Licensing​

  • Authentication: JWT access tokens + refresh tokens; optional OAuth (Google, etc.).
  • Authorization: Roles (User, Moderator, Admin). Admin-only routes (e.g. api/admin/*, api/EmailCampaign, api/ai-agent, api/tickets) are restricted by role and, where applicable, by license.
  • License: Some features (e.g. AI blog generation, email campaigns, support tickets) are gated by license tier/feature flags. The API enforces this in middleware and in controllers. See License System for MAIN/CONSUMER modes, tiers, activation, and enforcement.
  • Rate limiting, validation, HTTPS: Applied at the API layer.

Details: Security.


How the Pieces Fit Together (Summary)​

LayerComponentsPurpose
ClientsSocial frontend, Admin panelUI; REST to API; SignalR to ChatWorker / WebSocketWorker.
APIASP.NET Core .NET 8, Controllers, MediatR, QuartzHandle requests, write primary data, publish events, run cron jobs.
DataPostgreSQL, MongoDB, Redis, Neo4j, ElasticsearchPersistence and search; written by API and workers.
EventsKafka + RabbitMQDecouple API from heavy/async processing.
WorkersKafka / RabbitMQ / media polling + SignalR hostsSearch sync, graph, notifications, chat, push, AI blogs, moderation.
Scheduled jobsQuartz in API + BlogGenerationJob in workerCleanup, sync, email campaigns, backup, license, AI blog schedule.

Reading order from here:

  1. System Overview — Component diagram and request/event flow.
  2. System Architecture Deep Dive — Media, recommendations, push, search, chat/calls, scaling.
  3. Microservices — API surface and worker list.
  4. Data Flow — Sequences for post creation, real-time, caching.
  5. Frontend · Admin · Backend
  6. Database · Workers · Scheduled Jobs
  7. Deployment and Scaling
  8. Security
  9. Self-Hosting

This document gave you the overall architecture; those links take you deeper into each part.