Update wiki page 'Architecture'

2026-05-15 15:03:57 +00:00
parent 5fcb8deaf7
commit 66247fbb34

@@ -1,70 +1,104 @@
# Architecture # Architecture
## Overview Hexagonal (Ports & Adapters) with Domain-Driven Design. Two processes share one PostgreSQL database.
Thoughts runs as four independent Docker services orchestrated by Docker Compose. Traffic enters via Nginx, which routes API calls to the Rust backend and everything else to the Next.js frontend. Both services share a PostgreSQL database. ## Processes
| Process | Binary | Role |
|---|---|---|
| API server | `thoughts` | HTTP API + ActivityPub endpoints |
| Event worker | `thoughts-worker` | Federation fan-out, notifications, remote cache refresh |
Both processes are built from the same Docker image; run with different entrypoints.
## Crate Layout
```
crates/
├── domain — pure types and port trait definitions, no external deps
├── application — use cases and event processing services (business logic)
├── api-types — shared REST API request/response DTOs
├── presentation — Axum HTTP router, OpenAPI spec, composition root for API process
├── bootstrap — binary: thoughts (API server)
├── worker — binary: thoughts-worker (event consumer)
└── adapters/
├── auth — JWT issuance/validation, Argon2 password hashing
├── postgres — PostgreSQL repositories for all domain entities
├── postgres-search — PostgreSQL trigram full-text search
├── postgres-federation — PostgreSQL-backed federation repository
├── activitypub-base — core AP protocol types, ActivityPubService, federation middleware
├── activitypub — project-specific AP wiring (ThoughtsObjectHandler, inbox/outbox)
├── nats — NATS transport implementing Transport + MessageSource ports
├── event-payload — shared event serialization DTOs
└── event-transport — Transport trait + EventPublisherAdapter / MessageSource + EventConsumerAdapter
```
## Traffic Flow
``` ```
User's Browser User's Browser
Nginx (Reverse Proxy) Nginx (Reverse Proxy)
├── /api/* ──────────────► Rust Backend (Axum) ├── /api/* ──────────────► thoughts (API server :8000)
/.well-known/*
└── /* ──────► Next.js └──► PostgreSQL │ /users/* (AP Accept) └──► PostgreSQL
Frontend
└── /* ──────► Next.js NATS JetStream
Frontend │
thoughts-worker
(AP delivery, notifications)
``` ```
## Components ## Domain Layer
### Backend — Rust (Axum) `crates/domain` defines only pure types and port traits — no framework or DB dependencies:
The stateless core of the platform. Handles all business logic, authentication, database operations, and (planned) ActivityPub federation. - **Models**: `User`, `Thought`, `Follow`, `Like`, `Boost`, `Block`, `Tag`, `Notification`, `RemoteActor`, `TopFriend`, `ApiKey`, `FeedEntry`
- **Value objects**: `UserId`, `ThoughtId`, `Username`, `Email`, `Content`, `PasswordHash`, …
- **Port traits**: `UserRepository`, `ThoughtRepository`, `LikeRepository`, `BoostRepository`, `FollowRepository`, `BlockRepository`, `FeedRepository`, `NotificationRepository`, `FederationActionPort`, `OutboundFederationPort`, `EventPublisher`, `AuthService`, …
- **Domain events**: published by use cases, consumed by the worker
- Framework: **Axum** ## Application Layer
- ORM / query builder: **SeaORM** with migrations via `sea-orm-cli`
- OpenAPI docs generated at runtime (see `/doc` crate)
- Runs on the `tokio` async runtime
**Crate layout:** `crates/application` contains use cases backed by port traits. No concrete adapters here — fully unit-testable with in-memory fakes from `domain`'s `test-helpers` feature:
| Crate | Role | | Module | Responsibility |
|---|---| |---|---|
| `api` | HTTP handlers, extractors, routers | | `auth` | Register, login |
| `app` | Business logic, persistence layer | | `thoughts` | Create, delete, get thread, edit |
| `models` | Shared domain types, query/schema structs | | `social` | Follow/unfollow, like/unlike, boost/unboost, block |
| `common` | Shared utilities (pagination, etc.) | | `feed` | Home feed, public feed, user feed, tag feed |
| `doc` | OpenAPI / Swagger doc generation | | `notifications` | List, count unread, mark read |
| `migration` | Database migrations (SeaORM) | | `profile` | Update profile, top friends |
| `utils` | Test helpers, DB utilities | | `api_keys` | Create, list, revoke |
| `federation_management` | Pending followers, accept/reject, domain blocks |
### Frontend — Next.js ## Event System
Server-Side Rendered (SSR) web client. Fetches data from the Rust API during the request-response cycle; minimal client-side JS. 1. Use cases publish `DomainEvent`s via `EventPublisher` (port)
2. `adapters/nats` implements `EventPublisher` → writes to NATS JetStream
3. `thoughts-worker` consumes events via `EventConsumer` (pull consumer, 1-hour TTL caching)
4. Worker handlers perform AP fan-out and write notifications back to PostgreSQL
- Uses **shadcn/ui** component library NATS is optional — when not configured, events are queued in-process and the worker runs without it (federation and notifications still work, just synchronously).
- Frutiger font for the Frutiger Aero aesthetic
- Progressive Web App (PWA) manifest included
### Database — PostgreSQL
Single source of truth. UUIDs as primary keys to prevent enumeration and ease future federation.
### Reverse Proxy — Nginx
Routes traffic:
- `yourdomain.com/api/*` → Rust backend
- `yourdomain.com/*` → Next.js frontend
## Authentication ## Authentication
Two methods:
| Method | Header | Use case | | Method | Header | Use case |
|---|---|---| |---|---|---|
| JWT | `Authorization: Bearer <token>` | Official web client sessions (short-lived) | | JWT | `Authorization: Bearer <token>` | Web client sessions (short-lived) |
| API Key | `Authorization: ApiKey <key>` | Third-party apps (long-lived, user-generated) | | API Key | `Authorization: ApiKey <key>` | Third-party apps (long-lived, user-generated) |
## Planned: ActivityPub Federation ## ActivityPub
Each user profile will be exposed as an ActivityPub actor (e.g. `@username@thoughts.social`), making the platform a Fediverse citizen compatible with Mastodon and other federated platforms. `GET /users/{username}` performs content negotiation:
- `Accept: application/activity+json` → AP actor JSON
- Otherwise → REST profile JSON
Federation endpoints:
- `GET /.well-known/webfinger` — WebFinger discovery
- `GET /.well-known/nodeinfo` + `GET /nodeinfo/2.1` — NodeInfo
- `GET /users/{username}/outbox` — paginated outbox
- `GET /users/{username}/followers` / `/following` — AP collections
- `POST /users/{username}/inbox` — shared inbox