# Demeter — Systems Deep Dive

> The `apps/demeter/` area: the three deployable client/server applications of
> Demeter, a home-gardening platform — a Fastify REST API plus an Expo mobile
> app and a Vite web dashboard that both consume it.

## What this area is

Demeter is a home-gardening product: users model their gardens and beds, track
plantings through their lifecycle, log harvests and journal observations, manage
garden tasks, wire up IoT soil/environment sensors, watch the weather, and lean
on a gardening assistant. The `apps/demeter/` directory holds the three
**deployable applications** that surface that product — it is the app tier, not
the domain logic. The reusable domain packages live separately under
`libs/demeter/*` (for example `@demeter/core`, which owns the Drizzle schema and
Zod shapes, alongside feature libraries like `analytics`, `automation`,
`biodynamic`, `community`, and more).

The three apps are exactly the projects `git ls-files` reports under this tree:
`@demeter/api` (the backend, `apps/demeter/api`), `@demeter/mobile` (the React
Native client, `apps/demeter/mobile`), and `@demeter/web` (the browser
dashboard, `apps/demeter/web`). All three carry the `scope:demeter` / `type:app`
Nx tags; the API additionally tags `layer:service`, mobile tags
`platform:mobile`, and web tags `platform:web` (see each `project.json`).

The relationship between them is a classic one-backend / two-frontend split.
`@demeter/api` is the single source of truth: it exposes a versioned REST
surface under `/v1/*` (the route groups registered in
`apps/demeter/api/src/routes/index.ts` are `gardens`, `plants`, `plantings`,
`tasks`, `harvests`, `sensors`, `weather`, `observations`, `ai`, and
`community`, plus unauthenticated `/health*` probes). Both `@demeter/mobile` and
`@demeter/web` are pure clients of that surface — each ships its own fetch-based
API client (`apps/demeter/mobile/src/api/client.ts` and
`apps/demeter/web/src/api/client.ts`) and a parallel set of React Query hooks
per domain (`src/api/hooks/use*.ts` in both). They do not share code directly;
they share the wire surface the API defines.

## How it fits the wider system

`@demeter/api` is the only node here that reaches into the rest of the monorepo:
its `package.json` depends on `@demeter/core` (the Drizzle schema —
`demeterGardens`, `demeterGardenBeds`, `demeterGardenMembers`, etc., imported by
the stores) plus the platform libraries `@oshun/database`, `@oshun/errors`, and
`@oshun/logging`. It persists to PostgreSQL via Drizzle, caches/rate-limits via
Redis, and integrates the public Open-Meteo weather service. The two client apps
depend on nothing in the workspace at the app boundary — they talk HTTP to the
API and store tokens locally (Expo `SecureStore` on mobile, `localStorage` on
web), so the boundary between this area and the rest of the platform is the REST
contract plus `@demeter/core`/`@oshun/*` on the server side. The
`apps/demeter/docker-compose.yml` and the API's `Dockerfile` describe how the
backend runs against its Postgres/Redis dependencies.

## Entity reference

### @demeter/api

The Fastify backend (`apps/demeter/api`), described by its own README as the
"Demeter Home Gardening API — Fastify-based REST API." Its architecture is a
real ordered plugin stack assembled in `src/app.ts`'s `buildServer()`:
request-context, security headers, CORS, error handler, Swagger/OpenAPI, JWT
auth, a PostgreSQL+Drizzle database plugin, Redis, and rate limiting, with all
route groups registered last. Persistence is genuinely relational — the `src/db`
stores (e.g. `garden-store.ts`) issue Drizzle queries against the
`@demeter/core` tables, map between the public API shape and the schema (garden
`location` to lat/long/elevation/aspect columns, bed metres to centimetres, soil
type to a jsonb `soil_composition`), guard UUIDs, strip NUL bytes, and use
soft-delete (`deletedAt`). Six checked-in migrations live under `drizzle/`
(`0000_demeter_initial` through `0005_community_tables`). External integrations
are dependency-injectable seams: `src/weather/weather-provider.ts` ships a real
Open-Meteo client (mapping WMO codes and wind degrees, frost-risk thresholds)
that tests override with a deterministic provider, and
`src/auth/oauth-verifiers.ts` is similarly injectable. The `/v1/ai/*` routes
(`src/routes/ai.ts`) are honestly **heuristic, not an LLM pass-through** — and
the file says so: plant identification is a trait-matching scoring algorithm
over a built-in profile table, diagnosis is a symptom-disease correlation
engine, plus planting-plan generation, companion-aware layout optimization,
harvest-timing prediction, and a keyword-scored chat knowledge base. Testing is
extensive and layered: unit specs in `src/__tests__/`, a full
`test/integration/` suite, and a `test/performance/` suite with its own harness
and benchmarks.

