LogoAnswer Flow

System Architecture

The AnswerFlow is designed for high performance, scale, and extreme flexibility. It leverages the Server Components architecture from Next.js, along with a robust background processing pipeline.

Monorepo Workspace Structure

AnswerFlow is structured as a modern monorepo using Turborepo. This allows us to cleanly separate our frontend, background workers, and shared business logic.

biome.json
Caddyfile
commitlint.config.js
compose.prod.yaml
compose.yaml
CONTRIBUTING.md
lefthook.yaml
LICENSE
package.json
pnpm-lock.yaml
pnpm-workspace.yaml
README.md
turbo.json

Root Infrastructure

At the root of the repository, we maintain the configuration files that orchestrate the entire developer experience, CI/CD pipeline, and deployment strategy.

  • biome.json: Our master configuration for lightning-fast formatting and linting.
  • Caddyfile: Reverse proxy configuration for local development routing.
  • commitlint.config.js: Enforces the Conventional Commits standard (e.g., feat:, fix:) across the repository.
  • compose.prod.yaml: The secure, production-ready Docker architecture.
  • compose.yaml: The local Docker development environment (spins up MinIO, Postgres, and Redis).
  • CONTRIBUTING.md: Guidelines for contributing to the repository.
  • lefthook.yaml: Our Git hooks manager that automatically formats code and checks types before you commit or push.
  • LICENSE: The open-source license governing this project.
  • package.json: Root package configuration and scripts.
  • pnpm-lock.yaml: Exact dependency versions locked for reproducible builds.
  • pnpm-workspace.yaml: Defines the boundaries of our monorepo workspaces for the package manager.
  • README.md: Project overview and getting started instructions.
  • turbo.json: The Turborepo configuration that orchestrates our build pipelines and aggressive task caching.

Apps

  • web: The main Next.js application handling the AnswerFlow frontend and API routes.
  • docs: The Next.js documentation site (what you're reading now).
  • worker: A dedicated Node.js background worker process for heavy asynchronous tasks.

Packages

  • constants: Constants used across the application.
  • db: The single source of truth for our Prisma schema and generated client.
  • email: Email Provider integrations.
  • emails: Email templates and layouts.
  • push-notification: Contains Push notification functionality created with firebase admin sdk.
  • queue: Queue adapter with BullMQ & Qstash integrations.
  • rate-limit: Rate Limiter with redis.
  • redis: Single source of truth for redis.
  • storage: Single source of truth for S3 storage.
  • types: TypeScript types and DTOs.
  • typescript-config: TypeScript base config for the project.
  • ui: UI Components.
  • utils: Utilities and helper functions.
  • validators: Zod schemas for validation.

Core Stack

  • Framework: Next.js App Router (React Server Components)
  • Database: PostgreSQL (interacted via Prisma ORM)
  • Auth: Better-Auth
  • Caching: Redis (ioredis)
  • Virtualization: React-Virtuoso
  • Fetching: TanStack Query
  • Styling: Tailwind CSS & Radix UI

Database Schema (Prisma)

AnswerFlow uses a relational database architecture managed by Prisma. Below is a high-level overview of our core data models, followed by the complete schema reference.

Core Architecture

  • User: Manages identity, credentials (via Better-Auth), profile details, reputation scores, and site roles.
  • Question & Answer: The core of the Q&A engine. Supports rich text, specific authors, tag relationships, and upvote/downvote tracking.
  • Vote: Tracks individual user votes (value: +1 or -1) polymorphically on Questions or Answers to dynamically calculate aggregate voteScores.
  • Notification: Stores real-time system alerts for users (e.g., "@username mentioned you", "Someone answered your question").

User model schema
FieldTypeAttributes
idString@id @default(uuid())
nameString-
usernameString@unique @default(cuid())
displayUsernameString?Optional
emailString-
emailVerifiedBoolean@default(false)
imageString?Optional
bioString?Optional
websiteString?Optional
reputationInt@default(0)
createdAtDateTime@default(now())
updatedAtDateTime@updatedAt
accountsAccount[]Relation
questionsQuestion[]Relation
answersAnswer[]Relation
votesVote[]Relation
commentsComment[]Relation
bookmarksBookmark[]Relation
notificationsReceivedNotification[]Relation
notificationsSentNotification[]@relation("NotificationActor")
pushTokensPushToken[]Relation
notificationPreferencesNotificationPreferences?Relation
PushToken model schema
FieldTypeAttributes
idString@id @default(cuid())
tokenString@unique
userIdStringForeign Key
userUser@relation(..., onDelete: Cascade)
sessionIdStringIndexed
deviceString?Optional
createdAtDateTime@default(now())
updatedAtDateTime@updatedAt
Notification preferences schema
FieldTypeAttributes
idString@id @default(cuid())
userIdString@unique
userUser@relation(...)
emailRepliesBoolean@default(true)
emailMentionsBoolean@default(true)
emailMarketingBoolean@default(false)
pushRepliesBoolean@default(true)
pushMentionsBoolean@default(true)
pushUpvotesBoolean@default(false)
updatedAtDateTime@updatedAt
Account model schema
FieldTypeAttributes
idString@id @default(uuid())
accountIdString-
providerIdString-
userIdStringIndexed
userUser@relation(...)
accessTokenString?Optional
refreshTokenString?Optional
idTokenString?Optional
accessTokenExpiresAtDateTime?Optional
refreshTokenExpiresAtDateTime?Optional
scopeString?Optional
passwordString?Optional
createdAtDateTime@default(now())
updatedAtDateTime@updatedAt
Question model schema
FieldTypeAttributes
idString@id @default(uuid())
titleString-
slugString@unique
bodyString-
viewCountInt@default(0)
voteScoreInt@default(0)
authorIdStringIndexed
authorUser@relation(...)
Answer model schema
FieldTypeAttributes
idString@id @default(uuid())
bodyString-
isAcceptedBoolean@default(false)
voteScoreInt@default(0)
authorIdStringIndexed
questionIdStringIndexed
Tag model schema
FieldTypeAttributes
idString@id @default(uuid())
nameString@unique
slugString@unique
descriptionString?Optional
QuestionTag model schema
FieldTypeAttributes
questionIdStringComposite @id
tagIdStringComposite @id
Vote model schema
FieldTypeAttributes
idString@id @default(uuid())
valueInt+1 or -1
userIdStringForeign Key
questionIdString?Optional
answerIdString?Optional
Comment model schema
FieldTypeAttributes
idString@id @default(uuid())
bodyString-
authorIdStringIndexed
questionIdString?Optional
answerIdString?Optional
Bookmark model schema
FieldTypeAttributes
idString@id @default(uuid())
userIdStringForeign Key
questionIdStringForeign Key
Notification model schema
FieldTypeAttributes
idString@id @default(uuid())
userIdStringIndexed
typeNotificationTypeEnum
readAtDateTime?Optional
questionIdString?Optional
answerIdString?Optional
commentIdString?Optional
actorIdStringForeign Key

Caching Strategy

We utilize robust caching mechanisms to ensure pages remain snappy regardless of concurrent load:

  1. Next.js Full Route Cache: Static pages and basic routing are cached at the edge.
  2. TanStack Query: We use @tanstack/react-query to handle client-side pagination, fetching, and re-validation (like infinite-scrolling answers).

Background Worker Flow

To ensure the main Next.js web process stays lightning-fast, we offload heavy lifting (like compiling React email templates and sending Resend emails) to a background queue.

Because AnswerFlow is designed to be provider-agnostic, we support two completely different background worker architectures depending on how you want to deploy:

1. The VPS / Docker Route (BullMQ + Redis)

Perfect for self-hosters running traditional servers or Docker containers.

  1. Event Dispatch: The Next.js API pushes a job payload to the included Dockerized Redis container.
  2. Instant Response: Next.js immediately returns a 200 OK to the user so the UI feels instant.
  3. Dedicated Worker: Our separate apps/worker Node.js process continuously listens to Redis via BullMQ, picks up the job, and executes the heavy lifting in the background.

2. The Serverless Route (Upstash QStash)

Perfect for enterprise users deploying to Vercel, AWS, or Cloudflare.

  1. Event Dispatch: Instead of needing a persistent Redis container, the Next.js API pushes the job payload securely to Upstash QStash.
  2. Instant Response: Next.js returns a 200 OK to the user.
  3. Webhook Trigger: QStash acts as the reliable middleman and fires a secure HTTP POST request back to a dedicated Next.js webhook route (e.g., /api/webhooks/worker), executing the job dynamically on a serverless edge function.

This decoupling prevents third-party API latency from ever blocking the user experience, regardless of where you deploy!

On this page