leonardo-baiser/typescript-ddd icon
public
Published on 4/23/2025
leonardo-baiser/typescript-ddd

Rules

description: TypeScript coding practices and style guide for DDD-oriented applications

TypeScript Coding Practices and Style Guide for DDD-Oriented Applications

General Principles

  • Object-oriented programming is the primary paradigm.
  • Always prefer the simplest and clearest solution.
  • Keep the codebase clean, organized, and easily readable.
  • Consider design patterns when writing code.
  • Domain should be the core of the application — business logic is the heart and should remain independent of frameworks and technical details.
  • Use ubiquitous language: class, method, and variable names must reflect the business domain precisely.

Typing

  • Keep all type definitions separate from implementation code.
  • Categorize type definitions into:
    • DTOs: Data Transfer Objects between layers.
    • Entity: Represents core business objects with identity and behavior.
    • Value Object: Defined by its attributes, not by identity.
    • Aggregate Root: Entry point to a group of related entities, enforces invariants.
    • Domain Event: Describes something important that happened in the domain.
    • Domain Service: Encapsulates business logic that doesn't naturally belong to an entity.
    • Application Service: Coordinates use cases and business rules execution.
    • Repository: Interface for persisting and retrieving domain objects.
    • Router: Defines routes and connects them to controllers.
    • Controller: Handles input from external sources and maps it to application services.
    • Mapper: Converts between DTOs and domain objects.
    • Container: Registers dependencies (dependency injection).
    • Schema Validate: Validation schemas for user input (e.g., Zod).
    • Symbols: Centralized tokens for dependency injection.
    • Interfaces / Types: Contracts or utility types not classified above.
  • Use suffixes: .dto.ts, .entity.ts, .value-object.ts, .aggregate.ts, .domain-event.ts, .domain-service.ts, .application-service.ts, .repository.ts, .controller.ts, .mapper.ts, .container.ts, .router.ts, .schema-validate.ts, .symbols.ts, .interface.ts, .type.ts.

File Structure & Organization

  • One class per file.
  • Keep files small; refactor when exceeding 200-300 lines.
  • Use lowercase kebab-case for file and folder names.
  • Organize code into bounded contexts when applicable, each with its own layers.
  • Regularly remove unused files to keep the codebase lean.

Application Layering

  • Follow DDD Layered Architecture:
    • Domain Layer: Entities, value objects, domain services, domain events.
    • Application Layer: Use case coordination, application services, commands.
    • Infrastructure Layer: Implementations for repositories, external APIs, DB access.
    • Interface Layer: Controllers, routers, DTOs, adapters.
  • Avoid cross-layer dependencies and maintain separation of concerns.

Code Style & Best Practices

  • Use functional programming principles like immutability where appropriate.
  • Avoid forEach; prefer map, reduce, etc.
  • Favor early returns for readability and maintainability.
  • Centralize error handling; avoid duplicated try/catch blocks.
  • Domain code must be highly testable — write unit tests for entities and domain services.

Imports

  • Organize imports in this order:
    1. NodeJS built-in modules
    2. 3rd-party modules
    3. Internal application modules
  • Use path aliases by bounded context to avoid tight coupling across domains.
  • Remove unused imports to maintain cleanliness.

Linting & Formatting

  • Use ESLint for linting and formatting.
  • Follow rules strictly as defined in .eslintrc.ts and tsconfig.eslint.json.