--- description: Main guide for creating syncable entities in Twenty's workspace migration system globs: ["**/metadata-modules/**", "**/workspace-migration/**"] alwaysApply: false --- # Creating a New Syncable Entity - Main Guide This is the main guide for creating **syncable entities** in Twenty's workspace migration architecture. ## Documentation Structure This main guide provides a high-level overview and navigation hub. **⚡ Skills** (`.cursor/skills/syncable-entity-*/SKILL.md`) - Concise, action-oriented implementation guides for each step. Reference these when creating a new syncable entity. **When to use:** - Start here for architecture overview and workflow - Reference specific skills (`@syncable-entity-types-and-constants`) when implementing each step ## What is a Syncable Entity? A syncable entity is a metadata entity that: - Has a **`universalIdentifier`**: A unique identifier used for syncing entities across workspaces/applications - Has an **`applicationId`**: Links the entity to an application (Twenty Standard or Custom applications) - Participates in the **workspace migration system**: Can be created, updated, and deleted through the migration pipeline - Is **cached as a flat entity**: Denormalized representation for efficient validation and change detection Examples: `skill`, `agent`, `view`, `viewField`, `role`, `pageLayout`, etc. ## Architecture Overview ``` Input DTO → Transform → Universal Flat Entity → Builder/Validator → Runner → Database ↓ Cache Service ``` **Key Components:** - **TypeORM Entity**: Database model extending `SyncableEntity` - **Flat Entity**: Denormalized type (no relations, dates as strings) - for caching - **Universal Flat Entity**: Flat entity with foreign keys mapped to universal identifiers - for migrations - **Transform Utils**: Convert DTOs to universal flat entities - **Builder/Validator**: Validate and create migration actions - **Runner**: Execute actions against the database ## Implementation Steps Follow these skills in order: ### 1️⃣ **Foundation: Types & Constants** → `@syncable-entity-types-and-constants` **What:** Define all types, entities, and register in central constants **Tasks:** - Create TypeORM entity (extends `SyncableEntity`) - Define flat entity types - Define action types (universal + flat) - Register in 5 central constants **Why first:** Everything else depends on these types --- ### 2️⃣ **Data Layer: Cache & Transform** → `@syncable-entity-cache-and-transform` **What:** Handle conversion between different representations **Tasks:** - Create cache service - Create entity-to-flat conversion - Create input transform utils - Handle foreign key resolution **Dependencies:** Requires Step 1 --- ### 3️⃣ **Business Logic: Builder & Validation** → `@syncable-entity-builder-and-validation` **What:** Validate business rules and create actions **Tasks:** - Create validator service (never throws, never mutates) - Create builder service - Wire into orchestrator (⚠️ critical!) **Dependencies:** Requires Steps 1-2 --- ### 4️⃣ **Execution: Runner & Actions** → `@syncable-entity-runner-and-actions` **What:** Execute migration actions against the database **Tasks:** - Create action handlers (create/update/delete) - Implement transpilation methods - Create universal-to-flat conversion utilities **Dependencies:** Requires Steps 1-3 --- ### 5️⃣ **Assembly: Integration** → `@syncable-entity-integration` **What:** Wire everything together **Tasks:** - Register in 3 NestJS modules - Create service and resolver layers - Use exception interceptor **Dependencies:** Requires Steps 1-4 --- ### 6️⃣ **Testing: Integration Tests** (**MANDATORY**) → `@syncable-entity-testing` **What:** Comprehensive test suite **Tasks:** - Create test utilities - Write failing tests (all validator exceptions) - Write successful tests (all CRUD operations) - Use snapshot testing **Dependencies:** Requires all previous steps --- ## Quick Reference ### Multi-Agent Workflow For parallel development: 1. **Agent 1** (Foundation): Complete Step 1 first - unblocks everyone 2. **Agent 2** (Cache): Can start immediately after Step 1 3. **Agent 3** (Builder): Can work in parallel with Agent 4 after Step 1 4. **Agent 4** (Runner): Can work in parallel with Agent 3 after Step 1 5. **Agent 5** (Integration): Assembles everything after Steps 2-4 ### Key Design Principles | Layer | Responsibility | Can Throw? | Can Mutate? | |-------|---------------|------------|-------------| | Transform Utils | Data transformation | Yes (input validation) | N/A (creates new) | | Validator | Business rule validation | **No** (returns errors) | **No** | | Builder | Action creation | **No** (returns errors) | **No** | | Runner | Database operations | Yes (DB errors) | Yes (via TypeORM) | ### Common Pitfalls ⚠️ **Most Commonly Forgotten:** 1. Wiring builder in orchestrator service 2. Registering in all 3 modules (builder, validators, action handlers) 3. Setting `universalIdentifier` correctly in entity-to-flat conversion ⚠️ **Common Mistakes:** 1. Using regular IDs instead of universal identifiers in transform utils 2. Throwing exceptions in validators/builders 3. Mutating entity maps in validators/builders 4. Forgetting to handle JSONB properties with `SerializedRelation` ### File Locations ``` packages/twenty-shared/src/metadata/ └── all-metadata-name.constant.ts packages/twenty-server/src/engine/metadata-modules/ ├── my-entity/ # Step 1 │ └── entities/ ├── flat-my-entity/ # Steps 1-2 │ ├── types/ │ ├── constants/ │ ├── services/ │ └── utils/ └── flat-entity/constant/ # Step 1 (central registries) ├── all-entity-properties-configuration-by-metadata-name.constant.ts ├── all-one-to-many-metadata-relations.constant.ts ├── all-many-to-one-metadata-foreign-key.constant.ts └── all-many-to-one-metadata-relations.constant.ts packages/twenty-server/src/engine/workspace-manager/workspace-migration/ ├── workspace-migration-builder/ # Step 3 │ ├── builders/my-entity/ │ └── validators/services/ └── workspace-migration-runner/ # Step 4 └── action-handlers/my-entity/ ``` ### Complete Checklist Before considering complete: - [ ] All 6 guides completed - [ ] TypeORM entity extends `SyncableEntity` - [ ] All constants registered (5 central registries) - [ ] Cache service with correct decorator - [ ] Transform utils return universal flat entities - [ ] Validator never throws/mutates - [ ] Builder wired in orchestrator (⚠️ critical!) - [ ] All 3 action handlers implemented - [ ] All 3 modules updated - [ ] **Integration tests written (MANDATORY)** - [ ] **All failing scenarios covered** - [ ] **All successful use cases tested** --- ## Need Help? Reference the appropriate skill for step-by-step guidance: - `@syncable-entity-types-and-constants` - Types, entities, constants - `@syncable-entity-cache-and-transform` - Cache & transform - `@syncable-entity-builder-and-validation` - Builder & validation - `@syncable-entity-runner-and-actions` - Runner & actions - `@syncable-entity-integration` - Integration & wiring - `@syncable-entity-testing` - Testing patterns