Twenty CRM Architectural Explanations: A Deep Dive into the Monorepo Structure and Feature Design
Twenty CRM provides comprehensive architectural explanations in its open-source repository, documenting a modular NestJS backend, React frontend with Jotai state management, and Nx monorepo organization that powers features like Workflows, Contacts, and Messaging.
Twenty CRM is an open-source customer relationship management platform built with modern architectural patterns that prioritize maintainability and extensibility. The project maintains detailed architectural explanations within its CLAUDE.md file and throughout the source code, describing how the monorepo structure separates concerns between frontend, backend, and shared utilities. Understanding these Twenty CRM architectural explanations helps developers extend core features such as Workflow automation, Messaging, and dynamic metadata management.
Monorepo Architecture Overview
Twenty CRM organizes its codebase as an Nx monorepo workspace that cleanly separates applications and libraries into distinct packages. This structure enables shared configuration, consistent linting, and fast builds across the entire stack.
The package layout follows this structure:
packages/
├── twenty-front/ # React frontend application
├── twenty-server/ # NestJS backend API
├── twenty-ui/ # Shared UI components library
├── twenty-shared/ # Common types and utilities
├── twenty-emails/ # Email templates with React Email
├── twenty-website/ # Next.js documentation website
├── twenty-zapier/ # Zapier integration
└── twenty-e2e-testing/ # Playwright E2E tests
Source: CLAUDE.md – Package Structure
Technology Stack
According to the Twenty CRM architecture documentation, the tech stack includes:
- Frontend – React 18, TypeScript, Jotai, Linaria, Vite
- Backend – NestJS, TypeORM, PostgreSQL, Redis, GraphQL-Yoga
- Monorepo – Nx + Yarn 4
Source: CLAUDE.md – Tech Stack
Backend Architecture: NestJS Modular Design
The backend follows NestJS modular design principles, with each domain (Contacts, Messaging, Workflow) encapsulated in its own module under packages/twenty-server/src/modules/. Modules encapsulate providers, services, and controllers, then compose into higher-level modules via imports.
Dynamic Metadata System
Twenty CRM supports customizable object schemas through the metadata modules located at packages/twenty-server/src/engine/metadata-modules/. This engine handles dynamic object definitions, view configurations, and schema migrations, allowing users to create custom objects without database changes.
Workflow Feature Architecture
The Workflow feature demonstrates this modularity clearly. Located at packages/twenty-server/src/modules/workflow/, the implementation separates concerns across sub-modules:
src/modules/workflow/
│ workflow.module.ts ← top-level module
│ workflow-trigger/
│ workflow-status/
│ workflow-builder/
│ workflow-executor/
│ workflow-runner/
The top-level workflow.module.ts only wires the essential sub-modules:
// packages/twenty-server/src/modules/workflow/workflow.module.ts
import { Module } from '@nestjs/common';
import { WorkflowStatusModule } from 'src/modules/workflow/workflow-status/workflow-status.module';
import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module';
@Module({
imports: [WorkflowTriggerModule, WorkflowStatusModule],
})
export class WorkflowModule {}
Source: workflow.module.ts
Workflow Execution Flow
The architecture separates three distinct responsibilities:
- Trigger –
WorkflowTriggerWorkspaceServicevalidates versions and creates workflow runs - Runner –
WorkflowRunnerWorkspaceServiceexecutes runs through the executor - Status –
WorkflowStatusModulemanages state transitions and event emission
Key implementations reside in:
packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.tspackages/twenty-server/src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service.ts
This separation ensures triggers decide when to start, runners decide how to execute, and status modules decide what to record.
Frontend Architecture: State and Styling
The Twenty CRM frontend architecture emphasizes zero-runtime overhead and predictable state flow.
Global State with Jotai
Global state uses Jotai atoms instead of React Context or Redux. Primitive atoms hold core data (current user, selected objects), while derived atoms compose UI state without useEffect chains:
// packages/twenty-front/src/state/user.atom.ts
import { atom } from 'jotai';
export const currentUserAtom = atom<User | null>(null);
Zero-Runtime Styling with Linaria
Linaria provides CSS-in-JS that compiles away at build time, eliminating runtime style calculation overhead. Styles are written next to components but extracted to static CSS files.
Component Discipline
All components follow strict rules defined in CLAUDE.md:
- Functional components only – No class components
- Named exports only – No default exports
- Strong TypeScript typing – No
anytypes allowed
Source: CLAUDE.md – Key Development Principles
Development Workflow and Tooling
Twenty CRM architectural explanations include standardized commands for consistency across the monorepo:
| Step | Command | Purpose |
|---|---|---|
| Setup | bash packages/twenty-utils/setup-dev-env.sh |
Starts Postgres + Redis, creates DB, copies .env |
| Lint | npx nx lint:diff-with-main twenty-server |
Fast lint against main branch |
| Type-check | npx nx typecheck twenty-server |
Guarantees strict TS compliance |
| Build | npx nx build twenty-server |
Compiles NestJS app |
| GraphQL generation | npx nx run twenty-front:graphql:generate |
Sync GraphQL types from back-end schema |
| DB migrations | npx nx run twenty-server:typeorm migration:generate … |
Generates migration files for entity changes |
Source: CLAUDE.md – Development Workflow
Practical Code Examples
Importing Backend Modules
To extend the application, import modules into the root AppModule:
// packages/twenty-server/src/app.module.ts
import { Module } from '@nestjs/common';
import { WorkflowModule } from 'src/modules/workflow/workflow.module';
@Module({
imports: [WorkflowModule, /* other modules */],
})
export class AppModule {}
Source: app.module.ts
Triggering Workflows Programmatically
The WorkflowTriggerWorkspaceService provides methods to initiate workflow versions:
// packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts
async triggerWorkflowVersion({
workflowVersionId,
workspaceId,
}: { workflowVersionId: string; workspaceId: string }) {
const version = await this.workflowCommonWorkspaceService.getValidWorkflowVersionOrFail(
await this.workflowVersionRepository.findOne({ where: { id: workflowVersionId } })
);
// ... schedule execution, create run, etc.
}
Source: workflow-trigger.workspace-service.ts
Scaffolding New Packages
To add a new package to the monorepo:
npx nx generate @nrwl/react:application my-plugin --directory=packages
This automatically integrates with the Nx project graph and inherits shared ESLint and TypeScript configurations.
Summary
- Twenty CRM architectural explanations are centralized in
CLAUDE.mdand distributed throughout the source code, documenting a strictly modular design. - The Nx monorepo separates frontend (
twenty-front), backend (twenty-server), and shared utilities (twenty-shared) into discrete packages with independent build pipelines. - Backend features use NestJS modules to enforce separation of concerns, as demonstrated by the Workflow feature's division into Trigger, Runner, and Status sub-modules.
- Frontend architecture relies on Jotai for atomic state management and Linaria for zero-runtime CSS, following strict TypeScript and React functional component standards.
- Development tooling uses Nx commands for linting, type-checking, GraphQL generation, and database migrations, ensuring consistency across the codebase.
Frequently Asked Questions
Where can I find Twenty CRM architectural documentation?
Twenty CRM maintains comprehensive architectural explanations in the CLAUDE.md file at the repository root, which covers the monorepo structure, tech stack, development workflow, and coding conventions. Additional architectural details are embedded as comments in key files like packages/twenty-server/src/app.module.ts and within module-specific directories under packages/twenty-server/src/modules/.
How does Twenty CRM organize its backend code?
The backend follows NestJS modular architecture, where each feature (Workflows, Contacts, Messaging) resides in its own directory under packages/twenty-server/src/modules/. Each module encapsulates its controllers, services, and providers, then imports into higher-level modules. For example, the Workflow module composes WorkflowTriggerModule and WorkflowStatusModule without containing business logic itself.
What state management does Twenty CRM use for the frontend?
Twenty CRM uses Jotai for global state management, implementing atomic state patterns where primitive atoms store core data (like current user) and derived atoms compute UI state. This approach eliminates prop drilling and reduces re-renders compared to React Context, while maintaining better TypeScript inference than Redux.
How can I add a new feature to Twenty CRM?
To add a feature, create a new NestJS module under packages/twenty-server/src/modules/ with its own services and controllers, then import it into packages/twenty-server/src/app.module.ts. For frontend components, add Jotai atoms in packages/twenty-front/src/state/ and UI components in packages/twenty-front/src/modules/. Use npx nx commands to generate types, run migrations, and ensure the feature integrates with the existing Twenty CRM architecture.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →