CLI DDD Scaffolding - sebamar88/bytekit GitHub Wiki

πŸ—οΈ CLI DDD Scaffolding

πŸ‡ΊπŸ‡Έ English: The --ddd mode scaffolds a full hexagonal (ports & adapters) architecture from a single command. It generates the folder structure, port interfaces, entities, repository contracts, use cases, and HTTP adapters β€” ready for clean DDD development. πŸ‡ͺπŸ‡Έ EspaΓ±ol: El modo --ddd genera una arquitectura hexagonal (puertos y adaptadores) completa desde un solo comando. Crea la estructura de carpetas, interfaces de puertos, entidades, contratos de repositorio, casos de uso y adaptadores HTTP β€” lista para desarrollo DDD limpio.


Fuente / Source

src/cli/ddd-boilerplate.ts


API ProgramΓ‘tica / Programmatic API

EN: You can also invoke the generator programmatically from TypeScript:

ES: TambiΓ©n puedes invocar el generador de forma programΓ‘tica desde TypeScript:

import { generateDddBoilerplate } from "bytekit/cli/ddd-boilerplate";

const result = await generateDddBoilerplate({
    domain: "Order Management",
    port: "OrderRepository",
    actions: ["create", "findById", "update", "delete"],
});

console.log(result.rootDir); // Absolute path to generated root
console.log(result.slug);    // "order-management"

Firma / Signature

export function generateDddBoilerplate(
    options: DddBoilerplateOptions
): Promise<{ rootDir: string; slug: string }>;

DddBoilerplateOptions

Property Type Required Default Description (EN) DescripciΓ³n (ES)
domain string βœ… β€” Bounded context name (readable name or slug) Nombre del contexto acotado (nombre legible o slug)
port string βœ… β€” Secondary (driven) port name, e.g. "OrderRepository" Nombre del puerto secundario (driven), ej. "OrderRepository"
outDir string ❌ ./<domain-slug> Output base directory Directorio base de salida
actions string[] ❌ undefined Domain actions to scaffold, e.g. ["create", "findById"] Acciones de dominio a generar, ej. ["create", "findById"]

Helper Functions / Funciones Auxiliares

EN: Two utility functions are exported for slug and name conversions:

ES: Se exportan dos funciones auxiliares para conversiΓ³n de slugs y nombres:

slugifyDomain(domain: string): string

Converts a domain name to kebab-case for use in paths and filenames. Convierte un nombre de dominio a kebab-case para rutas y archivos.

import { slugifyDomain } from "bytekit/cli/ddd-boilerplate";

slugifyDomain("Order Management");   // β†’ "order-management"
slugifyDomain("userProfile");        // β†’ "user-profile"
slugifyDomain("  My Cool API  ");    // β†’ "my-cool-api"
slugifyDomain("");                   // β†’ "domain" (fallback)

pascalFromKebabSlug(slug: string): string

Converts a kebab-case slug to PascalCase for class and interface names. Convierte un slug kebab-case a PascalCase para nombres de clases e interfaces.

import { pascalFromKebabSlug } from "bytekit/cli/ddd-boilerplate";

pascalFromKebabSlug("order-management"); // β†’ "OrderManagement"
pascalFromKebabSlug("user-profile");     // β†’ "UserProfile"

Uso desde la terminal / Terminal Usage

bytekit --ddd --domain=<name> --port=<name> [--out=<dir>] [--actions=<list>]

Opciones / Options

Option Description (EN) DescripciΓ³n (ES)
--ddd Activate DDD scaffolding mode Activar modo de scaffolding DDD
--domain=<name> Bounded context name Nombre del contexto acotado
--port=<name> Outbound port interface name Nombre de la interfaz del puerto outbound
--out=<dir> Output directory (default: ./<domain-slug>/) Directorio de salida (por defecto: ./<domain-slug>/)
--actions=<list> Comma-separated domain actions Acciones de dominio separadas por coma

Estructura generada / Generated Folder Structure

EN: The generator always creates the following hexagonal architecture tree. Every leaf folder gets a .gitkeep file (except port directories, which get interface stubs).

ES: El generador siempre crea la siguiente estructura hexagonal. Cada carpeta hoja recibe un archivo .gitkeep (excepto los directorios de puertos, que reciben stubs de interfaces).

