# Architecture ## The one idea Three product surfaces — **Messaging**, **Inbox**, **Support** — are built on **one kernel**: a headless React SDK talking to a **BFF**. Everything a user sees is a projection of the same data model, exactly like the IIOS "everything is an interaction" spine (see [`IIOS_INTEGRATION.md`](IIOS_INTEGRATION.md)). ``` ┌──────────────────────────────────────────────────────────────────────┐ │ Browser (Next.js app · apps/web) │ │ │ │ Components (nav / message / inbox / support / overlays) │ │ │ use hooks, never fetch directly │ │ ┌────▼───────────────────────────────────────────┐ │ │ │ @lynkd/messaging-inbox-sdk │ │ │ │ Provider · hooks · theme · feature flags │ │ │ │ BffClient ────────────────┐ │ │ │ └──────────────────────────────┼───────────────────┘ │ └──────────────────────────────────┼─────────────────────────────────────┘ │ fetch /api/bff/** (the ONLY seam) ┌──────────────────────────────────▼─────────────────────────────────────┐ │ BFF (Next.js route handlers · apps/web/app/api/bff/**) │ │ bootstrap · channels · messages · threads · reactions · inbox · │ │ support/tickets · search · notifications │ │ │ │ │ ┌────▼─────────────────────────┐ (swap this box for real) │ │ │ Mock store (in-memory) │ ──► IIOS service / NestJS / Supabase │ │ └──────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────┘ ``` ## Layers ### 1. SDK — `packages/messaging-inbox-sdk` Framework-agnostic-ish React layer (needs React 18/19). No knowledge of Next.js, no backend coupling. - **`types.ts`** — the domain model. `User`, `Channel` (channel/private/dm/group_dm), `Message` (+ parts, reactions, attachments, thread metadata), `InboxItem`, `Ticket`, `Notification`, `SearchResult`, `Bootstrap`. - **`config.ts`** — `FeatureFlags` + `ThemeConfig` + `resolveConfig()` (defaults → env → props). - **`theme.ts`** — turns a `ThemeConfig` into CSS variables and applies them to ``. - **`client.ts`** — `BffClient`: one typed method per BFF endpoint. **The only place that does `fetch`.** - **`provider.tsx`** — `` holds config, the client, bootstrap data, and theme/scheme state. Exposes `useSdk`, `useFeature(s)`, `useTheme`. - **`hooks.ts`** — data hooks: `useChannels`, `useMessages`, `useThread`, `useInbox`, `useTickets`, `useTicket`, `useSearch`, `useNotifications`, `useAvailability`, `useTyping`. ### 2. BFF — `apps/web/app/api/bff/**` Next.js **Route Handlers**. This *is* the Backend-For-Frontend: it shapes data specifically for this UI, keeps secrets/tokens server-side, and decouples the client from the real backend. Today each handler calls the in-memory **mock store**; each maps 1:1 to a `BffClient` method. ### 3. Mock store — `apps/web/lib/mock/` `data.ts` is the seed (a full LynkedUp-flavoured workspace). `store.ts` is a mutable singleton (persists across requests in one Node process) implementing send/react/pin/save/patch/reply/ search. **This is the box you replace to go real** — nothing above it changes. ### 4. App shell + components — `apps/web/components` - `nav/` — `WorkspaceRail` (Slack-style far-left), `Sidebar` (sectioned channels + user card), `Header` (title + search + theme + notifications + user). - `message/` — `ChannelView`, `MessageItem`, `Composer`, `ThreadPanel`, `EmojiPicker`. - `inbox/` — `InboxView`. - `support/` — `SupportView` (master-detail console). - `overlays/` — `CommandPalette`, `NotificationsPanel`, `ProfileDrawer`, `ThemingPanel`, `HuddleBar`. - `ui/primitives.tsx` — `Avatar`, `PresenceDot`, `CountBadge`, `Chip`, `IconButton`. - `AppShell.tsx` composes rail + sidebar + main + thread panel + overlays. ## Request lifecycle (send a message) 1. `Composer` calls `send(body)` from `useMessages(channelId)`. 2. The hook calls `client.sendMessage(channelId, body)` → `POST /api/bff/channels/:id/messages`. 3. The route handler calls `store.send(...)`, which appends a `Message` and bumps thread/channel metadata, and returns it. 4. The hook appends the returned message to local state → it renders instantly. Reactions, pins, saves, inbox transitions, and ticket replies follow the same shape. ## Config lifecycle (flags + theme) 1. `app/layout.tsx` (server) calls `getServerConfig()` → `resolveConfig({ env: process.env })`. 2. The resolved **plain** `SdkConfig` is passed to `` (client) and the theme is inlined into `` for a flash-free first paint. 3. `MessagingInboxProvider` applies theme vars to ``, manages light/dark, and exposes flags via `useFeature`. ## Why these choices See [`../.claude/memory/architecture-decisions.md`](../.claude/memory/architecture-decisions.md) for the rationale (workspace layout, BFF seam, server-resolved config, CSS-variable theming, data-driven flags, and why the NestJS/DB backend is documented rather than built for this demo).