Files
cal-diy-oidc/agents/rules/data-dto-boundaries.md
Benny Joo ab21c7f805 refactor: Cal.diy (#28903)
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com

This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main:

- Rebrand Cal.com to Cal.diy across the entire codebase
- Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions
- Switch license from AGPL-3.0 to MIT
- Remove docs/ directory (migrated to Nextra at cal.diy)
- Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc.
- Clean up .env.example for self-hosted Cal.diy
- Update Docker image references to calcom/cal.diy
- Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork
- Add PR welcome bot for Cal.diy contributors
- Fix API v2 breaking changes oasdiff ignore entries
- Replace Blacksmith CI runners with default GitHub Actions

3893 files changed, 20789 insertions(+), 411020 deletions(-)

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* rip out org related comments in api v2

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-04-15 09:52:36 -03:00

3.4 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Use DTOs at Every Architectural Boundary CRITICAL Prevents technology coupling and security risks data, dto, boundaries, security, types

Use DTOs at Every Architectural Boundary

Impact: CRITICAL

Database types should not leak to the frontend. This has become a popular shortcut in our tech stack, but it's a code smell that creates multiple problems.

Problems with leaking database types:

  • Technology coupling (Prisma types end up in React components)
  • Security risks (accidental leakage of sensitive fields)
  • Fragile contracts between server and client
  • Inability to evolve the database schema independently

Incorrect (database types leaking):

// API route returning Prisma types directly
import type { User } from "@prisma/client";

export async function GET(): Promise<User> {
  const user = await prisma.user.findFirst();
  return user; // Leaks all database fields including sensitive ones
}

// Frontend using Prisma types
import type { User } from "@prisma/client";

function UserProfile({ user }: { user: User }) {
  // Component now coupled to database schema
}

Correct (explicit DTOs):

// Define explicit DTOs
interface UserDTO {
  id: number;
  name: string;
  email: string;
  // Only fields needed by the client
}

// API route transforms to DTO
export async function GET(): Promise<UserDTO> {
  const user = await userRepository.findById(id);
  return UserResponseSchema.parse(user); // Validate with Zod
}

// Frontend uses DTO
function UserProfile({ user }: { user: UserDTO }) {
  // Component decoupled from database
}

The standard:

  1. Data layer → Application layer → API: Transform database models into application-layer DTOs, then transform application DTOs into API-specific DTOs
  2. API → Application layer → Data layer: Transform API DTOs through application layer and into data-specific DTOs
  3. All DTO conversions through Zod to ensure all data is validated before sending to user

DTO Location and Naming

Location: All DTOs go in packages/lib/dto/

Naming conventions:

  • Base entity: {Entity}Dto (e.g., BookingDto)
  • With relations: {Entity}With{Relations}Dto (e.g., BookingWithAttendeesDto)
  • For specific projections: {Entity}For{Purpose}Dto (e.g., BookingForConfirmationDto)
  • Avoid: {Entity}Dto2, {Entity}DtoForHandler, or other use-case-specific names

Enum/union pattern - use string literal unions to stay ORM-agnostic:

// Good - ORM-agnostic string literal union
export type BookingStatusDto = "CANCELLED" | "ACCEPTED" | "REJECTED" | "PENDING";

// Bad - importing Prisma enum
import { BookingStatus } from "@calcom/prisma/client";

Type safety - never use as any in DTO mapping functions. If types don't align, fix the mapping explicitly.

Prisma Boundaries

  • Allowed: packages/prisma, repository implementations (packages/features/**/repositories/*Repository.ts), and low-level data access infrastructure.
  • Not allowed: packages/features/** business logic (non-repository), packages/trpc/** handlers, apps/web/**, apps/api/v2/** services/controllers, and workflow/webhook/service layers.

Yes, this requires more code. Yes, it's worth it. Explicit boundaries prevent the architectural erosion that creates long-term maintenance nightmares.

Reference: Cal.diy Engineering Blog