Conceptos Básicos de Arquitectura y Diseño
8 ejemplos para empezar con la Arquitectura y Diseño para backends de Node.js - 6 básicos y 2 intermedios.
Busca en todas las páginas de la documentación
8 ejemplos para empezar con la Arquitectura y Diseño para backends de Node.js - 6 básicos y 2 intermedios.
Estos ejemplos asumen Node.js 24.18.0, TypeScript 5.6+, y una pequeña API de Express 5 o Fastify 5.
mkdir orders-api && cd orders-api
npm init -y
npm install express@5 zod
npm install -D typescript@5.6 @types/node @types/express tsx
npx tsc --initUn único desplegable, límites de módulo claros dentro del repositorio.
src/
├── modules/
│ ├── orders/
│ │ ├── domain/
│ │ ├── application/
│ │ └── infrastructure/
│ └── billing/
├── shared/
└── main.tsRelacionado: Monolito Modular - package-by-feature dentro de un único desplegable
Las rutas traducen HTTP; el código de dominio permanece agnóstico al framework.
// src/modules/orders/application/create-order.ts
export type CreateOrderInput = { customerId: string; sku: string; qty: number };
export function createOrder(input: CreateOrderInput) {
if (input.qty <= 0) throw new Error("qty must be positive");
return { id: crypto.randomUUID(), ...input, status: "pending" as const };
}// src/modules/orders/infrastructure/http/routes.ts
import express from "express";
import { createOrder } from "../../application/create-order";
export const ordersRouter = express.Router();
ordersRouter.post("/", (req, res) => {
const order = createOrder(req.body);
res.status(201).json({ data: order });
});createOrder no tiene importaciones de Request o Response: las pruebas unitarias se ejecutan sin HTTPRelacionado: Arquitectura Hexagonal en Node - diseño de puertos y adaptadores
Un puerto es una interfaz de la que depende el dominio; los adaptadores la implementan.
// src/modules/orders/domain/ports/order-repository.ts
export type Order = { id: string; customerId: string; sku: string; qty: number };
export interface OrderRepository {
save(order: Order): Promise<void>;
findById(id: string): Promise<Order | null>;
}// src/modules/orders/infrastructure/postgres-order-repository.ts
import type { Order, OrderRepository } from "../domain/ports/order-repository";
export class PostgresOrderRepository implements OrderRepository {
constructor(private readonly pool: { query: (sql: string, params: unknown[]) => Promise<unknown> }) {}
async save(order: Order): Promise<void> {
await this.pool.query(
"INSERT INTO orders (id, customer_id, sku, qty) VALUES ($1,$2,$3,$4)",
[order.id, order.customerId, order.sku, order.qty]
);
}
async findById(id: string): Promise<Order | null> {
const rows = await this.pool.query("SELECT * FROM orders WHERE id = $1", [id]);
return (rows as Order[])[0] ?? null;
}
}pg o clientes de RedisAgrupa todo lo relacionado con "pedidos" en un solo lugar en lugar de un árbol global de controllers/.
src/modules/orders/
├── domain/
│ ├── order.ts
│ └── ports/
├── application/
│ ├── create-order.ts
│ └── list-orders.ts
└── infrastructure/
├── http/routes.ts
└── postgres-order-repository.tsimport/no-restricted-paths puede forzar los límites del móduloRelacionado: Monolito Modular - forzando límites en un solo repositorio
Los sistemas distribuidos compran independencia a costa de la red, las operaciones y la consistencia.
| Señal | Mantener monolito | Considerar servicios |
|---|---|---|
| Tamaño del equipo | 1-3 equipos en un producto | 4+ equipos pisando el mismo despliegue |
| Cadencia de despliegue | Lanzamiento compartido semanal o diario | Necesidad de despliegues horarios por dominio |
| Acoplamiento de datos | Transacciones compartidas entre características | Contextos delimitados claros con raras uniones cruzadas |
| Madurez de operaciones | Una única rotación de guardia | Equipo de plataforma para malla, trazado, SLOs |
Relacionado: Microservicios Cuando Valen la Pena - compensaciones de equipo y costos
Los Registros de Decisiones de Arquitectura sobreviven a las reorganizaciones y la pérdida de memoria.
# ADR-003: Mantener Monolito Modular para 2026
## Estado
Aceptado
## Contexto
Tres equipos, pico de 40k RPS, Postgres compartido. Sin equipo de plataforma dedicado.
## Decisión
Permanecer como un monolito modular. Reevaluar cuando la facturación necesite una cadencia de despliegue independiente.
## Consecuencias
+ Entrega de características más rápida, depuración más sencilla
- Los despliegues de facturación esperan a los lanzamientos de pedidosRelacionado: ADR: Monolito vs Servicios - plantilla específica de Node
La raíz de composición es el único lugar que conoce las implementaciones concretas.
// src/main.ts
import express from "express";
import { PostgresOrderRepository } from "./modules/orders/infrastructure/postgres-order-repository";
import { ordersRouter } from "./modules/orders/infrastructure/http/routes";
const pool = { query: async () => [] }; // reemplazar con pg.Pool real
const orderRepo = new PostgresOrderRepository(pool);
const app = express();
app.use(express.json());
app.use("/orders", ordersRouter);
app.listen(3000);main.ts o en un composition-root.ts dedicadoEjecuta una lista de verificación estructural antes de extraer un servicio.
// scripts/architecture-audit.ts - busca importaciones entre módulos
import fs from "node:fs";
import path from "node:path";
const modulesDir = "src/modules";
for (const mod of fs.readdirSync(modulesDir)) {
const files = fs.readdirSync(path.join(modulesDir, mod), { recursive: true }) as string[];
const tsFiles = files.filter((f) => f.endsWith(".ts"));
console.log(`${mod}: ${tsFiles.length} files`);
}index.ts reexporta todo, corrige primero la modularidad dentro del monolitoRelacionado: Lista de Verificación de Refactorización - auditorías de
index.ts"dios" y dispersión de rutas
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS Activo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, y NestJS 11.
Revisado por Chris St. John·Última actualización: 16 jul 2026