Architecture
Chativa is a Hexagonal (Ports & Adapters) TypeScript monorepo. The dependency rule is enforced by file structure, by tsconfig paths, and by code review:
UI → Application → Domain ← Infrastructure
Inner layers never import from outer layers. If you haven't read Concepts, start there — this page is the layered reference.
Layered view
| Layer | Package | Contents |
|---|---|---|
| UI | @chativa/ui, @chativa/genui | LitElement Web Components — chat-iva, chat-bot-button, message renderers, GenUI components. |
| Application | @chativa/core (application/) | ChatEngine, MultiConversationEngine, registries, Zustand stores, EventBus, ChativaContext, applyGlobalSettings(). |
| Domain | @chativa/core (domain/) | Pure types: ports (IConnector, IExtension, ISlashCommand, IMessageRenderer), entities (Message, GenUI, Conversation), value objects (Theme, ThemeBuilder). |
| Infrastructure | connector-* packages | Adapters: Dummy, WebSocket, SignalR, DirectLine, SSE, HTTP. |
flowchart TB
subgraph UI["UI · @chativa/ui · @chativa/genui"]
chativa["chat-iva"]
button["chat-bot-button"]
msgs["Message renderers"]
genui["GenUI components"]
end
subgraph App["Application · @chativa/core/application"]
engine[ChatEngine]
multi[MultiConversationEngine]
registries[Registries]
stores["Stores · Zustand"]
bus[EventBus]
ctx[ChativaContext]
end
subgraph Dom["Domain · @chativa/core/domain"]
ports["Ports — IConnector · IExtension · ISlashCommand"]
entities["Entities — Message · GenUI · Conversation"]
vo["Value Objects — Theme"]
end
subgraph Infra["Infrastructure · connector-*"]
dummy[Dummy]
ws[WebSocket]
sr[SignalR]
dl[DirectLine]
sse[SSE]
http[HTTP]
end
UI -->|uses| App
App -->|depends on| Dom
Infra -.->|implements| Dom
App -->|orchestrates| Infra
What lives where
domain/ — pure types
Zero runtime imports. No DOM, no lit, no zustand. If you need to add a class with side effects, it doesn't belong here.
ports/IConnector.ts— the connector contract (15+ optional capability hooks).ports/IExtension.ts— middleware lifecycle (onBeforeSend,onAfterReceive,onWidgetOpen/Close).ports/ISlashCommand.ts— slash command contract.ports/IMessageRenderer.ts— message component contract.entities/Message.ts—IncomingMessage,OutgoingMessage,MessageAction,MessageStatus,HistoryResult.entities/GenUI.ts—AIChunk,GenUIStreamState,GenUIChunkHandler.entities/Conversation.ts— multi-conversation entity.value-objects/Theme.ts—ThemeConfig,mergeTheme(),themeToCSS(),DEFAULT_THEME.value-objects/ThemeBuilder.ts— fluent theme builder.
application/ — orchestration
ChatEngine— wires a connector to the stores and extension pipeline. Owns reconnect logic.MultiConversationEngine— runs manyChatEngines; lazy-mounts per conversation.- Registries —
ConnectorRegistry,MessageTypeRegistry,ExtensionRegistry,SlashCommandRegistry. - Stores (Zustand vanilla) —
chatStore,messageStore,conversationStore. EventBus— typed pub/sub for analytics hooks.ChativaContext+createChativaContext()— facade injected into connectors and extensions.ChativaSettings+applyGlobalSettings()— readswindow.chativaSettingsand applies it before mount.
connector-* packages — adapters
Each implements IConnector. Optional capabilities are feature-detected at runtime via typeof connector.sendFile === "function". See connectors/overview.
ui/ and genui/ — Web Components
LitElement components. State comes exclusively from chatStore / messageStore subscriptions — components never instantiate connectors or registries directly.
Request flow
sequenceDiagram
participant U as User
participant Input as ChatInput
participant Engine as ChatEngine
participant Ext as ExtensionRegistry
participant Conn as IConnector
participant Store as messageStore
U->>Input: types "Hello"
Input->>Engine: send(text)
Engine->>Ext: onBeforeSend(msg)
Ext-->>Engine: msg or null
Engine->>Store: add(user message, status sending)
Engine->>Conn: sendMessage(msg)
Conn-->>Engine: ack
Conn->>Engine: onMessageStatus(id, "sent")
Engine->>Store: patch(id, status sent)
Conn->>Engine: onMessage(reply)
Engine->>Ext: onAfterReceive(reply)
Ext-->>Engine: reply or null
Engine->>Store: add(bot reply)
Why hexagonal?
- Swap any connector without touching UI or core code.
- Test
ChatEngineagainst a mockIConnectorwith no DOM and no network. - Keep
domain/framework-free — same types could power a React Native renderer in v2. - The contract drift between
domain/types and the JSON Schemas inschemas/is automatically guarded — see schema sync on GitHub.