DDD em TypeScript: Arquitetura limpa para projetos complexos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que o CRUD comum está matando seu projeto e como o DDD pode consertar isso

\n\n

Todo desenvolvedor já se deparou pelo menos uma vez com uma situação em que um projeto aparentemente simples se transforma em um "pântano" de serviços, controladores e lógica de negócios espalhada por todo o código. Você começa escrevendo UserService, depois OrderService, e seis meses depois, dentro de OrderService está a lógica de envio de e-mail, cálculo de descontos e verificação de saldo do usuário. Familiar?

\n\n

O problema é que pensamos cedo demais em bancos de dados, frameworks e APIs REST. Esquecemos o principal — o domínio (domain). O Domain-Driven Design (DDD) propõe uma abordagem radicalmente diferente: primeiro modelamos os processos de negócio na linguagem dos especialistas, e só depois — o código.

\n\n

Neste artigo, vamos explorar como aplicar Domain-Driven Design em TypeScript com exemplos reais. Você verá como construir uma arquitetura limpa, onde as regras de negócio vivem separadas da infraestrutura, e o código é fácil de testar e modificar.

\n\n

Fundamentos do DDD: linguagem, contextos e modelo

\n\n

Ubiquitous Language (Linguagem Ubíqua)

\n

O primeiro princípio do DDD é uma linguagem comum, compreensível tanto para desenvolvedores quanto para especialistas de negócio. Se o gerente diz "O cliente ativou a assinatura", então no código deve haver um método activateSubscription(), e não setStatus(1). TypeScript, com sua tipagem forte, é ideal para isso — os nomes das classes, métodos e variáveis se tornam documentação.

\n\n

Bounded Context (Contexto Delimitado)

\n

Um sistema grande é dividido em contextos independentes: "Pagamentos", "Gerenciamento de Usuários", "Entrega". Em cada contexto, há seu próprio modelo e linguagem. Os módulos do TypeScript e os tipos estritos ajudam a separar rigidamente esses contextos no nível das importações.

\n\n

Construindo o modelo de domínio em TypeScript

\n\n

Value Object (Objeto de Valor)

\n

É uma entidade imutável que é definida por seus atributos, e não por um identificador. Exemplo: Email, Money, Coordinates.

\n\n
// value-objects/Email.ts\nexport class Email {\n  private readonly value: string;\n\n  constructor(email: string) {\n    if (!this.isValid(email)) {\n      throw new Error('Invalid email address');\n    }\n    this.value = email;\n  }\n\n  private isValid(email: string): boolean {\n    return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\n  }\n\n  public getValue(): string {\n    return this.value;\n  }\n\n  public equals(other: Email): boolean {\n    return this.value === other.value;\n  }\n}\n
\n\n

Observe: o Email valida a si mesmo. Nenhum serviço precisa saber como é um e-mail válido. Isso é Domain-Driven Design em TypeScript puro — a regra de negócio vive dentro do objeto.

\n\n

Entity (Entidade)

\n

Ao contrário do Value Object, uma entidade possui um identificador (ID) e pode mudar ao longo do tempo. Exemplo: User, Order.

\n\n
// entities/User.ts\nimport { Email } from '../value-objects/Email';\nimport { UserId } from '../value-objects/UserId';\n\nexport class User {\n  private readonly id: UserId;\n  private email: Email;\n  private isActive: boolean;\n\n  constructor(id: UserId, email: Email) {\n    this.id = id;\n    this.email = email;\n    this.isActive = true;\n  }\n\n  public getId(): UserId {\n    return this.id;\n  }\n\n  public changeEmail(newEmail: Email): void {\n    if (!this.isActive) {\n      throw new Error('Cannot change email for inactive user');\n    }\n    this.email = newEmail;\n  }\n\n  public deactivate(): void {\n    this.isActive = false;\n  }\n}\n
\n\n

Aggregate (Agregado)

\n

Um agregado é um grupo de entidades e objetos de valor que são tratados como uma unidade coesa. O agregado possui uma raiz (Aggregate Root), através da qual o acesso aos objetos internos é feito.

\n\n

Exemplo: um pedido (Order)

Blogs

Book Recommendations