twenty/.cursor/rules/creating-syncable-entity.mdc
martmull bf5cc68f25
Rename standard and custom apps (#19631)
as title
no migration for existing apps, changes only apply on new workspaces
2026-04-13 13:13:59 +00:00

219 lines
7.3 KiB
Text
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 (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