Haven // Internal Architecture

NEXUS

FRAMEWORK v2.0 // ENTITY LIFECYCLE MANAGEMENT
SCROLL
// 01

The God File Problem

"A hook that knows everything about everything is a hook that controls nothing well."
context
What We Started With
The prior architecture centered on massive orchestration hooks — useChatAppOrchestration and its siblings — managing auth, server state, channels, messages, notifications, voice, and social data simultaneously. At 800+ lines per file, any feature that touched state had to go through them. They were impossible to test in isolation, difficult to trace, and produced unpredictable re-renders whenever any unrelated slice changed.
root cause
No Domain Boundaries
The core failure was a flat state namespace. Communities, channels, messages, and social graphs lived in the same hook scope with no enforcement that they stay separate. Cross-domain access wasn't a pattern — it was the default. The result: coupling that made every change a risk and every bug a multi-file investigation with unclear ownership.
Problem → Re-render cascades
One state update in auth re-rendered components watching message counts. Everything was subscribed to everything because everything lived together.
Problem → Implicit contracts
Consumers relied on undocumented initialization ordering and shared mutable refs. Break the order, break the app — and nothing told you the order existed.
Problem → Untestable
You can't unit-test a hook that requires 12 other hooks initialized first. Every test needed a full render environment just to touch one feature.


// 02

The Nexus Primitive

definition
A Bounded Domain With One Public Face
A Nexus is a self-contained domain that owns its entities from creation to deletion. It exposes a typed public API and hides everything else. No external code touches the Zustand store inside — they call the nexus. No external code knows how entities are persisted — the nexus handles it. No external code manages loading state — the nexus reports it.

The constraint is the feature. When the only interaction path is the public API, you know exactly where every state change originates.
// Generic base — T is your domain type, R is the raw API shape abstract class Nexus<T, R> { private _store: StoreApi<NexusState<T>> | null = null // lazy — no allocation until first access protected get store() { return (this._store ??= create(this.initialState())) } getOrCreate(id: string, raw: R): T getOrPartial(id: string, stub: Partial<T>): T update(id: string, changes: Partial<T>): void delete(id: string): void use<S>(selector: Selector<S>, eq?: EqualityFn<S>): S persist(): Promise<void> rehydrate(): Promise<void> }
"The constraint is the feature. When the only interaction path is the API, you know exactly where every state change originates."

LIFECYCLE METHODS

getOrCreate(id, raw)
Primary insertion. If the entity exists in cache, returns it. Otherwise transforms raw API data via the abstract fromRaw() method, inserts, and stamps a cachedAt timestamp for freshness tracking.
getOrPartial(id, stub)
Placeholder insertion for known-but-not-loaded entities. Marks the entry partial: true so consumers can distinguish "loading" from "not found." Used for optimistic references before full data arrives.
update(id, changes)
Shallow-merges changes into an existing entry and increments the revision counter. Subscribers react to the revision tick — not to deep entity equality — keeping update cost constant.
delete(id)
Removes the entity and fires cleanup callbacks. Downstream reads on a deleted ID get undefined, not stale data — this surfaces missing-reference bugs early rather than silently serving old state.
use(selector, eq?)
The React integration point. Wraps useStoreWithEqualityFn. Callers describe what they want; the nexus owns subscription granularity. The store is never touched directly from component code.
persist / rehydrate()
Storage via injected NexusPersistence adapter — the same nexus runs on Electron (filesystem), web (localStorage), and React Native without any domain logic changing.

// 03

HavenCore — The Router

"HavenCore is not a God file. It doesn't know how to manage channels. It doesn't know how messages are cached. It only knows which nexus to hand an event to."
role
Orchestration Without Domain Knowledge
HavenCore is the session-scoped composition root. It instantiates every nexus, injects backends, and routes inbound realtime events to the correct domain. What it deliberately does not do: implement any domain logic itself. It doesn't parse messages — it hands them to MessageNexus. It doesn't manage channel membership — it hands it to ChannelNexus. Its responsibility is routing and session lifecycle, nothing more.

SESSION BOOTSTRAP

pattern
MessageNexusRegistry — Per-Community Lazy Instantiation
Each community gets its own CommunityMessageNexus, created on first access and destroyed when the community is removed. The registry lives inside HavenCore as a nested class — it manages the per-community map, handles cache cleanup on removal, and injects the shared viewerMessagePolicyStore into each instance at construction time. Per-community message state stays fully isolated while the cross-cutting policy concern is shared efficiently.

// 04

The Policy Contract

the hard problem
Cross-Domain Knowledge Without Coupling
Two domains need to share information: SocialNexus knows who is blocked. MessageNexus needs to filter those users' messages. The naive solution — have MessageNexus call into SocialNexus directly — creates bidirectional coupling. Now neither can change independently, and you've recreated the God file problem at smaller scale.
"The solution is not sharing data. It's sharing a contract — a read-only policy describing what the viewer is allowed to see, computed once by HavenCore and applied everywhere."
// The shared read-only contract — HavenCore writes it, nexuses read it type ViewerMessagePolicyState = { hiddenAuthorIds: ReadonlySet<string> // my blocks ∪ users blocking me showHiddenMessages: boolean // moderator override toggle communities: Record<string, { suppressAuthorFilter: boolean // elevated users bypass block filter canViewBanHidden: boolean // mods see redacted messages revokedAuthorIdsByChannel: Record<string, string[]> }> }
data flow
One Writer. Many Readers. No Coupling.
HavenCore is the only entity that writes to the policy store. It aggregates from SocialNexus (blocked IDs), PermissionsNexus (elevated status, revoked authors), and the UI store (mod toggle) into a single coherent policy object. This store is then injected read-only into every MessageNexus instance at construction time.

MessageNexus reads the policy. It never writes to it. It never touches SocialNexus or PermissionsNexus. The message projection functions (projectVisibleChannelMessages) are pure: canonical messages + policy in → visible messages out. No side effects. No domain knowledge leakage.
Block filtering
Blocked author messages are hidden. The cascade: replies to hidden messages are also hidden, reactions from blocked authors are stripped, attachments from hidden messages are removed.
Revoked authors
Channel-specific revocation from moderation actions. Revoked messages become placeholder bundles — canonical data remains untouched in the nexus store, ready for policy recalculation.
Elevated users
Moderators and owners have suppressAuthorFilter set. They see all messages regardless of block state — you cannot block a moderator out of their own moderation visibility.

// 05

Performance Philosophy

"Discord achieves sub-100ms perceived state changes through optimistic UI and intelligent cache invalidation. The Nexus framework is the infrastructure that makes this tractable."
10s FRESHNESS WINDOW
INFLIGHT PER CHANNEL
O(1) ENTITY LOOKUP
0 REDUNDANT RE-RENDERS
pattern
Snapshot Caching + Revision Counters
Zustand re-runs selectors on every store update. For list selectors over hundreds of messages, this means expensive equality checks on every keystroke, every event, every realtime tick. The nexus solves this with snapshot caching: a private snapshot reference holds the last computed array. The selector reads a revision counter to subscribe, then compares the new projection against the snapshot using a stable equality function. If equal, it returns the same object reference — React bails out of the re-render entirely without touching the DOM.
// Same reference = React bails out, no DOM work private snapshot: Message[] = EMPTY_ARRAY private readonly selector = (state: ChannelState) => { void state.revision // subscribe to changes const next = projectVisibleMessages(state, this.policyStore) if (messagesEqual(this.snapshot, next)) return this.snapshot return (this.snapshot = next) } useMessages(channelId: string) { return useStoreWithEqualityFn(store, this.selector, Object.is) }
pattern
Inflight Promise Deduplication
When a user navigates rapidly between channels, multiple load requests can fire before any completes. Without deduplication, each one updates the store on completion — causing multiple renders and potentially out-of-order state. The nexus holds an inflight promise map keyed by channel ID. If a request is already in flight, the second call returns the existing promise. One network request. One state update. One render.
direction — next phase
Optimistic UI
Bounded domains with typed update paths make optimistic UI tractable. Message sends should write an optimistic entry to the nexus immediately — the user sees their message in under 16ms. Network latency becomes invisible. On response, reconcile: update the temporary entry with the confirmed ID, or roll back cleanly if the backend rejects. The infrastructure exists. The implementation pattern is the next performance lever.