packages/agentflow/ARCHITECTURE.md
This document describes the internal architecture of the @flowiseai/agentflow package.
The package follows a Domain-Driven Modular Architecture with clear separation of concerns. The goal is to separate low-level UI primitives from high-level business logic to ensure high reusability and easy testing.
src/
├── index.ts # Public Package API ("Public Face")
├── Agentflow.tsx # Main component
├── AgentflowProvider.tsx # Root provider (composition)
├── useAgentflow.ts # Primary public hook
│
├── atoms/ # ⚛️ UI Primitives ("What it looks like" (Dumb))
├── features/ # 🧩 Domain Features ("What it does" (Smart))
├── core/ # 🧠 Business Logic ("Business Rules" (Types/Logic))
└── infrastructure/ # 🌐 External Services ("Outside World" (API/Storage))
atoms/ - UI Primitives"What it looks like." Tiny, irreducible UI components with no business logic. These are the building blocks used by features.
atoms/
├── MainCard.tsx # Styled card wrapper
├── NodeInputHandler.tsx # Form input for node properties
├── ... # Other UI primitives
└── index.ts # Central export
Rules:
features/ or infrastructure/ (except types from core/types for prop definitions and design tokens from core/theme)Goal: 100% visual consistency.
features/ - Domain Features"What it does." Complex, self-contained domain modules (silos) that house the application's core functionality. Each feature owns its components, hooks, and utilities.
features/
├── canvas/ # Core ReactFlow canvas
│ ├── containers/ # Smart components with state/logic
│ │ ├── AgentFlowNode.tsx
│ │ ├── AgentFlowEdge.tsx
│ │ ├── ... # Other node containers
│ │ └── index.ts
│ ├── components/ # Presentational components
│ │ ├── ConnectionLine.tsx
│ │ ├── NodeOutputHandles.tsx
│ │ ├── ... # Other presentational components
│ │ └── index.ts
│ ├── hooks/ # Canvas-related hooks
│ │ ├── useFlowHandlers.ts
│ │ ├── useDragAndDrop.ts
│ │ ├── ... # Other canvas hooks
│ │ └── index.ts
│ ├── styled.ts # Styled components
│ └── index.ts # Public API
│
├── node-palette/ # Add nodes drawer
│ ├── AddNodesDrawer.tsx
│ ├── search.ts # Feature-specific utility (private)
│ ├── ...
│ └── index.ts
│
├── generator/ # AI flow generation
│ ├── GenerateFlowDialog.tsx
│ ├── ...
│ └── index.ts
│
└── node-editor/ # Node property editing
├── EditNodeDialog.tsx
└── index.ts
Rules:
index.ts gatekeepercanvas needs logic from generator, move that logic to core/containers/ for smart components (with state, hooks, side effects)components/ for presentational components (props in, JSX out)Goal: High cohesion. Should be able to delete a feature folder without breaking others.
core/ - Business Logic"The Brain." Framework-agnostic logic, types, and utilities. No React, no UI - pure TypeScript. Contains validation schemas, node registries, domain constants, and shared types.
core/
├── types/ # Global interfaces (Node, Edge, Flow)
│ └── index.ts
├── node-config/ # Node configuration (icons, colors, default types)
│ ├── nodeIcons.ts # AGENTFLOW_ICONS, DEFAULT_AGENTFLOW_NODES
│ └── ...
├── node-catalog/ # Node catalog and filtering logic
│ ├── nodeFilters.ts # filterNodesByComponents, isAgentflowNode
│ └── ...
├── theme/ # Design tokens and MUI theming
│ ├── tokens.ts # Color palettes, spacing, shadows
│ ├── createAgentflowTheme.ts
│ └── ...
├── validation/ # Flow validation logic
│ ├── flowValidation.ts # validateFlow, validateNode
│ ├── connectionValidation.ts # isValidConnectionAgentflowV2
│ └── ...
├── utils/ # Generic utilities
│ ├── nodeFactory.ts # initNode, getUniqueNodeId
│ └── ...
└── index.ts # Barrel export (use sparingly)
Rules:
Goal: To be the framework-agnostic source of truth.
infrastructure/ - External Services"The Outside World." Handles communication with external systems: data persistence, network requests, and global state management.
infrastructure/
├── api/ # API client layer (network requests)
│ ├── client.ts # Axios factory
│ ├── ... # Endpoint modules (nodes, chatflows, etc.)
│ └── index.ts
│
└── store/ # State management
├── AgentflowContext.tsx # Flow state context
├── agentflowReducer.ts # Reducer for flow state actions
├── ... # Other contexts (ApiContext, ConfigContext)
└── index.ts
Rules:
Goal: To wrap external dependencies so they can be easily swapped or mocked in tests.
Dependencies must only flow downwards.
┌─────────────────────────────────────────────────────────┐
│ Root Files (Public API) │
│ (index.ts, Agentflow.tsx, AgentflowProvider.tsx) │
│ "The Public Face" │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ features/ │
│ (canvas, node-palette, generator, node-editor) │
│ "What it does" │
└─────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌────────────────────────┐
│ atoms/ │ │ infrastructure/ │
│ (UI primitives) │ │ (api, store) │
│ "What it looks │ │ "The Outside World" │
│ like" │ │ │
└──────────────────┘ └────────────────────────┘
│ │
└──────────────┬─────────────────────┘
▼
┌─────────────────────┐
│ core/ │
│ (types, constants, │
│ validation, etc.) │
│ "The Brain" │
└─────────────────────┘
Import Rules:
features → atoms, infrastructure, core ✅infrastructure → core ✅atoms → core/types and core/theme only (for type definitions and design tokens) ✅core → nothing (leaf node) ✅features/ or infrastructure/Each module exposes only what's needed via its index.ts:
// features/canvas/index.ts
// ✅ Public API
export const nodeTypes = { ... }
export const edgeTypes = { ... }
export { ConnectionLine, AgentflowHeader, createHeaderProps }
export { useFlowNodes, useFlowHandlers, useDragAndDrop }
// Container components are re-exported for advanced usage
export { AgentFlowNode, AgentFlowEdge, StickyNote, IterationNode }
// ❌ Internal sub-components stay private within containers/components
This enables:
Create folder under features/:
features/my-feature/
├── containers/ # Smart components (optional)
│ └── MyContainer.tsx
├── components/ # Presentational components (optional)
│ └── MyComponent.tsx
├── hooks/ # Feature-specific hooks (optional)
│ └── useMyHook.ts
├── helper.ts # Feature-specific util
└── index.ts # Public exports only
Export only what's needed:
// index.ts
export { MyContainer } from './containers'
export { MyComponent } from './components'
export { useMyHook } from './hooks'
export type { MyComponentProps } from './components'
Import from infrastructure/core, never from other features:
// ✅ Good
import { useApiContext } from '../../infrastructure/store'
import type { NodeData } from '../../core/types'
// ❌ Bad - cross-feature import
import { AgentFlowNode } from '../canvas/containers/AgentFlowNode'
src/*.ts)"The Public Face." The entry point of the package.
AgentflowProvider.tsx: Injects infrastructure (stores/api) into the appAgentflow.tsx: The primary component users will drop into their appsuseAgentflow.ts: Primary public hook for accessing flow state and actionsindex.ts: The "Barrel" that exports the public API for npmThe package exposes a minimal public API via src/index.ts:
// Components
export { Agentflow } from './Agentflow'
export { AgentflowProvider } from './AgentflowProvider'
// Hooks
export { useAgentflow } from './useAgentflow'
export { useApiContext, useConfigContext, useAgentflowContext } from './infrastructure/store'
// Types
export type { AgentflowProps, FlowData, NodeData, ... } from './core/types'
// Utilities (for advanced usage)
export { AGENTFLOW_ICONS, validateFlow, ... } from './core/...'
Everything else is internal implementation detail.
Every directory must have an index.ts that acts as a gatekeeper.
✅ Good:
import { Button } from '@atoms'
import { useFlowHandlers } from '@features/canvas'
❌ Bad:
// Never deep-link into files
import { Button } from '@atoms/Button/Button'
import { useFlowHandlers } from '@features/canvas/hooks/useFlowHandlers'
| Type | Convention | Example |
|---|---|---|
| Component | PascalCase.tsx | AgentFlowNode.tsx |
| Hook | camelCase.ts (use prefix) | useFlowHandlers.ts |
| Logic/Types | camelCase.ts | flowValidation.ts, nodeFilters.ts |
| Styles | kebab-case (co-located) | canvas.css |