Project
Architecture
Architecture
Topology
flowchart TD
browser[Browser] --> web[apps/web React SPA]
wechat[WeChat] --> miniapp[apps/miniapp native shell]
miniapp -->|WebView| web
web -->|"/api/*"| api[apps/api Hono]
web -->|"/api/auth/*"| api
miniapp -->|"wx.login + bearer bridge"| api
api --> app[Application use cases]
app --> domain[Domain aggregates]
app --> ports[Repository ports]
ports --> pg[PostgreSQL adapters]
api --> auth[Better Auth]
auth --> pg
subgraph cf [Cloudflare runtime]
pages[Pages] --> web
worker[Workers] --> api
worker --> hyperdrive[Hyperdrive] --> extpg[External PostgreSQL]
end
subgraph dk [Docker runtime]
webc[web container] --> web
apic[api container] --> api
apic --> pgc[postgres container]
endLayers
- apps/web — React SPA (FSD). Renders Trips and the Planner, talks to the
API over
fetch, and delegates auth to the Better Auth client. - apps/miniapp — dependency-free native WeChat shell. It exchanges
wx.loginfor a short-lived in-memory bearer, mints a single-use WebView code per native page, and hosts the responsive PWA behind a native page stack (one native page per page-level PWA route) so navigation bar, back gestures, share cards, and deep links are native. See ../frontend/miniapp.md. - apps/api — Hono app (DDD + Hexagonal).
interfaces/httproutes callapplicationuse cases, which operate ondomainaggregates through repository ports implemented ininfrastructure. - packages/agent-ui-catalog — framework-neutral json-render catalog, prompt, streaming-part compiler, and runtime safety boundary shared by the AI adapter and planner renderer. Its Zod 4 dependency is isolated from the API's Zod 3 HTTP schemas.
Request flow
- The SPA calls
GET /api/trips(credentials included). - Hono middleware resolves the Better Auth session; unauthenticated requests
get
401. - The route handler invokes a use case; the use case loads the aggregate via a repository port and returns a DTO.
- The handler serializes the DTO with the shared response envelope.
Auth flow
- The SPA uses the Better Auth React client against
/api/auth/*. - The mini program calls the same auth surface, captures the bearer plugin's
set-auth-tokenresponse header, and sendsAuthorization: Bearer. - The API mounts
auth.handleron/api/auth/*. Sessions are cookie-based; the bearer plugin converts mobile credentials to the same session model; business routes read the session through shared middleware.
Data flow
- All persistence is PostgreSQL. Better Auth and the domain repositories share one connection pool.
- Seed data mirrors the prototype (members, days, 22 stops, 8 expenses).
- After committed trip mutations, the Workers runtime publishes change events through a Durable Object so connected planner sessions refresh. See ../backend/realtime.md.
Runtime differences (Cloudflare vs Docker)
| Concern | Cloudflare | Docker |
|---|---|---|
| Frontend | Pages (apps/web/dist) | static container |
| API | Workers (fetch entry) | Node (@hono/node-server) |
| DB connection | Hyperdrive binding connectionString | DATABASE_URL |
| Realtime | Trip Durable Object WebSocket | HTTP-only; use Workers for full realtime |
| Secrets | wrangler secret / vars | env file |
| Avatar storage | S3-compatible API configured by env (R2) | persistent filesystem volume or S3-compatible API |
Both runtimes import the same application/domain/infrastructure code; only
the entry point and the connection-string source differ. See
../operations/cloudflare.md and
../operations/docker.md.
Internationalization
The frontend is fully internationalized with react-i18next (English +
Chinese). All user-facing copy is centralized in locale resources. See
../frontend/i18n.md.