Skip to content

PT BR Conceitos

Vinícius Campos edited this page Aug 22, 2026 · 1 revision

Conceitos

English · Voltar às docs

Seis ideias sustentam a biblioteca inteira.

Error

Uma falha de domínio descrita como dado: um Code estável (chave do catálogo de mensagens), um ErrorCode (identificador de tela), um ErrorKind, Arguments de interpolação e um Field opcional. Não é uma exceção e não carrega stack trace.

var error = Error.NotFound("order", 42);
// Code      = "not_found"
// ErrorCode = "NOT_FOUND"
// Kind      = ErrorKind.NotFound
// Arguments = { resource: "order", id: 42 }
// Field     = null

ErrorCode é o que os clientes usam para escolher a tela. Vários Codes de catálogo podem compartilhar um ErrorCode. Omita-o na factory e Error.DefaultErrorCode(Kind) preenche (NOT_FOUND, VALIDATION, TOO_MANY_REQUESTS, …). Passe um valor específico quando a tela precisar de um identificador mais fino:

Error.Custom("order.already_shipped", ErrorKind.Conflict, new { orderId }, errorCode: "ORDER_ALREADY_SHIPPED");

Error é imutável e compara por valor, então é seguro cachear, comparar em testes e passar adiante livremente:

Error.NotFound("order", 42) == Error.NotFound("order", 42);   // true

As instâncias vêm apenas das factories estáticas — o construtor é interno. É isso que garante que todo erro do sistema tenha um formato conhecido e um código que algum catálogo consiga resolver.

ErrorKind

O conjunto fechado de espécies de falha. Um kind decide duas coisas: o status HTTP e o rank de severidade usado para escolher um vencedor quando um resultado carrega vários erros.

Regras de negócio não inventam kinds. Elas reusam um e fornecem o próprio código:

Error.Custom("order.already_shipped", ErrorKind.Conflict, new { orderId });

Esse é o trade central do design. Um conjunto fechado de kinds torna o mapeamento de transporte total — todo erro que o domínio pode produzir já tem um status definido, sem registro para manter e sem branch default para esquecer. Um espaço de códigos aberto deixa o domínio ser tão específico quanto quiser. Veja o guia ASP.NET Core para a tabela completa kind → status.

Result e Result<T>

O desfecho de uma operação: sucesso (com um valor, no caso de Result<T>) ou falha carregando um ou mais erros. É assim que código de domínio e de aplicação reporta falha — devolvendo, não lançando.

Result<Order> found   = Result<Order>.Success(order);
Result<Order> missing = Result<Order>.Failure(Error.NotFound("order", id));
Result        done    = Result.Success();

Ambos são readonly struct, então não há alocação no caminho de sucesso. Duas consequências que vale conhecer:

  • default(Result) é sucesso. Um campo que você nunca atribuiu é lido como "nada deu errado".
  • Result.Failure() com zero erros lança ArgumentException. Uma falha sem nada a reportar seria uma falha sobre a qual ninguém poderia agir.

Ler o valor é explícito — Value lança em um resultado falho, então use TryGetValue, Match, ou cheque IsSuccess antes.

Erro primário

Quando um resultado carrega vários erros, um deles dirige a resposta: o erro do kind mais severo, com empates resolvidos a favor do primeiro erro do resultado. Ele fornece o title, o detail e o errorCode do Problem Details, e seu kind fornece o status HTTP.

Os demais erros não são descartados — todos aparecem no array errors. Um formulário que falha validação em três campos devolve 400 e reporta os três.

Catálogo de mensagens

Um arquivo JSON por cultura mapeando Code → template de mensagem. Os metadados ficam em C#; só o texto é traduzido.

{ "not_found": "{resource} '{id}' não foi encontrado." }

Os tokens são preenchidos a partir de Error.Arguments. A resolução caminha da cultura pedida para o pai e daí para o catálogo invariante, então pt-BR cai para pt e depois para o default. Detalhes em Mensagens e culturas.

Escape hatch

Error.ToException() produz uma DomainException carregando os erros, para fronteiras cuja assinatura você não controla — um construtor, ou uma interface que você não escreveu.

if (quantity <= 0)
    throw Error.Validation("quantity", attemptedValue: quantity).ToException();

Isso é a exceção, não a regra. Falhas de negócio comuns devolvem um Result. Recorrer a ToException rotineiramente abre mão da propriedade que torna toda a abordagem válida: a de que a assinatura de um método diz que ele pode falhar.

Por que não exceções

Exceções são fluxo de controle para o inesperado. Um pedido inexistente, um e-mail duplicado, um token expirado — nada disso é inesperado; são desfechos que quem chamou deveria tratar. Modelá-los como valor de retorno significa:

  • A assinatura é honesta. Result<Order> Get(string id) diz que a falha é possível; Order Get(string id) afirma que não.
  • Várias falhas podem ser reportadas de uma vez. Uma exceção carrega uma.
  • O mapeamento de transporte é dado, não uma cadeia de blocos catch.
  • Nada desempilha a stack por causa de uma regra de negócio.

ErrorKind.Unexpected continua existindo para falhas genuínas — e é o único kind cujo detalhe nunca é mostrado ao cliente. Veja tratamento de 500.

Clone this wiki locally