Você já deve ter ouvido a palavra entidade em diferentes contextos no desenvolvimento de software. No banco de dados, uma entidade é quase uma linha numa tabela. No ORM (ferramenta que converte tabelas do banco em objetos), é uma classe cheia de getters e setters.
Mas quando falamos de Domain-Driven Design (DDD) — uma abordagem para modelar software complexo focando no negócio — o significado é bem mais específico. E é sobre ele que vamos conversar hoje, sem complicação.
Imagine que você está construindo um sistema de matrículas para uma escola. No começo, você faz o simples, cria uma classe Aluno com nome, email e cpf e guarda numa tabela. Para alterar o e-mail, você faz:
$student->email = "new@email.com";
$db->save($student);
Tudo funciona mas com o tempo, os pedidos mudam:
- Ao trocar de e-mail, é preciso registrar essa alteração para auditoria.
- Um aluno não pode se matricular se já tiver outro com o mesmo CPF ativo.
- Quando o aluno muda de nome, precisa enviar uma notificação para a secretaria.
Você tenta colocar essas regras nos controllers (camada que recebe requisições HTTP) ou nos services (camada de orquestração). O resultado? O código começa a se repetir com validação de CPF que aparece em três lugares diferentes; a lógica de notificação que se perde entre uma requisição e outra; o Aluno que vira um simples saco de dados, e qualquer parte do sistema pode deixá-lo em um estado inválido (por exemplo, um aluno ativo com CPF vazio).
Essa dor tem nome: Modelo Anêmico. É quando a classe tem apenas propriedades (atributos) e nenhum comportamento (métodos com regras de negócio). O sistema fica frágil, espalhado e difícil de mudar. É justamente isso que a Entidade do DDD resolve. Ela coloca a identidade e as regras dentro do próprio objeto, protegendo a consistência do negócio.
No DDD, uma Entidade é um objeto com identidade própria (um ID único). Ela muda ao longo do tempo, mas continua sendo a mesma por causa dessa identidade. Ela contém atributos (dados) e comportamentos (regras de negócio) que garantem sua validade e evolução.
Pense em uma pessoa. Ela nasce, troca de nome ao casar, muda de endereço várias vezes, talvez até de cor do cabelo. Apesar de todas essas mudanças, essa pessoa continua sendo ela mesma — sua identidade (documento, biometria, história) é o que a define. Uma empresa também: pode mudar de sede, de sócios, de produto principal, mas o CNPJ e a razão social permanecem. No software, a Entidade é exatamente isso: algo que reconhecemos pela identidade, e não pelos seus dados momentâneos.
Abaixo um exemplo em PHP. Repare que não há set público solto — as mudanças acontecem por métodos nomeados com a intenção do negócio.
// Generic base class for all Domain Entities
abstract class Entity
{
public function __construct(public readonly string $id)
{
}
public function equals(Entity $other): bool
{
return $this->id === $other->id;
}
}
// Concrete Student entity
class Student extends Entity
{
private string $email;
private string $name;
private bool $active = true;
public function __construct(
string $id,
private readonly string $cpf,
string $name,
string $email
) {
parent::__construct($id);
$this->name = $name;
$this->email = $email;
}
public function changeEmail(string $newEmail): void
{
if (!str_contains($newEmail, '@')) {
throw new \InvalidArgumentException("Invalid email");
}
// Could register an "EmailChanged" event for audit here
$this->email = $newEmail;
}
public function deactivate(): void
{
if (!$this->active) {
return;
}
$this->active = false;
// Notify the registrar, etc.
}
public function getEmail(): string { return $this->email; }
public function getName(): string { return $this->name; }
public function getCpf(): string { return $this->cpf; }
public function isActive(): bool { return $this->active; }
}
Como usar:
$student = new Student("123", "111.222.333-44", "João", "joao@mail.com");
$student->changeEmail("joao.novo@mail.com"); // valid
$student->deactivate();
// $student->email = "any" → error (private property, no public setter)
Perceba: a identidade é o id (passado no construtor). O objeto muda (email e active), mas o id nunca muda. As regras de negócio ficam dentro da própria classe, não espalhadas.
Exemplos de entidades são: Curso, Aula, Usuário, Progresso, Discussão, Anexo, Tutor, Post, Certificado, Produto, etc.
Não devemos focar o domínio no banco de dados! Há uma propensão de desenvolvedores focarem dados em vez de domínio. Isso pode ocorrer devido a abordagens que colocam a importância no banco de dados. Em vez de projetar conceitos de domínio com comportamentos ricos, pensam principalmente em atributos “colunas” e associações/relacionamentos “chaves estrangeiras”.
Você está projetando pensando em colunas ou em comportamentos de negócio? Pare e reflita: sua classe principal tem regras ou só getters?
Com o tempo, o sistema se torna uma "Big Ball of Mud". Essa expressão descreve um software com estrutura indefinida, cheio de dependências emaranhadas. É impossível prever o efeito de uma alteração. As regras de negócio ficam espalhadas por controllers, serviços e até helpers, tornando o sistema frágil e difícil de evoluir. Para converter um modelo anêmico em uma entidade rica (com comportamento), siga estes passos:
- Remova todos os setters públicos da classe. Nenhuma propriedade poderá ser alterada diretamente de fora.
- Identifique os motivos de mudança no negócio (ex: “cancelar pedido”, “aprovar pagamento”, “alterar e-mail do aluno”).
- Crie métodos públicos nomeados com esses verbos (ex: cancelar(), aprovarPagamento(), alterarEmail($novoEmail)). Cada método deve conter as validações e regras necessárias, além de alterar o estado interno.
- Mova para dentro desses métodos toda a lógica que antes estava em serviços ou controllers.
- Proteja a criação – use um construtor que exija os dados mínimos para a entidade existir em um estado válido (incluindo o ID de identidade).
Ao seguir esses passos, você perceberá que a entidade deixa de ser um mero recipiente de dados. Ela passa a expressar, por si mesma, as regras do negócio. É aqui que alcançamos o verdadeiro propósito do DDD - ter uma entidade validada na aplicação. Isso significa que, se a entidade já existe (ou seja, foi construída respeitando suas invariantes), automaticamente ela já é válida. Você não precisa espalhar validações pelo sistema para garantir sua consistência. E mais, comportamentos ricos dentro da entidade expandem sua capacidade de evoluir sem quebrar o mundo lá fora.
Esse é o coração do que chamamos de Modelo Rico: um modelo que tem os atributos e os comportamentos, ou seja, os métodos que implementam as regras de negócio. Uma Entidade no DDD não é teoria distante — é uma ferramenta prática para acabar com a bagunça do modelo anêmico e da Big Ball of Mud. A diferença entre um sistema que só armazena dados e um que expressa o negócio está exatamente aí: nas classes que protegem sua própria identidade e evolução.
Agora, pare por um momento e olhe para a classe mais crítica do seu projeto atual. Ela tem comportamento ou é só um saco de getters e setters? Se for a segunda opção, você já tem um candidato perfeito para aplicar os cinco passos que vimos.
Não precisa refatorar tudo de uma vez. Comece pequeno: remova um setter, crie um método com nome de verbo do negócio, mova uma validação para dentro da entidade. O efeito é imediato: testes ficam mais claros, bugs diminuem e seu time volta a conversar sobre regras de negócio, não sobre colunas de banco.
Dê identidade e comportamento à sua próxima Entidade. Você vai sentir a diferença na primeira regra de negócio que deixar de se repetir. Abra o código e comece agora!
Até a próxima galerinha.
Top comments (0)