Casos de uso e serviços de aplicação
Expresse ações de negócio como casos de uso independentes de frameworks.
Casos de uso e serviços de aplicação é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 3 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 que um caso de uso realmente é
Um caso de uso (também chamado de serviço de aplicação ou interator) representa exatamente uma operação específica da aplicação: Registrar usuário, Fazer pedido, Cancelar assinatura. Ele coordena entidades e portas para cumprir uma única intenção. O mais importante é que ele é independente de frameworks: não há Request, nem Response, nem auxiliares globais — apenas PHP simples que pode ser chamado de qualquer lugar.
DTOs de comando e resultado
Um caso de uso recebe um DTO de comando imutável como entrada e retorna um DTO de resultado. DTOs são simples portadores de dados — não têm comportamento nem lógica de validação além da estrutura. Propriedades readonly (PHP 8.1 ou superior) impedem adulterações.
<?php
final class RegisterUserCommand
{
public function __construct(
public readonly string $email,
public readonly string $plainPassword,
) {}
}
final class RegisterUserResult
{
public function __construct(public readonly string $userId) {}
}O corpo do serviço de aplicação
O serviço traduz o comando em operações de domínio. Ele faz a orquestração no nível da aplicação — verificações de unicidade, persistência e retorno de identificadores — enquanto delega as regras às entidades.
<?php
final class RegisterUser
{
public function __construct(
private Users $users,
private PasswordHasher $hasher,
) {}
public function __invoke(RegisterUserCommand $c): RegisterUserResult {
if ($this->users->existsByEmail($c->email)) {
throw new EmailAlreadyRegistered($c->email);
}
$user = User::register(
UserId::generate(),
new Email($c->email),
$this->hasher->hash($c->plainPassword),
);
$this->users->add($user);
return new RegisterUserResult((string) $user->id());
}
}Mantenha a lógica nas entidades
Cuidado com o modelo de domínio anêmico: entidades reduzidas a métodos de leitura e atribuição, enquanto toda a lógica fica nos serviços. As invariantes pertencem à entidade. O caso de uso deve parecer um pequeno roteiro de intenções, não um bloco enorme de regras de negócio.
<?php
final class User
{
private function __construct(
private UserId $id,
private Email $email,
private string $passwordHash,
private bool $active = false,
) {}
public static function register(UserId $id, Email $e, string $hash): self {
return new self($id, $e, $hash); // invariants enforced here
}
public function activate(): void {
if ($this->active) throw new AlreadyActive();
$this->active = true;
}
public function id(): UserId { return $this->id; }
}Limites de transação
Um caso de uso é o limite de transação natural: um caso de uso = uma unidade de trabalho consistente. Em vez de espalhar beginTransaction() pelos serviços, envolva-os com um decorador transacional para manter o núcleo independente da persistência.
<?php
interface TransactionManager {
public function transactional(callable $work): mixed;
}
final class TransactionalRegisterUser
{
public function __construct(
private RegisterUser $inner,
private TransactionManager $tx,
) {}
public function __invoke(RegisterUserCommand $c): RegisterUserResult {
return $this->tx->transactional(fn() => ($this->inner)($c));
}
}Validação: onde ela pertence
Divida a validação em dois níveis:
- Validação de entrada (formato e campos obrigatórios) ocorre no adaptador de entrada ou em um validador dedicado, antes da execução do caso de uso.
- Validação de domínio (invariantes e regras de negócio) fica nos objetos de valor e nas entidades, que lançam exceções de domínio.
O caso de uso pressupõe uma entrada bem-formada e impõe seu significado.
<?php
final class Email
{
public function __construct(public readonly string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email: $value");
}
}
}
try { new Email('nope'); } catch (Throwable $e) { echo $e->getMessage(), PHP_EOL; }
echo (new Email('a@b.com'))->value, PHP_EOL;Retornando a saída sem HTTP
Há dois padrões para retornar dados sem depender de frameworks:
- Retornar um DTO de resultado (simples e síncrono).
- Porta de saída / apresentador — o caso de uso envia o resultado para um limite de saída injetado, permitindo que o adaptador decida a formatação (JSON, HTML, CLI). Assim, até mesmo o formato da resposta permanece fora do núcleo.
<?php
interface RegisterUserOutput {
public function present(RegisterUserResult $r): void;
}
final class RegisterUserWithPresenter {
public function __construct(private Users $users, private PasswordHasher $h) {}
public function __invoke(RegisterUserCommand $c, RegisterUserOutput $out): void {
$user = User::register(UserId::generate(), new Email($c->email), $this->h->hash($c->plainPassword));
$this->users->add($user);
$out->present(new RegisterUserResult((string) $user->id()));
}
}Eventos de domínio a partir de casos de uso
Os casos de uso frequentemente registram eventos de domínio gerados pelas entidades e depois os despacham quando a transação é confirmada. Isso desacopla efeitos colaterais (enviar um e-mail de boas-vindas, atualizar um modelo de leitura) do fluxo de trabalho do núcleo.
<?php
trait RecordsEvents {
private array $events = [];
protected function record(object $e): void { $this->events[] = $e; }
public function releaseEvents(): array {
$e = $this->events; $this->events = []; return $e;
}
}
final class UserRegistered {
public function __construct(public readonly string $userId) {}
}
// Use case calls $user->releaseEvents() and hands them to a dispatcher
echo 'event recorded pattern', PHP_EOL;Uma classe por caso de uso
Prefira uma classe de ação única (um método público, frequentemente __invoke) a um serviço extenso com dez métodos. Benefícios:
- Responsabilidade única clara e nomes precisos (
CancelSubscription, nãoSubscriptionService::cancel). - O construtor injeta apenas o que esta operação precisa.
- É fácil envolvê-la com decoradores (transação, registro, autorização).
Configuração na raiz de composição
O caso de uso nunca cria diretamente suas dependências; a raiz de composição faz isso. Esta é uma configuração manual que poderia ser colocada na definição de um contêiner de DI.
<?php
$pdo = new PDO('sqlite::memory:');
$users = new PdoUsers($pdo);
$hasher = new BcryptHasher();
$register = new RegisterUser($users, $hasher);
// Decorate with a transaction boundary
$register = new TransactionalRegisterUser($register, new PdoTransactionManager($pdo));
// Driving adapter calls it
$result = $register(new RegisterUserCommand('dev@coddykit.com', 's3cret!'));
echo $result->userId, PHP_EOL;Preocupações transversais por meio de decoradores
Registro, métricas e autorização são preocupações transversais — mantenha-as fora do corpo do caso de uso. Envolva o serviço em decoradores que compartilham sua interface, para que o núcleo permaneça concentrado no fluxo de trabalho enquanto as preocupações de infraestrutura são compostas ao redor dele.
<?php
interface RegisterUserHandler {
public function __invoke(RegisterUserCommand $c): RegisterUserResult;
}
final class LoggingRegisterUser implements RegisterUserHandler {
public function __construct(
private RegisterUserHandler $inner,
private LoggerInterface $log,
) {}
public function __invoke(RegisterUserCommand $c): RegisterUserResult {
$this->log->info('register.start', ['email' => $c->email]);
$r = ($this->inner)($c);
$this->log->info('register.ok', ['id' => $r->userId]);
return $r;
}
}Verificação rápida
Onde deve ficar a regra "um e-mail deve ser exclusivo e bem-formado"?
Recapitulação
Casos de uso independentes de frameworks proporcionam uma camada de aplicação limpa:
- Uma classe de ação única por operação, recebendo um DTO de comando e retornando um DTO de resultado (ou enviando o resultado para uma porta de saída).
- Entidades e objetos de valor são responsáveis pelas invariantes; o serviço apenas orquestra — evite modelos anêmicos.
- Os casos de uso são limites de transação, envolvidos por decoradores em vez de conterem
beginTransactiondiretamente. - Separe a validação de entrada (adaptador/objeto de valor) da validação de domínio (entidades).
- Eventos de domínio desacoplam efeitos colaterais; a raiz de composição conecta as dependências.
Perguntas Frequentes
A aula “Casos de uso e serviços de aplicação” é grátis?
Sim — o texto completo de “Casos de uso e serviços de aplicação” é 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 “Casos de uso e serviços de aplicação”?
Expresse ações de negócio como casos de uso independentes de frameworks. 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 3 de 4.
Quanto tempo leva a aula “Casos de uso e serviços de aplicação”?
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
- Da arquitetura em camadas à arquitetura limpa
- Portas e adaptadores explicados
- Casos de uso e serviços de aplicação
- Inversão de dependências na prática