Tổng quan kiến trúc
Tệp nguồn liên quan
Trang này được tạo dựa trên các tệp nguồn sau:
- .claude/rules/sim-architecture.md
- apps/sim/blocks/registry.ts
- apps/sim/connectors/registry.ts
- apps/sim/triggers/registry.ts
- apps/sim/providers/registry.ts
- apps/sim/executor/index.ts
- apps/sim/stores/index.ts
- apps/sim/triggers/index.ts
- apps/sim/triggers/types.ts
- apps/sim/triggers/constants.ts
- packages/db/index.ts
- packages/python-sdk/setup.py
- apps/sim/tools/index.ts
- apps/sim/blocks/index.ts
- apps/sim/socket/index.ts
- packages/cli/src/index.ts
- apps/sim/providers/index.ts
- apps/sim/connectors/index.ts
- apps/sim/serializer/index.ts
- packages/logger/src/index.ts
Sim là một nền tảng tự động hóa workflow dựa trên AI, được xây dựng với kiến trúc module hóa và type-safe. Dự án sử dụng Next.js App Router cho frontend, Drizzle ORM cho database layer, và một DAG Executor cho việc thực thi workflow. Kiến trúc tuân theo nguyên tắc Single Responsibility, ưu tiên Composition Over Complexity, và đảm bảo Type Safety First.
Nguyên tắc cốt lõi và cấu trúc thư mục
Bốn nguyên tắc thiết kế chính
Kiến trúc của Sim được xây dựng trên bốn nguyên tắc cốt lõi được định nghĩa trong quy tắc kiến trúc:
- Single Responsibility: Mỗi component, hook, store có một mục đích rõ ràng
- Composition Over Complexity: Phân tách logic phức tạp thành các phần nhỏ hơn
- Type Safety First: TypeScript interfaces cho tất cả props, state, return types
- Predictable State: Zustand cho global state, useState cho UI-only concerns
(.claude/rules/sim-architecture.md:1-28)
Cấu trúc thư mục Root-Level
Cấu trúc thư mục chính của ứng dụng Sim được tổ chức như sau:
apps/sim/
├── app/ # Next.js app router (pages, API routes)
├── blocks/ # Block definitions and registry
├── components/ # Shared UI (emcn/, ui/)
├── executor/ # Workflow execution engine
├── hooks/ # Shared hooks (queries/, selectors/)
├── lib/ # App-wide utilities
├── providers/ # LLM provider integrations
├── stores/ # Zustand stores
├── tools/ # Tool definitions
└── triggers/ # Trigger definitions
Mỗi thư mục có trách nhiệm riêng biệt: blocks/ chứa định nghĩa và registry của các block, executor/ chứa workflow execution engine, providers/ tích hợp các LLM providers, và stores/ quản lý Zustand stores cho state management.
(.claude/rules/sim-architecture.md:14-28)
Quy tắc tổ chức Feature và Naming Conventions
Các feature được tổ chức dưới app/workspace/[workspaceId]/ với cấu trúc:
feature/
├── components/ # Feature components
├── hooks/ # Feature-scoped hooks
├── utils/ # Feature-scoped utilities (2+ consumers)
├── feature.tsx # Main component
└── page.tsx # Next.js page entry
Naming conventions được quy định:
- Components: PascalCase (
WorkflowList) - Hooks:
useprefix (useWorkflowOperations) - Files: kebab-case (
workflow-list.tsx) - Stores:
stores/feature/store.ts - Constants: SCREAMING_SNAKE_CASE
- Interfaces: PascalCase với suffix (
WorkflowListProps)
Quy tắc Utils quan trọng: không tạo utils.ts cho single consumer - inline nó; chỉ tạo utils.ts khi 2+ files cần cùng một helper. Vị trí ưu tiên: lib/ (app-wide) → feature/utils/ (feature-scoped) → inline (single-use).
(.claude/rules/sim-architecture.md:30-56)
Hệ thống Block Registry
Định nghĩa và đăng ký Blocks
Block Registry là trung tâm của hệ thống, quản lý tất cả các block definitions. Registry import các block từ nhiều provider khác nhau như Airtable, GitHub, Gmail, Google Workspace, Slack, và nhiều hơn nữa.
(apps/sim/blocks/registry.ts:1-50)
Registry object chứa tất cả block configs được đăng ký theo alphabetically sorted:
typescript1export const registry: Record<string, BlockConfig> = { 2 a2a: A2ABlock, 3 agent: AgentBlock, 4 ahrefs: AhrefsBlock, 5 airtable: AirtableBlock, 6 // ... 200+ blocks 7}
(apps/sim/blocks/registry.ts:207-270)
Các loại Block: Tools, Triggers, Blocks
Hệ thống phân loại blocks thành ba category chính:
| Category | Mô tả | Ví dụ |
|---|---|---|
blocks | Các block xử lý logic workflow | Agent, Condition, Router |
tools | Các công cụ tích hợp external services | GitHub, Slack, Gmail |
triggers | Các block khởi tạo workflow | Manual Trigger, Schedule, Webhook |
Hàm getBlocksByCategory cho phép lọc blocks theo category:
typescript1export const getBlocksByCategory = (category: 'blocks' | 'tools' | 'triggers'): BlockConfig[] => 2 Object.values(registry).filter((block) => block.category === category)
(apps/sim/blocks/registry.ts:466-467)
API truy xuất Block
Registry cung cấp nhiều helper functions để truy xuất blocks:
- getBlock(type: string): Lấy block theo type, hỗ trợ normalize
-thành_ - getLatestBlock(baseType: string): Lấy version mới nhất của block (ví dụ:
google_sheets_v2) - getBlockByToolName(toolName: string): Tìm block theo tool name
- getAllBlockTypes(): Lấy tất cả block types
- isValidBlockType(type: string): Kiểm tra type có hợp lệ không
typescript1export const getBlock = (type: string): BlockConfig | undefined => { 2 if (registry[type]) { 3 return registry[type] 4 } 5 const normalized = type.replace(/-/g, '_') 6 return registry[normalized] 7} 8 9export const getLatestBlock = (baseType: string): BlockConfig | undefined => { 10 const normalized = baseType.replace(/-/g, '_') 11 const versionedKeys = Object.keys(registry).filter((key) => { 12 const match = key.match(new RegExp(`^${normalized}_v(\\d+)$`)) 13 return match !== null 14 }) 15 // Sort và return version cao nhất 16}
(apps/sim/blocks/registry.ts:434-474)
Trigger System và Connector Registry
Cấu hình Trigger và Webhook
Trigger System định nghĩa cách workflow được khởi tạo. Mỗi trigger có cấu trúc TriggerConfig với các thuộc tính:
typescript1export interface TriggerConfig { 2 id: string 3 name: string 4 provider: string 5 description: string 6 version: string 7 icon?: React.ComponentType<{ className?: string }> 8 subBlocks: SubBlockConfig[] 9 outputs: Record<string, TriggerOutput> 10 webhook?: { 11 method?: 'POST' | 'GET' | 'PUT' | 'DELETE' 12 headers?: Record<string, string> 13 } 14 polling?: boolean 15}
(apps/sim/triggers/types.ts:7-35)
Trigger Output Definitions
TriggerOutput interface định nghĩa cấu trúc dữ liệu đầu ra của trigger:
typescript1export interface TriggerOutput { 2 type?: string 3 description?: string 4 [key: string]: TriggerOutput | string | undefined 5}
Hệ thống hỗ trợ hai loại trigger:
- Push-based: Webhook-driven, nhận event real-time từ external services
- Poll-based: Cron-driven, kiểm tra định kỳ theo schedule
(apps/sim/triggers/types.ts:1-6)
Hàm getTrigger và Namespace Management
Hàm getTrigger là entry point chính để lấy trigger config, với logic namespace subBlock IDs để tránh conflicts:
typescript1export function getTrigger(triggerId: string): TriggerConfig { 2 const trigger = TRIGGER_REGISTRY[triggerId] 3 if (!trigger) { 4 throw new Error(`Trigger not found: ${triggerId}`) 5 } 6 7 // Clone và filter deprecated trigger-save subblocks 8 const subBlocks = trigger.subBlocks 9 .filter((subBlock) => subBlock.id !== 'triggerSave' && subBlock.type !== 'trigger-save') 10 .map((subBlock) => namespaceSubBlockId(subBlock, triggerId)) 11 12 // Inject samplePayload cho webhooks/pollers 13 if (trigger.webhook || trigger.id.includes('webhook') || trigger.id.includes('poller')) { 14 // Generate mock payload từ outputs definition 15 } 16}
(apps/sim/triggers/index.ts:70-116)
Connector Registry cho External Services
Connector Registry quản lý các kết nối đến external services như Airtable, GitHub, Gmail, Google Workspace, Slack, và nhiều hơn nữa:
typescript1export const CONNECTOR_REGISTRY: ConnectorRegistry = { 2 airtable: airtableConnector, 3 asana: asanaConnector, 4 confluence: confluenceConnector, 5 discord: discordConnector, 6 dropbox: dropboxConnector, 7 // ... 30+ connectors 8}
(apps/sim/connectors/registry.ts:33-64)
Provider và Executor Architecture
Provider Initialization và Registry
Provider Registry quản lý lifecycle của các LLM providers:
typescript1export async function getProviderExecutor( 2 providerId: ProviderId 3): Promise<ProviderConfig | undefined> { 4 const provider = providerRegistry[providerId] 5 if (!provider) { 6 logger.error(`Provider not found: ${providerId}`) 7 return undefined 8 } 9 return provider 10} 11 12export async function initializeProviders(): Promise<void> { 13 for (const [id, provider] of Object.entries(providerRegistry)) { 14 if (provider.initialize) { 15 try { 16 await provider.initialize() 17 logger.info(`Initialized provider: ${id}`) 18 } catch (error) { 19 logger.error(`Failed to initialize ${id} provider`, { 20 error: error instanceof Error ? error.message : 'Unknown error', 21 }) 22 } 23 } 24 } 25}
(apps/sim/providers/registry.ts:39-63)
DAG Executor Pattern
Executor sử dụng DAG (Directed Acyclic Graph) pattern để thực thi workflow:
typescript1/** 2 * Executor - Main entry point 3 * Exports the DAG executor as the default executor 4 */ 5export { DAGExecutor as Executor } from '@/executor/execution/executor'
DAG Executor cho phép:
- Thực thi các block theo dependency order
- Parallel execution của các block không phụ thuộc lẫn nhau
- Error handling và retry logic
- Real-time status updates qua Socket.IO
(apps/sim/executor/index.ts:1-6)
Tool Injection Mechanism
Hệ thống cung cấp cơ chế inject hosted key cho tools khi cần:
typescript1async function injectHostedKeyIfNeeded( 2 tool: ToolConfig, 3 params: Record<string, unknown>, 4 executionContext: ExecutionContext | undefined, 5 requestId: string 6): Promise<HostedKeyInjectionResult> { 7 if (!tool.hosting) return { isUsingHostedKey: false } 8 if (!isHosted) return { isUsingHostedKey: false } 9 // ... injection logic 10}
Cơ chế này cho phép Sim cung cấp managed API keys cho các tools, giảm friction cho users khi sử dụng các services cần authentication.
(apps/sim/tools/index.ts:50-57)
State Management và Application Lifecycle
Application Initialization Flow
Quá trình khởi tạo application được quản lý bởi initializeApplication:
typescript1async function initializeApplication(): Promise<void> { 2 if (typeof window === 'undefined' || isInitializing) return 3 4 isInitializing = true 5 appFullyInitialized = false 6 7 const initStartTime = Date.now() 8 9 try { 10 // Load environment variables directly from DB 11 await useEnvironmentStore.getState().loadEnvironmentVariables() 12 13 // Mark data as initialized 14 dataInitialized = true 15 16 const initDuration = Date.now() - initStartTime 17 logger.info(`Application initialization completed in ${initDuration}ms`) 18 19 appFullyInitialized = true 20 } catch (error) { 21 logger.error('Error during application initialization:', { error }) 22 appFullyInitialized = true 23 dataInitialized = false 24 } finally { 25 isInitializing = false 26 } 27}
(apps/sim/stores/index.ts:24-55)
State Check Functions
Hệ thống cung cấp hai functions để kiểm tra initialization state:
typescript1export function isAppInitialized(): boolean { 2 return appFullyInitialized 3} 4 5export function isDataInitialized(): boolean { 6 return dataInitialized 7}
isDataInitialized() nên được check trước khi thực hiện bất kỳ sync operations nào.
(apps/sim/stores/index.ts:60-70)
Cleanup và User Data Management
Application cleanup được handle bởi handleBeforeUnload và cleanupApplication:
typescript1function handleBeforeUnload(event: BeforeUnloadEvent): void { 2 if (typeof window !== 'undefined') { 3 const path = window.location.pathname 4 if ( 5 path === '/login' || 6 path === '/signup' || 7 path === '/reset-password' || 8 path === '/verify' 9 ) { 10 return 11 } 12 } 13 event.preventDefault() 14 event.returnValue = '' 15} 16 17function cleanupApplication(): void { 18 window.removeEventListener('beforeunload', handleBeforeUnload) 19 // Socket.IO handles cleanup 20}
(apps/sim/stores/index.ts:75-101)
Database Connection
Database layer sử dụng Drizzle ORM với PostgreSQL:
typescript1import { drizzle } from 'drizzle-orm/postgres-js' 2import postgres from 'postgres' 3import * as schema from './schema' 4 5const connectionString = process.env.DATABASE_URL! 6if (!connectionString) { 7 throw new Error('Missing DATABASE_URL environment variable') 8} 9 10const postgresClient = postgres(connectionString, { 11 prepare: false, 12 idle_timeout: 20, 13 connect_timeout: 30, 14 max: 10, 15 onnotice: () => {}, 16}) 17 18export const db = drizzle(postgresClient, { schema })
Connection pool được configure với max 10 connections, idle timeout 20s, và connect timeout 30s.
Sơ đồ kiến trúc hệ thống
正在加载图表渲染器...
Giải thích sơ đồ:
- Client Layer: Browser chạy Next.js App với Socket.IO client cho real-time updates
- UI Layer: Components sử dụng Hooks để tương tác với Zustand Stores
- Core Systems: Block Registry, Trigger System, Connector Registry, và Provider Registry là trung tâm của hệ thống
- Execution Layer: DAG Executor điều phối việc thực thi blocks, tools, và serialization
- Infrastructure: PostgreSQL database, Logger, CLI, và Python SDK hỗ trợ toàn bộ hệ thống
(.claude/rules/sim-architecture.md:14-28, apps/sim/blocks/registry.ts:207-250, apps/sim/triggers/types.ts:7-35)
Luồng dữ liệu và chuỗi gọi
正在加载图表渲染器...
Giải thích luồng dữ liệu:
- Trigger Phase: User khởi tạo workflow từ browser, state được update trong Zustand stores
- Execution Phase: DAG Executor lấy block config từ Block Registry, provider config từ Provider Registry nếu cần
- Processing Phase: Mỗi block được thực thi theo dependency order, với parallel execution khi có thể
- Persistence Phase: Kết quả được lưu vào PostgreSQL database
- Response Phase: Browser nhận notification và hiển thị kết quả cho user
(apps/sim/executor/index.ts:1-6, apps/sim/providers/registry.ts:39-48, apps/sim/stores/index.ts:24-55)
Sơ đồ quan hệ phụ thuộc module
正在加载图表渲染器...
Giải thích quan hệ phụ thuộc:
- Frontend → State: Components và hooks phụ thuộc vào Zustand stores
- State → Data: Stores tương tác với database thông qua Drizzle ORM
- Execution → Registry: Executor sử dụng tất cả registry modules để lấy configs
- Registry Cross-dependencies: Blocks sử dụng connectors và providers
- External Integration: Socket.IO, Python SDK, và CLI tích hợp với app layer
(.claude/rules/sim-architecture.md:14-28, apps/sim/blocks/registry.ts:207-250, apps/sim/connectors/registry.ts:33-64)
Quyết định thiết kế cốt lõi và đánh đổi
1. Chọn DAG Executor Pattern
Lý do: DAG (Directed Acyclic Graph) pattern cho phép thực thi workflow với:
- Parallel execution của các block không phụ thuộc lẫn nhau
- Clear dependency resolution
- Easier debugging và visualization
Đánh đổi: Complexity cao hơn so với simple sequential execution, nhưng performance và flexibility được cải thiện đáng kể.
(apps/sim/executor/index.ts:1-6)
2. Registry Pattern cho Blocks, Triggers, Connectors, Providers
Lý do: Centralized registry cho phép:
- Easy discovery và lookup của components
- Consistent API cho tất cả integrations
- Hot-pluggable architecture (có thể thêm blocks mới mà không thay đổi core)
Đánh đổi: Tăng initial boilerplate, nhưng giảm coupling và improve maintainability.
(apps/sim/blocks/registry.ts:207-270, apps/sim/connectors/registry.ts:33-64)
3. Zustand cho State Management
Lý do: Zustand cung cấp:
- Minimal boilerplate compared to Redux
- Built-in persistence và middleware support
- Excellent TypeScript support
- Predictable state updates
Đánh đổi: Ít structured hơn Redux, nhưng phù hợp với philosophy "Composition Over Complexity".
(.claude/rules/sim-architecture.md:9-13)
4. Drizzle ORM thay vì Prisma
Lý do: Drizzle ORM cung cấp:
- SQL-like syntax, closer to database
- Smaller bundle size
- Better performance
- TypeScript-first approach
Đánh đổi: Ít features hơn Prisma (như migrations GUI), nhưng performance và simplicity được ưu tiên.
5. Namespace SubBlock IDs để tránh Conflicts
Lý do: Khi multiple triggers được merge vào một block, subBlock IDs có thể conflict. Namespace pattern đảm bảo uniqueness.
Đánh đổi: Tăng complexity trong subBlock management, nhưng prevent runtime errors.
(apps/sim/triggers/index.ts:39-63)
6. Versioned Blocks (v2, v3)
Lý do: Cho phép backward compatibility khi upgrade blocks, users có thể chọn version phù hợp.
Đánh đổi: Tăng số lượng blocks trong registry, nhưng improve upgrade path.
(apps/sim/blocks/registry.ts:442-460)
7. Hosted Key Injection cho Tools
Lý do: Giảm friction cho users bằng cách cung cấp managed API keys cho các services cần authentication.
Đánh đổi: Tăng operational complexity và cost, nhưng improve user experience.
(apps/sim/tools/index.ts:50-57)
Bảng chọn công nghệ
| Công nghệ | Mục đích | Lý do chọn | Phương án thay thế |
|---|---|---|---|
| Next.js App Router | Frontend framework | Server components, streaming, file-based routing | Remix, Nuxt |
| TypeScript | Type system | Type safety, better DX, catch errors at compile time | JavaScript với JSDoc |
| Zustand | State management | Minimal boilerplate, TypeScript support, persistence | Redux, Jotai, Recoil |
| Drizzle ORM | Database ORM | SQL-like, lightweight, TypeScript-first | Prisma, TypeORM |
| PostgreSQL | Database | ACID compliance, JSON support, extensions | MySQL, MongoDB |
| Socket.IO | Real-time communication | WebSocket abstraction, fallbacks, rooms | raw WebSockets, Pusher |
| Mermaid | Diagrams | Text-based, GitHub support, multiple diagram types | PlantUML, Draw.io |
| Python SDK | External integration | Python popularity in AI/ML community | REST API only |
| CLI Package | Developer tooling | Local development, deployment automation | Web UI only |
| Monorepo (Turborepo) | Project structure | Shared packages, unified tooling | Polyrepo |
(packages/db/index.ts:1-21, packages/python-sdk/setup.py:1-51)
Cấu hình quan trọng và quy trình khởi động
Biến môi trường bắt buộc
| Biến | Mô tả | Ví dụ |
|---|---|---|
DATABASE_URL | PostgreSQL connection string | postgresql://user:pass@host:5432/db |
NEXT_PUBLIC_API_URL | API endpoint cho client | https://api.sim.ai |
Quy trình khởi động Application
- Database Connection: PostgreSQL client được khởi tạo với connection pool
- Provider Initialization:
initializeProviders()được gọi để khởi tạo tất cả LLM providers - Application Initialization:
initializeApplication()load environment variables từ DB - State Hydration: Zustand stores được hydrate với data từ database
- Socket Connection: Socket.IO client kết nối đến server cho real-time updates
(apps/sim/stores/index.ts:24-55, apps/sim/providers/registry.ts:50-63)
Database Connection Pool Configuration
typescript1const postgresClient = postgres(connectionString, { 2 prepare: false, // Disable prepared statements cho compatibility 3 idle_timeout: 20, // Close idle connections sau 20s 4 connect_timeout: 30, // Timeout connection sau 30s 5 max: 10, // Maximum 10 connections trong pool 6 onnotice: () => {}, // Suppress NOTICE messages 7})
Error Handling Strategy
Hệ thống sử dụng multi-layer error handling:
- Provider Level: Try-catch trong
initializeProviders()với logging - Application Level: Try-catch trong
initializeApplication()với graceful degradation - Database Level: Connection validation tại startup với throw error nếu
DATABASE_URLmissing
(apps/sim/providers/registry.ts:54-60, apps/sim/stores/index.ts:46-51, packages/db/index.ts:9-11)