<domain-slug>/
β”œβ”€β”€ domain/                          ← 🧠 Core domain logic
β”‚   β”œβ”€β”€ entities/                    ← Aggregate roots & entities
β”‚   β”œβ”€β”€ value-objects/               ← Immutable value types
β”‚   β”œβ”€β”€ aggregates/                  ← Aggregate boundaries
β”‚   β”œβ”€β”€ events/                      ← Domain events
β”‚   β”œβ”€β”€ repositories/                ← Repository contracts (interfaces)
β”‚   └── services/                    ← Domain services
β”œβ”€β”€ application/                     ← 🎯 Use cases & orchestration
β”‚   β”œβ”€β”€ use-cases/                   ← One class per action
β”‚   β”œβ”€β”€ dto/                         ← Data transfer objects
β”‚   └── ports/
β”‚       β”œβ”€β”€ inbound/                 ← Primary port .ts (driving)
β”‚       └── outbound/                ← Secondary port .ts (driven)
β”œβ”€β”€ infrastructure/                  ← πŸ”Œ External adapters
β”‚   β”œβ”€β”€ persistence/                 ← DB / HTTP repository adapters
β”‚   └── config/                      ← Infrastructure config
└── presentation/                    ← 🌐 Delivery mechanism
    └── http/
        β”œβ”€β”€ routes/                  ← Route definitions
        └── controllers/             ← HTTP controllers

Modo bΓ‘sico (sin --actions) / Basic Mode (without --actions)

EN: Without --actions, the generator creates only the folder structure, .gitkeep placeholder files, and port interface stubs.

ES: Sin --actions, el generador crea solo la estructura de carpetas, archivos .gitkeep de marcador de posiciΓ³n, y stubs de interfaces de puertos.

Ejemplo / Example

bytekit --ddd --domain="Payment Gateway" --port="PaymentProcessor"

EN: This creates:

ES: Esto crea:

payment-gateway/
β”œβ”€β”€ domain/                         # Reglas de negocio e invariantes (Capa Central)
β”‚   β”œβ”€β”€ entities/                   # Objetos con identidad (ej: Payment.ts)
β”‚   β”œβ”€β”€ value-objects/              # Objetos sin identidad (ej: Money, Currency)
β”‚   β”œβ”€β”€ aggregates/                 # Conjunto de entidades tratadas como unidad
β”‚   β”œβ”€β”€ events/                     # Notificaciones de cambios (ej: PaymentApproved)
β”‚   β”œβ”€β”€ repositories/               # Interfaces (Contratos) para persistencia
β”‚   └── services/                   # LΓ³gica que no pertenece a una sola entidad
β”œβ”€β”€ application/                    # Casos de uso y orquestaciΓ³n
β”‚   β”œβ”€β”€ use-cases/                  # LΓ³gica de aplicaciΓ³n (ej: ProcessPayment.ts)
β”‚   β”œβ”€β”€ dto/                        # Objetos de transferencia de datos
β”‚   └── ports/                      # DefiniciΓ³n de interfaces de comunicaciΓ³n
β”‚       β”œβ”€β”€ inbound/                # Lo que la app expone (Primary Ports)
β”‚       └── outbound/               # Lo que la app necesita (Secondary Ports)
β”œβ”€β”€ infrastructure/                 # Implementaciones tΓ©cnicas y herramientas
β”‚   β”œβ”€β”€ persistence/                # Repositorios concretos (TypeORM, Prisma, etc.)
β”‚   └── config/                     # Variables de entorno y constantes
└── presentation/                   # Interfaz de entrada (Drivers)
    └── http/
        β”œβ”€β”€ routes/                 # DefiniciΓ³n de endpoints
        └── controllers/            # Manejo de requests y ejecuciΓ³n de Use Cases

Generated port stubs / Stubs de puertos generados

application/ports/inbound/payment-gateway-primary.port.ts

/**
 * Puerto primario (driving): contrato por el que el mundo exterior invoca la aplicaciΓ³n
 * (HTTP, CLI, jobs, mensajes, etc.). Los adaptadores de entrada lo implementan
 * o llaman a los casos de uso.
 *
 * Contexto: payment-gateway
 * Generado por bytekit --ddd
 */
export interface PaymentGatewayPrimaryPort {
    // Ej.: firmas que exponen comandos o consultas hacia el dominio / casos de uso
}

application/ports/outbound/payment-processor.port.ts

/**
 * Puerto secundario (driven): contrato que la aplicaciΓ³n define para hablar con el exterior
 * (persistencia, APIs de terceros, sistema de archivos, colas, etc.).
 * Las implementaciones viven en infrastructure/.
 *
 * Puerto: PaymentProcessor
 * Contexto: payment-gateway
 * Generado por bytekit --ddd
 */
export interface PaymentProcessor {
    // Ej.: guardar o recuperar agregados, publicar eventos, llamar a un servicio externo
}

Modo completo (--actions) / Full Mode (--actions)

