Pular para o conteúdo

apps/service-design/special-case

169 · Padrões de Design de Serviço · ≈ 5 min de estudo

Special Case (Null Object)

A busca nunca devolve null nem lança “não encontrado”: devolve um objeto com a mesma interface da conta real e uma resposta segura para cada pergunta. Uma classe por situação — conta encerrada, conta inexistente — e cada uma diz qual é pelo status. Use quando a ausência é esperada e todo chamador repetiria o mesmo if (conta === null).

passos
5
arquivos
4
testes
0
tecnologias
5
Infraestrutura realTypeScriptBunElysiaPostgreSQLDocker
Baixar cartão

Cenário

O banco digital consulta saldo e autoriza saque pelo número da conta. O número pode ser de uma conta ativa, de uma conta encerrada (a linha continua no banco para extrato e auditoria) ou de uma conta que nunca existiu. As rotas tratam os três casos com o mesmo código, sem checagem de nulo, e nenhum dinheiro sai de conta encerrada ou inexistente.

Planta

Fluxo
10/10
ChamadorAPI de contasaccountLoadPostgreSQLAccountActivesaldo real, saque ate o saldoAccountClosedsaldo exibido, saque recusadoAccountNotFoundsaldo 0, saque recusadoInterfaceAccountGET ou POST/accounts/:numberSELECT por numerolinha ativalinha encerradanenhuma linhawithdrawDecide
10

10 passos — reproduza para seguir o fluxo

Como funciona

5 passos

Objeto neutro no lugar do nulo.

Esconde cada passo: lembre antes de tocar para revelar.

  1. 01

    accountLoad é o único ponto que decide o que é "inexistente" e "encerrada": devolve AccountActive, AccountClosed ou AccountNotFound, todas InterfaceAccount

  2. 02

    A rota pergunta ao objeto withdrawDecide(amountCents); cada classe responde com allowed ou com o motivo (insufficient-funds, account-closed, account-not-found)

  3. 03

    O código HTTP sai de uma tabela sobre o status do objeto (not-found → 404), não de um if de nulo

  4. 04

    O saque aprovado passa por um UPDATE com guarda de saldo e status: dois saques simultâneos não gastam o mesmo saldo

  5. 05

    Só a ausência esperada vira caso especial — banco fora do ar continua sendo erro, nunca "conta inexistente"

Trade-offs

O que se ganha, o que se paga

3 vantagenscada ganho tem um preço3 custos

Vantagens

  • Nenhum if (conta === null) espalhado pelos chamadores

  • Resposta segura por padrão: nada sai de conta inexistente

  • status distingue vazia, encerrada e inexistente

Custos

  • Uma classe a mais por situação especial

  • Usado para erro real, esconde a falha atrás de um objeto silencioso

  • Quem precisa distinguir os casos ainda lê o status

apps/service-design/special-case

4 arquivos

src/

  • account.tsInterfaceAccount e as três classes: ativa, encerrada, inexistente
  • repository_account.tsBusca que nunca devolve null e saque com guarda de saldo
  • api_account.tsRotas sem checagem de nulo nem captura de "não encontrado"

sql/

  • 01_schema.sqlschemaContas com status e saques

Executar · com Docker

  1. ./docker.sh up# sobe PostgreSQL
  2. cp .env.example .env# variáveis de ambiente
  3. bun install# dependências
  4. bun run demo# roda o cenário
  5. bun run test# integração contra o serviço real
Requisitos
BunDocker
Sobe junto
PostgreSQL

Por que se relacionam

Teste rápido

Qual destes combina com Special Case?

Próximo projeto · Padrões de Design de ServiçoTransaction Script
Esc

↑ ↓ navegarEnter abrir191 resultados