Resumo rápido:
Entidade→ Tem identidade única e carrega regras de negócio.Objeto de Valor→ Imutável, definido apenas por seus valores, sem identidade.DTO→ Estrutura simples de transporte de dados entre camadas (API, UI, banco) e o domínioMapper→ Serviço de conversão entre camadas.Adapter→ É responsável por traduzir dados de um sistema externo para algo que o domínio entenda.
Saiba mais sobre isso lendo o Guia para Nomeação de Classes, Métodos e Funções
1. Entidades
- Definição: Representam conceitos centrais do domínio que possuem identidade própria. Mesmo que seus atributos mudem, a Entidade continua sendo a mesma ao longo do tempo.
- Características:
- Possuem ID único.
- Representam algo que evolui no sistema (ex.: Usuário, Pedido, Produto).
- Carregam regras de negócio relacionadas ao ciclo de vida do objeto.
- Quando usar:
- Sempre que precisar representar algo único e persistente no domínio.
Exemplo:
class User {
constructor(
public readonly id: string,
private name: string,
private email: string,
) {}
changeEmail(newEmail: string) {
if (!newEmail.includes('@')) {
throw new Error('Email inválido');
}
this.email = newEmail;
}
}
2. Objetos de Valor (Value Objects)
- Definição: Objetos do domínio sem identidade própria, definidos apenas por seus atributos e sempre imutáveis.
- Características:
- Não têm ID.
- Dois objetos com os mesmos valores são considerados iguais.
- São imutáveis: qualquer alteração gera um novo objeto.
- Quando usar:
- Para modelar conceitos representados como valores compostos, garantindo consistência e regras locais.
Exemplo:
class Email {
constructor(private readonly value: string) {
if (!value.includes('@')) {
throw new Error('Email inválido');
}
}
getValue(): string {
return this.value;
}
}
3. DTO (Data Transfer Object)
- Definição:
- Objetos usados apenas para transferência de dados entre camadas (ex.: Controllers → Use Cases, API → Application).
- Características:
- Estruturas simples, sem lógica de domínio.
- Podem representar entrada ou saída de operações.
- Facilitam a serialização (ex.: JSON, resposta HTTP).
- Quando usar:
- Para transportar dados de camadas externas para a aplicação.
- Para retornar informações processadas ao cliente.
Exemplo: em TS
interface CreateUserDTO {
name: string;
email: string;
}
interface UserResponseDTO {
id: string;
name: string;
email: string;
}
//Em PHP
class UserResponseDTO
{
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly string $email
) {}
}
⚠️ DTOs não contêm regras de negócio, apenas dados
4. Como Entidades, Objetos de Valor e DTOs se relacionam
Fluxo básico:
- O Controller recebe um DTO
- Converte em Entidade + Objetos de Valor
- Executa as regras de negócio
- Retorna outro DTO para fora
Passos:
- O Controller recebe um DTO de entrada (
CreateUserDTO). - Converte o campo
emailem um Objeto de Valor (Email). - Cria a Entidade
Usercom os valores validados. - Executa regras de negócio (ex.: verificar se já existe usuário com o mesmo e-mail).
- Persiste a Entidade (via repositório).
- Retorna um DTO de saída (
UserResponseDTO).
5. Mappers (Conversão entre Domínio e DTOs)
- Definição: Responsáveis por converter dados entre DTOs e Entidades/Objetos de Valor. Garantem isolamento entre domínio e infraestrutura.
- Quando usar:
- Recebendo DTO de entrada → converter para Entidade + Value Objects.
- Enviando resposta → converter Entidade em DTO de saída.
- Onde ficam:
- Camada de application ou infrastructure.
- Nunca dentro do domínio.
- Boas práticas:
- Não colocar lógica de negócio.
- Converter dados de forma simples e direta.
Exemplo:
class UserMapper {
static toDTO(user: User): UserResponseDTO {
return {
id: user.id,
name: user.name,
email: user.getEmail(),
};
}
static toEntity(dto: CreateUserDTO): User {
const email = new Email(dto.email);
return new User(crypto.randomUUID(), dto.name, email);
}
}
6. Ports e Adapters
- Definição: Parte da estratégia de Ports & Adapters. Mantêm o domínio independente de tecnologias externas, criando contratos (ports) e implementações (adapters).
- Quando usar:
- Integrações com APIs externas (ex.: Footstats, Firebase).
- Interações com infraestruturas (ex.: filas, provedores de email, cache).
- Sempre que o formato externo não for compatível com o domínio.
Exemplo:
// SDK fictício de terceiro
class ExternalEmailService {
async send(payload: { to: string; subject: string; body: string }): Promise<void> {
console.log('Enviando email externo...', payload);
}
}
// Port (contrato do domínio)
interface EmailProvider {
sendEmail(to: string, subject: string, body: string): Promise<void>;
}
// Adapter (implementação concreta)
class ExternalEmailAdapter implements EmailProvider {
constructor(private externalService: ExternalEmailService) {}
async sendEmail(to: string, subject: string, body: string): Promise<void> {
await this.externalService.send({ to, subject, body });
}
}
- Onde ficam:
- Port = contrato definido pelo domínio (ex.:
EmailProvider). - Adapter = implementação do contrato para uma tecnologia específica (ex.:
ExternalEmailAdapter). - Geralmente na camada de infrastructure.
- Port = contrato definido pelo domínio (ex.:
7. Boas práticas gerais
- Entidades nunca recebem nem retornam DTOs. Trabalham apenas com Objetos de Valor e outras Entidades.
- DTOs só existem nas bordas. Nunca dentro do domínio.
- Objetos de Valor são usados pelas Entidades. Ex.:
Usertrabalha comEmail, não comstring. - Transformações ficam em Mappers/Adapters. Ex.:
UserMapper.toDTO(user)ouUserMapper.toEntity(dto).