EN: When --actions is provided, the generator additionally creates fully-wired DDD layer files:

ES: Cuando se proporciona --actions, el generador ademΓ‘s crea archivos DDD completamente conectados:

# Layer File Description (EN) DescripciΓ³n (ES)
1 Entity domain/entities/<slug>.entity.ts Class with id, createdAt, validation. Exports <Pascal>EntityProps interface. Clase con id, createdAt, validaciΓ³n. Exporta interfaz <Pascal>EntityProps.
2 Repository Interface domain/repositories/i-<slug>.repository.ts Interface with one method per action Interfaz con un mΓ©todo por acciΓ³n
3 Use Cases application/use-cases/<action>-<slug>.use-case.ts One file per action; each class delegates to the repository Un archivo por acciΓ³n; cada clase delega al repositorio
4 HTTP Adapter infrastructure/persistence/http-<slug>.repository.ts Implements repository interface using ApiClient from bytekit/api-client Implementa la interfaz del repositorio usando ApiClient de bytekit/api-client

Ejemplo completo / Full Example

bytekit --ddd \
  --domain="Order Management" \
  --port="OrderRepository" \
  --actions=create,findById,update,delete

EN: This generates all the base structure PLUS the following files:

ES: Esto genera toda la estructura base MÁS los siguientes archivos:

order-management/
β”œβ”€β”€ domain/
β”‚   β”œβ”€β”€ entities/
β”‚   β”‚   └── order-management.entity.ts          ← Entity class
β”‚   β”œβ”€β”€ repositories/
β”‚   β”‚   └── i-order-management.repository.ts    ← Repository interface
β”‚   └── ...
β”œβ”€β”€ application/
β”‚   β”œβ”€β”€ use-cases/
β”‚   β”‚   β”œβ”€β”€ create-order-management.use-case.ts
β”‚   β”‚   β”œβ”€β”€ find-by-id-order-management.use-case.ts
β”‚   β”‚   β”œβ”€β”€ update-order-management.use-case.ts
β”‚   β”‚   └── delete-order-management.use-case.ts
β”‚   └── ports/
β”‚       β”œβ”€β”€ inbound/order-management-primary.port.ts
β”‚       └── outbound/order-repository.port.ts
β”œβ”€β”€ infrastructure/
β”‚   └── persistence/
β”‚       └── http-order-management.repository.ts  ← HTTP adapter
└── presentation/
    └── ...

Inferencia de acciones / Action Inference

EN: The generator infers the HTTP method, parameters, and return types from the action name:

ES: El generador infiere el mΓ©todo HTTP, parΓ‘metros y tipos de retorno a partir del nombre de la acciΓ³n:

Action Pattern HTTP Verb Parameters Return Type Example Actions
create, add, save, insert POST entity: <Pascal>Entity <Pascal>Entity create, addOrder
update, replace PUT id: string, entity: <Pascal>Entity <Pascal>Entity update, replaceOrder
patch PATCH id: string, data: Partial<<Pascal>EntityProps> <Pascal>Entity patch
delete, remove, destroy DELETE id: string void delete, removeOrder
findAll, findMany, list, search GET (none) <Pascal>Entity[] findAll, listOrders
findById, get (default) GET id: string <Pascal>Entity | null findById, getOrder

CΓ³digo generado β€” Ejemplo con create / Generated Code β€” Example with create

EN: Here is the exact code generated for the create action with domain "Order Management":

ES: Este es el cΓ³digo exacto generado para la acciΓ³n create con dominio "Order Management":

1. Entity β€” domain/entities/order-management.entity.ts

/**
 * Aggregate root entity for the OrderManagement bounded context.
 * Contains core domain invariants β€” keep business rules here, not in use cases.
 *
 * @generated bytekit --ddd
 */
export class OrderManagementEntity {
    readonly id: string;
    readonly createdAt: Date;

    constructor(props: OrderManagementEntityProps) {
        if (!props.id?.trim()) {
            throw new Error("OrderManagementEntity: 'id' is required and cannot be empty.");
        }
        this.id = props.id.trim();
        this.createdAt = props.createdAt ?? new Date();
    }
}

/** Constructor properties for {@link OrderManagementEntity}. */
export interface OrderManagementEntityProps {
    id: string;
    createdAt?: Date;
}

2. Repository Interface β€” domain/repositories/i-order-management.repository.ts

import type { OrderManagementEntity } from "../entities/order-management.entity.js";

/**
 * Outbound port (secondary): persistence and data-access contract for OrderManagement.
 * Domain and application layers depend on this interface β€” never on its implementations.
 *
 * @generated bytekit --ddd
 */
