Domain Errors
Sistema de erros de dominio do Facter Truck. Substitui o antigo AppException/ExceptionHandler por erros tipados e estruturados.
Por que DomainErrors?
O padrao antigo usava ExceptionHandler com mensagens hardcoded e sem estrutura. Problemas: sem codigo de erro, frontend precisa parsear strings, dificil internacionalizacao.
O padrao atual usa DomainError que:
- Nao tem conhecimento de HTTP (separacao de camadas)
- Carrega dados contextuais tipados
- E convertido para HTTP pelo
GlobalExceptionFilter - Permite o frontend usar
switch(errorCode)em vez de regex
Estrutura de Arquivos
src/core/domain/errors/
├── domain-error.ts # Classe base
├── work-order.errors.ts # Erros de WorkOrder
├── part-request.errors.ts # Erros de PartRequest
├── vehicle.errors.ts # Erros de Vehicle
└── index.ts # Re-exports
src/core/exceptions/
├── error-codes.ts # Enum ErrorCode + ErrorMetadata
src/infra/http/filters/
└── global-exception.filter.ts # Converte DomainError -> HTTP
Classe Base
// src/core/domain/errors/domain-error.ts
export abstract class DomainError extends Error {
public readonly context?: Record<string, unknown>;
public readonly timestamp: Date;
constructor(message: string, context?: Record<string, unknown>) {
super(message);
this.name = this.constructor.name;
this.context = context;
this.timestamp = new Date();
}
}
Caracteristicas: sem conhecimento HTTP, context para dados dinamicos, timestamp automatico, name automatico via constructor.name.
Como Criar um Novo Domain Error
Passo 1: Criar a classe de erro
// src/core/domain/errors/vehicle.errors.ts
import { DomainError } from './domain-error';
export class VehicleNotFoundError extends DomainError {
constructor(vehicleId: string) {
super(`Vehicle not found: ${vehicleId}`, { vehicleId });
}
}
export class DuplicatePlateError extends DomainError {
constructor(plate: string) {
super(`Vehicle with plate ${plate} already exists`, { plate });
}
}
Passo 2: Adicionar ErrorCode e metadata PT-BR
// src/core/exceptions/error-codes.ts
export enum ErrorCode {
VEHICLE_NOT_FOUND = 'VEHICLE_NOT_FOUND',
VEHICLE_DUPLICATE_PLATE = 'VEHICLE_DUPLICATE_PLATE',
}
export interface ErrorMetadata {
title: string;
description: string;
suggestedActions?: string[];
severity: 'error' | 'warning' | 'info';
retryable: boolean;
}
export const ERROR_METADATA: Record<ErrorCode, ErrorMetadata> = {
[ErrorCode.VEHICLE_NOT_FOUND]: {
title: 'Veiculo Nao Encontrado',
description: 'O veiculo solicitado nao foi encontrado no sistema.',
severity: 'error',
retryable: false,
},
[ErrorCode.VEHICLE_DUPLICATE_PLATE]: {
title: 'Placa Duplicada',
description: 'Ja existe um veiculo cadastrado com esta placa.',
suggestedActions: ['Verifique a placa informada', 'Busque o veiculo existente'],
severity: 'warning',
retryable: false,
},
};
Passo 3: Mapear no GlobalExceptionFilter
// src/infra/http/filters/global-exception.filter.ts
const DOMAIN_ERROR_MAP: Record<string, { status: number; code: ErrorCode }> = {
VehicleNotFoundError: { status: 404, code: ErrorCode.VEHICLE_NOT_FOUND },
DuplicatePlateError: { status: 409, code: ErrorCode.VEHICLE_DUPLICATE_PLATE },
};
Passo 4: Exportar
// src/core/domain/errors/index.ts
export * from './vehicle.errors';
Resposta HTTP Estruturada
Quando um DomainError e capturado pelo GlobalExceptionFilter, a resposta segue este formato:
{
"statusCode": 404,
"timestamp": "2026-01-24T10:30:00.000Z",
"path": "/api/vehicles/abc-123",
"errorCode": "VEHICLE_NOT_FOUND",
"title": "Veiculo Nao Encontrado",
"message": "Vehicle not found: abc-123",
"description": "O veiculo solicitado nao foi encontrado no sistema.",
"severity": "error",
"retryable": false,
"context": { "vehicleId": "abc-123" }
}
Uso no Frontend
switch (error.response?.data?.errorCode) {
case 'VEHICLE_NOT_FOUND':
toast.error(error.response.data.title);
router.push('/vehicles');
break;
case 'VEHICLE_DUPLICATE_PLATE':
toast.warning(error.response.data.title);
break;
default:
toast.error(error.response.data.message);
}
Regra de Migracao
Ao tocar em um use case que ainda usa AppException ou ExceptionHandler, migrar para DomainError. Status atual: 32 arquivos ainda usam o padrao antigo. Veja Tech Debt para detalhes.
Referencias
- Clean Architecture -- Separacao de camadas
- Testes -- Como testar erros de dominio
- Tech Debt -- Status da migracao