Blocos básicos de DDD: entidades e objetos de valor
Modele o domínio com entidades ricas e objetos de valor imutáveis.
Blocos básicos de DDD: entidades e objetos de valor é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.
O núcleo tático
Os padrões táticos do Design Orientado a Domínio fornecem um vocabulário para modelar conceitos de negócio no código. Os dois blocos fundamentais são Entidades (objetos definidos pela identidade ao longo do tempo) e Objetos de Valor (objetos definidos por seus atributos e tratados como imutáveis). Acertar essa distinção molda todo o restante da camada de domínio.
Identidade versus valor
Uma entidade tem uma identidade estável que persiste mesmo quando seus atributos mudam: um Customer continua sendo o mesmo cliente depois de mudar de casa. Um objeto de valor não tem identidade; dois objetos de valor com atributos iguais são intercambiáveis, como duas notas de 5 dólares. Pergunte: "se todos os campos mudarem, ainda será a mesma coisa?" Se sim, trata-se de uma entidade.
Um objeto de valor
Os objetos de valor são imutáveis e autovalidáveis. As propriedades readonly do PHP 8.1 impõem a imutabilidade; o construtor protege as invariantes para que uma instância inválida jamais possa existir.
<?php
final class Email {
public function __construct(public readonly string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email: $value");
}
}
public function equals(Email $other): bool {
return strtolower($this->value) === strtolower($other->value);
}
}
$a = new Email('Jane@Example.com');
var_dump($a->equals(new Email('jane@example.com'))); // true
Igualdade por valor
Os objetos de valor são comparados por igualdade estrutural, não por referência. Forneça um método explícito equals() em vez de depender de ==, pois a comparação flexível do PHP tem regras surpreendentes. Um objeto de valor Money deve considerar tanto o valor quanto a moeda.
<?php
final class Money {
public function __construct(
public readonly int $cents,
public readonly string $currency
) {}
public function equals(Money $o): bool {
return $this->cents === $o->cents && $this->currency === $o->currency;
}
public function add(Money $o): self {
if ($this->currency !== $o->currency) {
throw new DomainException('Currency mismatch');
}
return new self($this->cents + $o->cents, $this->currency);
}
}
$sum = (new Money(500, 'USD'))->add(new Money(250, 'USD'));
echo $sum->cents, PHP_EOL; // 750
Imutabilidade e métodos de cópia
Como os objetos de valor são imutáveis, "alterar" um deles significa retornar uma nova instância. Métodos como add() ou withAmount() nunca modificam o objeto; eles produzem um novo valor. Isso elimina erros de aliasing e torna os objetos de valor seguros para serem compartilhados livremente em todo o domínio.
<?php
final class DateRange {
public function __construct(
public readonly DateTimeImmutable $start,
public readonly DateTimeImmutable $end
) {
if ($end < $start) { throw new InvalidArgumentException('end before start'); }
}
public function withEnd(DateTimeImmutable $end): self {
return new self($this->start, $end);
}
}
$r = new DateRange(new DateTimeImmutable('2026-01-01'), new DateTimeImmutable('2026-01-10'));
$r2 = $r->withEnd(new DateTimeImmutable('2026-02-01'));
echo $r->end->format('Y-m-d'), ' / ', $r2->end->format('Y-m-d'), PHP_EOL;
Uma entidade
Uma entidade é definida pela identidade, normalmente um identificador gerado pelo domínio, e não por um incremento automático do banco de dados. A igualdade compara os identificadores. Seus atributos podem mudar por meio de métodos ricos em comportamento que protegem as invariantes.
<?php
final class Customer {
private Email $email;
public function __construct(
public readonly string $id,
Email $email
) { $this->email = $email; }
public function changeEmail(Email $new): void { $this->email = $new; }
public function email(): Email { return $this->email; }
public function sameIdentityAs(Customer $o): bool { return $this->id === $o->id; }
}
$c = new Customer('cus_1', new Email('a@b.com'));
$c->changeEmail(new Email('c@d.com'));
echo $c->email()->value, PHP_EOL; // c@d.com
Geração de identidade
Prefira gerar a identidade no domínio (por exemplo, um UUID) antes da persistência, em vez de esperar pelo banco de dados. Isso permite construir uma entidade totalmente válida em memória, referenciá-la entre agregados e testá-la sem um banco de dados. O identificador faz parte do modelo, não é um artefato de armazenamento.
Comportamento rico, não dados anêmicos
Um antipadrão comum é o modelo de domínio anêmico: as entidades são conjuntos de getters/setters públicos, enquanto toda a lógica fica em classes de "serviço". O DDD coloca o comportamento dentro da entidade. $order->cancel() encapsula as regras do cancelamento, em vez de permitir que os chamadores alterem diretamente um campo de status.
<?php
final class Order {
private string $status = 'open';
public function __construct(public readonly string $id) {}
public function cancel(): void {
if ($this->status === 'shipped') {
throw new DomainException('Cannot cancel a shipped order');
}
$this->status = 'cancelled';
}
public function status(): string { return $this->status; }
}
$o = new Order('o1');
$o->cancel();
echo $o->status(), PHP_EOL; // cancelled
Objetos de valor em toda parte
Substitua a obsessão por tipos primitivos por objetos de valor. Em vez de passar string $email, int $cents e string $currency por toda parte, envolva-os em Email e Money. Benefícios:
- A validação acontece uma vez, na construção.
- As regras do domínio (correspondência de moedas) ficam junto dos dados.
- As assinaturas de tipo documentam a intenção e evitam confusões.
Observação sobre mapeamento da persistência
Os objetos de valor geralmente são mapeados para colunas incorporadas, em vez de tabelas próprias (Doctrine #[Embeddable]). As entidades são mapeadas para linhas identificadas por sua identidade. Mantenha as preocupações de persistência fora dos próprios objetos de domínio: o modelo não deve saber que está armazenado no MySQL. Um mapeador objeto-relacional ou um mapeador escrito manualmente traduz entre o domínio rico e o banco de dados.
Objetos de valor compostos
Os objetos de valor podem ser compostos. Um Address agrega vários tipos primitivos em um conceito coeso, com sua própria igualdade e formatação. Assim, a entidade mantém um único tipo rico em vez de cinco textos soltos, e as regras do endereço ficam em um só lugar.
<?php
final class Address {
public function __construct(
public readonly string $street,
public readonly string $city,
public readonly string $postcode
) {
if ($postcode === '') { throw new InvalidArgumentException('postcode required'); }
}
public function equals(Address $o): bool {
return $this->street === $o->street
&& $this->city === $o->city
&& $this->postcode === $o->postcode;
}
public function oneLine(): string {
return "{$this->street}, {$this->city} {$this->postcode}";
}
}
echo (new Address('1 Main St', 'Ankara', '06000'))->oneLine(), PHP_EOL;
Verificação rápida
Entidade ou objeto de valor?
Recapitulação
Você aprendeu os dois blocos fundamentais do DDD. As entidades são definidas por uma identidade estável, carregam um comportamento rico e protegem suas invariantes por meio de métodos. Os objetos de valor são imutáveis, autovalidáveis e comparados pela igualdade de atributos, substituindo a obsessão por tipos primitivos. Modelá-los corretamente, mantendo a lógica dentro dos objetos em vez de conjuntos de dados anêmicos, é a base para agregados, repositórios e o restante da camada de domínio.
Perguntas Frequentes
A aula “Blocos básicos de DDD: entidades e objetos de valor” é grátis?
Sim — o texto completo de “Blocos básicos de DDD: entidades e objetos de valor” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.
O que vou aprender em “Blocos básicos de DDD: entidades e objetos de valor”?
Modele o domínio com entidades ricas e objetos de valor imutáveis. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar PHP Academy?
Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.
Quanto tempo leva a aula “Blocos básicos de DDD: entidades e objetos de valor”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de PHP Academy?
Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Blocos básicos de DDD: entidades e objetos de valor
- Agregados, repositórios e fábricas
- Eventos e serviços de domínio
- Contextos delimitados e mapeamento de contextos