export interface IOrderManagementRepository {
    create(entity: OrderManagementEntity): Promise<OrderManagementEntity>;
    findById(id: string): Promise<OrderManagementEntity | null>;
    update(id: string, entity: OrderManagementEntity): Promise<OrderManagementEntity>;
    delete(id: string): Promise<void>;
}

3. Use Case β€” application/use-cases/create-order-management.use-case.ts

import type { IOrderManagementRepository } from "../../domain/repositories/i-order-management.repository.js";
import type { OrderManagementEntity } from "../../domain/entities/order-management.entity.js";

/**
 * Use case: Create OrderManagement.
 * Orchestrates domain logic without containing it β€” delegates to the repository port.
 *
 * @generated bytekit --ddd
 */
export class CreateOrderManagementUseCase {
    constructor(private readonly repository: IOrderManagementRepository) {}

    async execute(entity: OrderManagementEntity): Promise<OrderManagementEntity> {
        return this.repository.create(entity);
    }
}

4. HTTP Adapter β€” infrastructure/persistence/http-order-management.repository.ts

import { ApiClient } from "bytekit/api-client";
import type { OrderManagementEntity } from "../../domain/entities/order-management.entity.js";
import type { IOrderManagementRepository } from "../../domain/repositories/i-order-management.repository.js";

/**
 * HTTP adapter: implements {@link IOrderManagementRepository} using {@link ApiClient} from bytekit.
 * Lives in the infrastructure layer β€” domain and application layers must not import this class.
 *
 * @generated bytekit --ddd
 */
export class HttpOrderManagementRepository implements IOrderManagementRepository {
    constructor(private readonly client: ApiClient) {}

    async create(entity: OrderManagementEntity): Promise<OrderManagementEntity> {
        return this.client.post<OrderManagementEntity>("/order-management", entity);
    }

    async findById(id: string): Promise<OrderManagementEntity | null> {
        return this.client.get<OrderManagementEntity | null>(`/order-management/${id}`);
    }

    async update(id: string, entity: OrderManagementEntity): Promise<OrderManagementEntity> {
        return this.client.put<OrderManagementEntity>(`/order-management/${id}`, entity);
    }

    async delete(id: string): Promise<void> {
        await this.client.delete<void>(`/order-management/${id}`);
    }
}

Diagrama de dependencias / Dependency Diagram

EN: The generated code follows the Dependency Rule β€” inner layers never depend on outer layers:

ES: El cΓ³digo generado sigue la Regla de Dependencia β€” las capas internas nunca dependen de las externas:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  presentation/http        (controllers, routes)          β”‚
β”‚    ↓ calls                                               β”‚
β”‚  application/use-cases    (CreateOrderUseCase, ...)      β”‚
β”‚    ↓ depends on interface                                β”‚
β”‚  domain/repositories      (IOrderRepository)             β”‚
β”‚    ↑ implements                                          β”‚
β”‚  infrastructure/persistence (HttpOrderRepository)        β”‚
β”‚    ↓ uses                                                β”‚
β”‚  bytekit/api-client       (ApiClient)                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Tips / Consejos

  • EN: You can combine any actions: --actions=create,findById,findAll,update,patch,delete

  • ES: Puedes combinar cualquier acciΓ³n: --actions=create,findById,findAll,update,patch,delete

  • EN: Custom action names also work β€” they default to GET with id parameter if the name doesn't match any known pattern.

  • ES: Los nombres de acciΓ³n personalizados tambiΓ©n funcionan β€” por defecto usan GET con parΓ‘metro id si el nombre no coincide con ningΓΊn patrΓ³n conocido.

  • EN: The outDir option lets you place the generated code anywhere: --out=src/modules/orders

  • ES: La opciΓ³n outDir permite colocar el cΓ³digo generado en cualquier lugar: --out=src/modules/orders

  • EN: All generated files include a @generated bytekit --ddd JSDoc tag for easy identification.

  • ES: Todos los archivos generados incluyen una etiqueta JSDoc @generated bytekit --ddd para fΓ‘cil identificaciΓ³n.


VΓ©ase tambiΓ©n / See Also

  • CLI Overview β€” All CLI modes and global options
  • Type Generator β€” Generate TypeScript types from API responses
  • Swagger Generator β€” Generate DTOs from OpenAPI/Swagger specs
  • ApiClient β€” The HTTP client used by the generated HTTP adapter
  • RetryPolicy β€” Add retry strategies to your API calls
  • CircuitBreaker β€” Protect your services with the circuit breaker pattern
⚠️ **GitHub.com Fallback** ⚠️