Bảng giá

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:

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:

  1. Single Responsibility: Mỗi component, hook, store có một mục đích rõ ràng
  2. Composition Over Complexity: Phân tách logic phức tạp thành các phần nhỏ hơn
  3. Type Safety First: TypeScript interfaces cho tất cả props, state, return types
  4. 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: use prefix (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:

typescript
1export 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:

CategoryMô tảVí dụ
blocksCác block xử lý logic workflowAgent, Condition, Router
toolsCác công cụ tích hợp external servicesGitHub, Slack, Gmail
triggersCác block khởi tạo workflowManual Trigger, Schedule, Webhook

Hàm getBlocksByCategory cho phép lọc blocks theo category:

typescript
1export 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:

  1. getBlock(type: string): Lấy block theo type, hỗ trợ normalize - thành _
  2. getLatestBlock(baseType: string): Lấy version mới nhất của block (ví dụ: google_sheets_v2)
  3. getBlockByToolName(toolName: string): Tìm block theo tool name
  4. getAllBlockTypes(): Lấy tất cả block types
  5. isValidBlockType(type: string): Kiểm tra type có hợp lệ không
typescript
1export 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:

typescript
1export 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:

typescript
1export 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:

typescript
1export 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:

typescript
1export 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:

typescript
1export 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&lt;void&gt; {
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:

typescript
1/**
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:

typescript
1async 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:

typescript
1async function initializeApplication(): Promise&lt;void&gt; {
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:

typescript
1export 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 handleBeforeUnloadcleanupApplication:

typescript
1function 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:

typescript
1import { 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.

(packages/db/index.ts:1-21)

Sơ đồ kiến trúc hệ thống

正在加载图表渲染器...

Giải thích sơ đồ:

  1. Client Layer: Browser chạy Next.js App với Socket.IO client cho real-time updates
  2. UI Layer: Components sử dụng Hooks để tương tác với Zustand Stores
  3. Core Systems: Block Registry, Trigger System, Connector Registry, và Provider Registry là trung tâm của hệ thống
  4. Execution Layer: DAG Executor điều phối việc thực thi blocks, tools, và serialization
  5. 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:

  1. Trigger Phase: User khởi tạo workflow từ browser, state được update trong Zustand stores
  2. Execution Phase: DAG Executor lấy block config từ Block Registry, provider config từ Provider Registry nếu cần
  3. Processing Phase: Mỗi block được thực thi theo dependency order, với parallel execution khi có thể
  4. Persistence Phase: Kết quả được lưu vào PostgreSQL database
  5. 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:

  1. Frontend → State: Components và hooks phụ thuộc vào Zustand stores
  2. State → Data: Stores tương tác với database thông qua Drizzle ORM
  3. Execution → Registry: Executor sử dụng tất cả registry modules để lấy configs
  4. Registry Cross-dependencies: Blocks sử dụng connectors và providers
  5. 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.

(packages/db/index.ts:1-21)

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 đíchLý do chọnPhương án thay thế
Next.js App RouterFrontend frameworkServer components, streaming, file-based routingRemix, Nuxt
TypeScriptType systemType safety, better DX, catch errors at compile timeJavaScript với JSDoc
ZustandState managementMinimal boilerplate, TypeScript support, persistenceRedux, Jotai, Recoil
Drizzle ORMDatabase ORMSQL-like, lightweight, TypeScript-firstPrisma, TypeORM
PostgreSQLDatabaseACID compliance, JSON support, extensionsMySQL, MongoDB
Socket.IOReal-time communicationWebSocket abstraction, fallbacks, roomsraw WebSockets, Pusher
MermaidDiagramsText-based, GitHub support, multiple diagram typesPlantUML, Draw.io
Python SDKExternal integrationPython popularity in AI/ML communityREST API only
CLI PackageDeveloper toolingLocal development, deployment automationWeb UI only
Monorepo (Turborepo)Project structureShared packages, unified toolingPolyrepo

(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ếnMô tảVí dụ
DATABASE_URLPostgreSQL connection stringpostgresql://user:pass@host:5432/db
NEXT_PUBLIC_API_URLAPI endpoint cho clienthttps://api.sim.ai

(packages/db/index.ts:8-11)

Quy trình khởi động Application

  1. Database Connection: PostgreSQL client được khởi tạo với connection pool
  2. Provider Initialization: initializeProviders() được gọi để khởi tạo tất cả LLM providers
  3. Application Initialization: initializeApplication() load environment variables từ DB
  4. State Hydration: Zustand stores được hydrate với data từ database
  5. 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

typescript
1const 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})

(packages/db/index.ts:13-19)

Error Handling Strategy

Hệ thống sử dụng multi-layer error handling:

  1. Provider Level: Try-catch trong initializeProviders() với logging
  2. Application Level: Try-catch trong initializeApplication() với graceful degradation
  3. Database Level: Connection validation tại startup với throw error nếu DATABASE_URL missing

(apps/sim/providers/registry.ts:54-60, apps/sim/stores/index.ts:46-51, packages/db/index.ts:9-11)