### @demeter/mobile

The Expo / React Native client (`apps/demeter/mobile`) — "Demeter - Home
Gardening Platform Mobile App." `App.tsx` is a real entry point that wires
`SafeAreaProvider`, React Query, and the navigation container, restores the
session from stored tokens, and wires the API client's 401 handler to force
logout. The app is broad and implemented, not scaffolded: `src/screens/`
contains full screen sets for `gardens`, `plants`, `journal`, `tasks`,
`sensors`, `weather`, `ai` (plant identification/diagnosis, assistant,
recommendations feed), `community`, `settings`, and `auth`, navigated through
`src/navigation/AppNavigator.tsx`. Server access goes through
`src/api/client.ts` — a fetch client with Expo `SecureStore` token storage,
deduplicated 401 refresh, exponential-backoff retries, and per-domain React
Query hooks under `src/api/hooks/`. The standout is the offline layer in
`src/services/offline/`: `SyncEngine.ts` is a real priority queue (CRITICAL→LOW
enum), persisted to `AsyncStorage`, with deduplication by key, batched
processing, per-item exponential-backoff retry, status listeners, and auto-sync
— accompanied by `ActionQueue`, `ConflictResolver`, `OfflineStorage`,
`PlantCache`, `WeatherCache`, `SensorSync`, and `OfflinePhotoManager`. There is
also a `src/services/widgets/` set (Siri Shortcuts, Google Assistant, Quick
Actions, Watch, Widget data, Notification actions) and Jest test suites
co-located with the screens and services.

### @demeter/web

The Vite + React browser dashboard (`apps/demeter/web`). `src/App.tsx`
initializes the auth store and theme then mounts a `RouterProvider`;
`src/router.tsx` defines a large lazy-loaded route tree — eager `auth` pages
plus protected sections for `dashboard`, `gardens` (including a planner and a
`GardenPlanner3DPage`), `plants` (companion matrix, planting calendar,
pest/disease browser, comparison), `tasks` (calendar, recurring, scheduler,
templates, bulk, export), `harvests`, `sensors`/IoT (visualization, automation
rules, scenes, alerts, device pairing, real-time monitor, history), `weather`,
`journal`, `analytics` (yield, cost, resource, environmental, comparison, report
builder, export, printable), `community`, `settings`, and `profile`. It talks to
the same backend via `src/api/client.ts` (base URL from `VITE_API_URL`, falling
back to `/api`, with `localStorage` tokens and 401 auto-refresh) and per-domain
hooks under `src/api/hooks/`. The component library is real (`src/components/`
charts, forms, layout, table, ui) and styling is Tailwind. One honest caveat
lives in `src/stores/expensesStore.ts`: it is a Zustand store backed by
`localStorage` with seed data, explicitly labelled in its own docstring as a
stand-in for `CostAnalysisPage` "before the production expense API is available"
— a documented local placeholder, not a claimed server integration. Quality is
covered by `src/__tests__/` (vitest) and an `e2e/` Playwright suite spanning
auth, gardens, plants, tasks, sensors, community, journal, navigation, and
plant-id flows.
