DDD.Error (vicander v0.1.1)

Copy Markdown View Source

Structured error produced by the framework.

  • :kind - :business (the domain said no; retrying gives the same answer) or :infrastructure (the system failed; retrying may succeed). Event handlers use it: a business error is a recorded rejection, an infrastructure error is retried.
  • :entity - business entity (module) that produced the error
  • :entity_id - the identity (Entity.identity/1) of the instance it happened to, or nil when there is none yet or it can't be told (a new call that is missing its identity)
  • :operation - operation being run (:new, :update, :validate or a defop name)
  • :code - machine-matchable code, e.g. :validation_failed
  • :message - human-readable message
  • :details - data produced by the rule that failed
  • :violations - list of %{field, code, message, details} when several rules failed
  • :context - added by outer layers (running a command adds :command)

Summary

Functions

Build a business error: the domain decided no. Never retried.

True for business errors.

All violation codes of an error (or [code] when there are no violations).

True when the error, or any of its violations, has the given code.

Build an infrastructure error: the system failed. Retried by event handlers.

True for infrastructure errors.

Build an error from a keyword list or map.

Merge extra context into the error.

Per-field map of messages (form style): %{field => [message]}. Errors without violations are reported under :base.

Build a :validation_failed error from a non-empty list of violations.

Build a violation map (one failed rule).

Convert any error reason into a DDD.Error, merging context.

Types

kind()

@type kind() :: :business | :infrastructure

t()

@type t() :: %DDD.Error{
  code: atom(),
  context: map(),
  details: map(),
  entity: module() | nil,
  entity_id: String.t() | nil,
  kind: kind(),
  message: String.t(),
  operation: atom() | nil,
  violations: [violation()]
}

violation()

@type violation() :: %{
  field: atom(),
  code: atom(),
  message: String.t(),
  details: map()
}

Functions

business(attrs)

@spec business(keyword() | map()) :: t()

Build a business error: the domain decided no. Never retried.

business?(error)

@spec business?(t()) :: boolean()

True for business errors.

codes(error)

@spec codes(t()) :: [atom()]

All violation codes of an error (or [code] when there are no violations).

has_code?(error, code)

@spec has_code?(t(), atom()) :: boolean()

True when the error, or any of its violations, has the given code.

infrastructure(attrs)

@spec infrastructure(keyword() | map()) :: t()

Build an infrastructure error: the system failed. Retried by event handlers.

infrastructure?(error)

@spec infrastructure?(t()) :: boolean()

True for infrastructure errors.

new(attrs)

@spec new(keyword() | map()) :: t()

Build an error from a keyword list or map.

put_context(error, extra)

@spec put_context(t(), map() | keyword()) :: t()

Merge extra context into the error.

to_map(error)

@spec to_map(t()) :: %{required(atom()) => [String.t()]}

Per-field map of messages (form style): %{field => [message]}. Errors without violations are reported under :base.

validation_failed(entity, operation, violations, entity_id \\ nil)

@spec validation_failed(module(), atom(), [violation()], String.t() | nil) :: t()

Build a :validation_failed error from a non-empty list of violations.

violation(field, code, message, details \\ %{})

@spec violation(atom(), atom(), String.t(), map()) :: violation()

Build a violation map (one failed rule).

wrap(error, context)

@spec wrap(any(), map() | keyword()) :: t()

Convert any error reason into a DDD.Error, merging context.

Errors pass through. Atoms and {code, message} tuples are wrapped as :business errors (a plain reason is the domain's answer). An exception becomes an :infrastructure error with code :exception. Anything else becomes :unknown (business) with the inspected reason in details.