6.4 KiB
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).
┌──────────────────────────────────────────────────────────────────────┐
│ 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 aThemeConfiginto CSS variables and applies them to<html>.client.ts—BffClient: one typed method per BFF endpoint. The only place that doesfetch.provider.tsx—<MessagingInboxProvider>holds config, the client, bootstrap data, and theme/scheme state. ExposesuseSdk,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.tsxcomposes rail + sidebar + main + thread panel + overlays.
Request lifecycle (send a message)
Composercallssend(body)fromuseMessages(channelId).- The hook calls
client.sendMessage(channelId, body)→POST /api/bff/channels/:id/messages. - The route handler calls
store.send(...), which appends aMessageand bumps thread/channel metadata, and returns it. - 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)
app/layout.tsx(server) callsgetServerConfig()→resolveConfig({ env: process.env }).- The resolved plain
SdkConfigis passed to<Providers config=…>(client) and the theme is inlined into<head>for a flash-free first paint. MessagingInboxProviderapplies theme vars to<html>, manages light/dark, and exposes flags viauseFeature.
Why these choices
See ../.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).