3

Clean Architecture com TypeScript e Node.js: Estruturando Projetos que Escalam

A maioria dos projetos Node.js começa como um punhado de arquivos em src/, cresce para 200 arquivos em 6 meses e, em algum momento, ninguém sabe onde colocar a próxima feature sem quebrar três coisas. A reação natural é buscar "arquitetura" e cair em Clean Architecture. O problema: a maioria das implementações que circulam por aí adiciona 15 camadas de abstração para um CRUD de 4 entidades.

Clean Architecture funciona. Funciona quando você entende o princípio (inversão de dependência entre domínio e infraestrutura) e ignora o dogma (criar interface para tudo, inclusive para console.log). Este post mostra como estruturar um projeto Node.js com TypeScript usando as partes úteis da Clean Architecture, sem a burocracia que faz dev senior revirar os olhos.

O princípio que importa: a Regra de Dependência

Robert Martin define várias camadas concêntricas, mas o núcleo da ideia cabe em uma frase: código de negócio não importa código de infraestrutura. Nunca. A entidade Order não sabe que existe Prisma, Express ou Redis. Se você precisar trocar o banco, o domínio não muda. Se precisar trocar o framework HTTP, o domínio não muda.

Na prática, isso se traduz em três camadas para a maioria dos projetos Node.js:

CamadaResponsabilidadeDepende deExemplos concretos
DomainEntidades, value objects, regras de negócio purasNadaOrder, Email (value object), calculateDiscount()
ApplicationCasos de uso, orquestração, ports (interfaces)DomainCreateOrderUseCase, OrderRepository (interface)
InfrastructureImplementações concretas, frameworks, I/OApplication + DomainPrismaOrderRepository, ExpressRouter, RedisCache

A seta de dependência aponta sempre para dentro: Infrastructure conhece Application, Application conhece Domain, Domain não conhece ninguém.

Estrutura de pastas que reflete as camadas

Estrutura de pastas não é arquitetura, mas uma estrutura ruim sabota qualquer arquitetura boa. A organização abaixo separa por camada no primeiro nível e por módulo de domínio no segundo:

src/
├── domain/
│   ├── order/
│   │   ├── Order.ts
│   │   ├── OrderItem.ts
│   │   └── OrderStatus.ts
│   └── shared/
│       └── Email.ts
├── application/
│   ├── order/
│   │   ├── CreateOrderUseCase.ts
│   │   ├── CancelOrderUseCase.ts
│   │   └── ports/
│   │       ├── OrderRepository.ts
│   │       └── PaymentGateway.ts
│   └── shared/
│       └── UseCase.ts
├── infrastructure/
│   ├── persistence/
│   │   └── PrismaOrderRepository.ts
│   ├── http/
│   │   ├── server.ts
│   │   └── routes/
│   │       └── orderRoutes.ts
│   ├── payment/
│   │   └── StripePaymentGateway.ts
│   └── config/
│       └── container.ts
└── main.ts

Essa estrutura escala até 20-30 entidades sem virar labirinto. Acima disso, considere monorepos com Turborepo para dividir domínios em pacotes independentes.

Domain: entidades com regras, não DTOs glorificados

Uma entidade de domínio encapsula regras de negócio. Se sua entidade é um objeto com getters e setters e zero lógica, você tem um DTO disfarçado.

// src/domain/order/Order.ts
import { OrderItem } from "./OrderItem";
import { OrderStatus } from "./OrderStatus";

export class Order {
  private constructor(
    public readonly id: string,
    public readonly customerId: string,
    private _items: OrderItem[],
    private _status: OrderStatus,
    public readonly createdAt: Date
  ) {}

  // Factory method garante que Order nunca nasce em estado inválido
  static create(id: string, customerId: string, items: OrderItem[]): Order {
    if (items.length === 0) {
      throw new Error("Order must have at least one item");
    }
    return new Order(id, customerId, items, OrderStatus.PENDING, new Date());
  }

  // Reconstituição a partir do banco não valida regras de criação,
  // porque o dado já foi validado quando entrou
  static reconstitute(
    id: string,


---

Leia o artigo completo em [https://www.vivodecodigo.com.br/backend/clean-architecture-typescript-nodejs-estrutura-projetos](https://www.vivodecodigo.com.br/backend/clean-architecture-typescript-nodejs-estrutura-projetos)
Carregando publicação patrocinada...
1

Meus 2 cents,

Parabens pelo post !

Sem entrar na briga filosofica sobre "Clean Architecture", gosto do uso de DTOs/contratos no harness/SPECs para o desenvolvimento de aplicacoes usando agentes - faz bastante diferenca no resultado depois.

Obrigado por compartilhar !

Saude e Sucesso !


Este post foi favoritado via extensão TABNEWS FAVORITOS

Tem curiosidade sobre IA ? Da uma olhada no meu LIVRO: IA PARA ENGENHEIROS