96 lines
6.4 KiB
Markdown
96 lines
6.4 KiB
Markdown
# 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 `<html>`.
|
|
- **`client.ts`** — `BffClient`: one typed method per BFF endpoint. **The only place that
|
|
does `fetch`.**
|
|
- **`provider.tsx`** — `<MessagingInboxProvider>` 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 `<Providers config=…>` (client) and the
|
|
theme is inlined into `<head>` for a flash-free first paint.
|
|
3. `MessagingInboxProvider` applies theme vars to `<html>`, 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).
|