Repository navigation
Result Pattern
Rótulo: Explicação TL;DR: Como representamos sucesso e falha em todas as camadas sem lançar exceções para fluxo de controle. Última revisão: 2026-05-18
Lançar exceções para sinalizar "OS não encontrada" ou "cliente inativo" tem 3 problemas:
- Custo — stack trace caro.
- Implicitude — a assinatura do método não documenta as falhas possíveis.
- Mistura semântica — confunde erro de programação (deveria virar 500) com resultado de negócio (deveria virar 400/404/409).
O Result Pattern torna a falha explícita no tipo de retorno.
Dois tipos definidos no SDK (Mecanica.Hermes.Shared.Core):
public record Result
{
public bool IsSuccess { get; }
public bool IsFailure => !IsSuccess;
public HttpStatusCode StatusCode { get; }
public IReadOnlyList<Error> Errors { get; }
public static Result Success(HttpStatusCode statusCode = HttpStatusCode.OK);
public static Result Accepted();
public static Result Failure(HttpStatusCode statusCode, params Error[] errors);
public static Result NotFound(string message);
public static Result Conflict(string message);
public static Result Validation(params Error[] errors);
}
public record Result<T> : Result
{
public T? Value { get; }
}public Result<OrdemDeServicoStatusBase> AvancarEtapa()
{
if (this is StatusEntregue or StatusCancelada or StatusRejeitada)
return Result.Failure<OrdemDeServicoStatusBase>(
HttpStatusCode.Conflict,
new Error("OS.estado-terminal", "OS já está em estado terminal.")
);
return Result.Success(ProximoEstado());
}public async Task<Result<OrdemDeServicoDto>> Handle(GetOrdemByIdQuery query, CancellationToken ct)
{
var os = await _reader.GetByIdAsync(query.Id, ct);
return os is null
? Result.NotFound<OrdemDeServicoDto>($"OS {query.Id} não encontrada.")
: Result.Success(os.ToDto());
}O ResultPresenter converte Result<T> em IResult HTTP:
public static IResult Present<T>(this Result<T> result) => result.StatusCode switch
{
HttpStatusCode.OK => Results.Ok(result.Value),
HttpStatusCode.Created => Results.Created(...),
HttpStatusCode.Accepted => Results.Accepted(),
HttpStatusCode.NotFound => Results.NotFound(result.AsProblemDetails()),
HttpStatusCode.Conflict => Results.Conflict(result.AsProblemDetails()),
_ => Results.Problem(result.AsProblemDetails())
};Quando o resultado é falha, a resposta segue RFC 7807:
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "OS já está em estado terminal.",
"errors": [
{ "code": "OS.estado-terminal", "message": "OS já está em estado terminal." }
]
}-
Erro de programação (argumento null em construtor, violação de invariante interna) — lançar exception. Vira 500 pelo
GlobalExceptionHandlerMiddleware. -
Concorrência otimista —
ConcurrencyConflictExceptioné lançada pelo EF Core e o consumer trata explicitamente (retry da SAGA cuida).
🔧 Mecânica Hermes — Tech Challenge 13SOAT (FIAP) · Wiki pública, não inclua credenciais · Editar a Wiki · Plano editorial
🏠 Home
📖 Visão geral
🧭 Como usar a Wiki
🚀 Início rápido
🏛️ Arquitetura
Padrões transversais
- Clean Architecture + DDD + CQRS
- Result Pattern
- State Pattern
- SAGA com MassTransit
- Outbox transacional
- Idempotência cross-service
- DLQ observability
Mensageria
Persistência
Segurança
- Autenticação Cognito + JWT
- Autorização por scopes
- M2M Client Credentials
- Webhooks assinados (HMAC)
- Modelo de ameaças
🔁 Fluxos de negócio
- Caminho feliz
- Orçamento rejeitado
- Pagamento recusado e recriado
- Pagamento expirado
- Cancelamento em execução
- Cancelamento em aguardando pagamento
- Idempotência de webhook
- Proteção por timeout da SAGA
🛠️ Infraestrutura e deploy
- Diagrama AWS completo
- Provisionamento (Terraform)
- Cluster Kubernetes
- API Gateway + VPC Link
- Cognito
- Lambda CognitoToken
- Bancos de dados
- RabbitMQ
- Pipelines GitHub Actions
📊 Observabilidade
- Visão geral
- Logs
- Métricas de negócio
- Métricas de mensageria
- Traces distribuídos
- Dashboards e alertas
🧪 Qualidade
🚨 Operação
- Runbook macro
- Runbook — Ordem de Serviço
- Runbook — Cadastros
- Runbook — Pagamentos
- Resposta a incidentes
- Forçar limpeza de namespace
🧩 SDK compartilhado
👥 Contribuição
📚 Referência
🧠 Decisões