Skip to content

feat: standardize error handling#5

Merged
allisson merged 1 commit intomainfrom
error-domain
Jan 24, 2026
Merged

feat: standardize error handling#5
allisson merged 1 commit intomainfrom
error-domain

Conversation

@allisson
Copy link
Owner

Implement a comprehensive error handling system that expresses business intent rather than exposing infrastructure details. This change establishes a clear separation between domain errors, infrastructure errors, and HTTP responses following Clean Architecture principles.

Changes:

  • Create shared domain error package (internal/errors/):
    • Define standard errors: ErrNotFound, ErrConflict, ErrInvalidInput, ErrUnauthorized, ErrForbidden
    • Add helper functions: Wrap(), Is(), As()
    • Provide foundation for domain-specific error definitions
  • Add user domain errors (internal/user/domain/):
    • ErrUserNotFound wraps ErrNotFound
    • ErrUserAlreadyExists wraps ErrConflict
    • ErrInvalidEmail, ErrNameRequired, ErrEmailRequired, ErrPasswordRequired wrap ErrInvalidInput
  • Update repository layer (internal/user/repository/):
    • Transform sql.ErrNoRows to domain.ErrUserNotFound
    • Detect unique constraint violations and return domain.ErrUserAlreadyExists
    • Support both PostgreSQL and MySQL error detection
    • Prevent infrastructure error leakage
  • Enhance use case layer (internal/user/usecase/):
    • Add input validation returning domain errors
    • Pass through repository domain errors without re-wrapping
    • Normalize email addresses (lowercase, trimmed)
    • Validate email format
  • Create HTTP error mapper (internal/httputil/):
    • Add HandleError() for automatic domain error → HTTP status mapping
    • Add HandleValidationError() for JSON decode errors
    • Return structured JSON error responses with error codes
    • Map: ErrNotFound→404, ErrConflict→409, ErrInvalidInput→422, ErrUnauthorized→401, ErrForbidden→403
    • Log all errors with full context before responding
  • Update HTTP handlers (internal/user/http/):
    • Replace ad-hoc error handling with httputil.HandleError()
    • Provide consistent error responses across endpoints
    • Return appropriate HTTP status codes based on error type
  • Update DTOs (internal/user/http/dto/):
    • Return domain errors from validation methods
    • Maintain consistency with domain error definitions
  • Update all tests:
    • Repository tests verify domain errors are returned
    • Use case tests check error propagation
    • HTTP tests validate correct status codes and error response structure
    • All tests pass with 100% compatibility
  • Comprehensive README documentation:
    • Add "Error Handling" section with architecture explanation
    • Document error flow through layers (repository → use case → handler)
    • Include HTTP error response mapping table
    • Provide examples for adding errors to new domains
    • Update all relevant sections to mention error handling

Benefits:

  • No infrastructure leaks: Database errors never exposed to clients
  • Business intent: Errors express domain concepts (ErrUserNotFound vs sql.ErrNoRows)
  • Consistent HTTP mapping: Same domain error always maps to same status code
  • Type-safe: Use errors.Is() to check for specific error types
  • Structured responses: All errors return consistent JSON format
  • Centralized logging: Full error context logged before responding
  • Extensible: Easy pattern to follow when adding new domains

Breaking changes: None

  • HTTP error responses now return structured JSON with "error" and "message" fields
  • Error codes changed (e.g., "not_found" instead of generic messages)
  • HTTP status codes now accurately reflect error types (404, 409, 422 instead of 500)

Implement a comprehensive error handling system that expresses business
intent rather than exposing infrastructure details. This change establishes
a clear separation between domain errors, infrastructure errors, and HTTP
responses following Clean Architecture principles.

Changes:
* Create shared domain error package (internal/errors/):
  - Define standard errors: ErrNotFound, ErrConflict, ErrInvalidInput,
    ErrUnauthorized, ErrForbidden
  - Add helper functions: Wrap(), Is(), As()
  - Provide foundation for domain-specific error definitions
* Add user domain errors (internal/user/domain/):
  - ErrUserNotFound wraps ErrNotFound
  - ErrUserAlreadyExists wraps ErrConflict
  - ErrInvalidEmail, ErrNameRequired, ErrEmailRequired, ErrPasswordRequired
    wrap ErrInvalidInput
* Update repository layer (internal/user/repository/):
  - Transform sql.ErrNoRows to domain.ErrUserNotFound
  - Detect unique constraint violations and return domain.ErrUserAlreadyExists
  - Support both PostgreSQL and MySQL error detection
  - Prevent infrastructure error leakage
* Enhance use case layer (internal/user/usecase/):
  - Add input validation returning domain errors
  - Pass through repository domain errors without re-wrapping
  - Normalize email addresses (lowercase, trimmed)
  - Validate email format
* Create HTTP error mapper (internal/httputil/):
  - Add HandleError() for automatic domain error → HTTP status mapping
  - Add HandleValidationError() for JSON decode errors
  - Return structured JSON error responses with error codes
  - Map: ErrNotFound→404, ErrConflict→409, ErrInvalidInput→422,
    ErrUnauthorized→401, ErrForbidden→403
  - Log all errors with full context before responding
* Update HTTP handlers (internal/user/http/):
  - Replace ad-hoc error handling with httputil.HandleError()
  - Provide consistent error responses across endpoints
  - Return appropriate HTTP status codes based on error type
* Update DTOs (internal/user/http/dto/):
  - Return domain errors from validation methods
  - Maintain consistency with domain error definitions
* Update all tests:
  - Repository tests verify domain errors are returned
  - Use case tests check error propagation
  - HTTP tests validate correct status codes and error response structure
  - All tests pass with 100% compatibility
* Comprehensive README documentation:
  - Add "Error Handling" section with architecture explanation
  - Document error flow through layers (repository → use case → handler)
  - Include HTTP error response mapping table
  - Provide examples for adding errors to new domains
  - Update all relevant sections to mention error handling

Benefits:
- No infrastructure leaks: Database errors never exposed to clients
- Business intent: Errors express domain concepts (ErrUserNotFound vs sql.ErrNoRows)
- Consistent HTTP mapping: Same domain error always maps to same status code
- Type-safe: Use errors.Is() to check for specific error types
- Structured responses: All errors return consistent JSON format
- Centralized logging: Full error context logged before responding
- Extensible: Easy pattern to follow when adding new domains

Breaking changes: None
- HTTP error responses now return structured JSON with "error" and "message" fields
- Error codes changed (e.g., "not_found" instead of generic messages)
- HTTP status codes now accurately reflect error types (404, 409, 422 instead of 500)
@allisson allisson merged commit 425dd5f into main Jan 24, 2026
1 check passed
@allisson allisson deleted the error-domain branch January 24, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant