Skip to content

NeutrinoCorp/ddderr

Repository files navigation

👺 DDD Error

Go Build GoDoc Go Report Card codebeat badge Coverage Status Go Version

DDD Error is a reflection-free Domain-Driven error wrapper made for Go.

Using existing validators such as playground's implementation is overwhelming because tag validation and the need to rewrite descriptions. With DDD Error, you may still use 3rd-party validators or make your own validations in your value objects, entities or aggregates.

In addition, infrastructure exceptions were added so you may be able to catch specific kind of infrastructure errors.

Exceptions descriptions are based on the Google Cloud API Design Guidelines.

DDD Error is compatible with popular error-handling packages such as Hashicorp's go-multierror

In conclusion, DDD Error aims to ease the lack of exception handling in The Go Programming Language by defining a wide selection of common exceptions which happen inside the domain and/or infrastructure layer(s).

Note: DDD Error is dependency-free, it complies with Go's built-in error interface and avoids reflection to increase overall performance.

Installation

Install DDD Error by running the command

go get github.com/neutrinocorp/ddderr/v3

Full documentation is available here

Common Use Cases

  • Implement retry strategy and circuit breaker resiliency patterns by adding Network exception to the whitelist.
  • Not Acknowledging messages from an event bus if got a Network or Infrastructure generic exception.
  • Get an HTTP/gRPC/OpenCensus status code from an error.
  • Implement multiple strategies when an specific (or generic) type of error was thrown in.
  • Fine-grained exception logging on infrastructure layer by using GetParentDescription() function.

Usage

HTTP status codes

Set an HTTP error code depending on the exception.

err := ddderr.NewNotFound("foo")
log.Print(err) // prints: "The resource foo was not found"

if err.IsNotFound() {
  log.Print(http.StatusNotFound) // prints: 404
  return
}

log.Print(http.StatusInternalServerError) // prints: 500

Or use the builtin HTTP utils:

err := ddderr.NewNotFound("foo")
log.Print(err) // prints: "The resource foo was not found"

// HttpError struct is ready to be marshaled using JSON encoding libs
//
// Function accepts the following optional params (specified on the RFC spec):
// - Type
// - Instance
httpErr := ddderr.NewHttpError("", "", err)
// Will output -> If errType param is empty, then HTTP status text is used as type
// (e.g. Not Found, Internal Server Error)
log.Print(httpErr.Type)
// Will output -> 404 as we got a NotFound error type
log.Print(httpErr.Status)
// Will output -> The resource foo was not found
log.Print(httpErr.Detail)

Domain generic exceptions

Create a generic domain exception when other domain errors don't fulfill your requirements.

err := ddderr.NewDomain("generic error title", "foo has returned a generic domain error")
log.Print(err) // prints: "foo has returned a generic domain error"

if err.IsDomain() {
  log.Print(http.StatusBadRequest) // prints: 400
  return
}

log.Print(http.StatusInternalServerError) // prints: 500

Infrastructure generic exceptions

Create a generic infrastructure exception when other infrastructure exceptions don't fulfill your requirements.

msgErr := errors.New("sarama: Apache kafka consumer error")
err := ddderr.NewInfrastructure("generic error title", "error while consuming message from queue").
	AttachParent(msgErr)
log.Print(err) // prints: "error while consuming message from queue"
log.Print(err.Parent()) // prints: "sarama: Apache kafka consumer error"

Implement multiple strategies depending on exception kind

Take an specific action depending on the exception kind.

esErr := errors.New("failed to connect to Elasticsearch host http://127.0.0.1:9300")

err := ddderr.NewRemoteCall("http://127.0.0.1:9300").
	AttachParent(esErr)
log.Print("infrastructure error: ", err)                       // prints "failed to call external resource [http://127.0.0.1:9300]"
log.Print("infrastructure error resource: ", err.Property())       // http://127.0.0.1:9300
log.Print("is domain error: ", err.IsDomain())                 // false
log.Print("is infrastructure error: ", err.IsInfrastructure()) // true
log.Print("infrastructure error parent: ", err.Parent())       // prints "failed to connect to Elasticsearch host http://127.0.0.1:9300"

if err.IsRemoteCall() {
    // implement retry and/or circuit breaker pattern(s)
}

See examples for more details.

Requirements

  • Go version >= 1.13