Introduce ServiceResponse#reason to distinguish between errors

Problem

The ServiceResponse#http_status is a violation of a good Hexagonal architecture because it ties the result of a domain service (business logic) to how it should be rendered to the client. It should be the adapter’s responsibility (for example the REST/GraphQL/Controller endpoint) to decide what HTTP status to use.

Maybe we could use reason or resolution which we can set to a symbol that the caller can parse and decide how to react.

This can be a problem if we want to reuse a service object across 2 endpoints that return different HTTP statuses on error. Also having a way to choose how to react to a response error based on the reason (for example: a symbol) would be better than switching based on the http_status or message.

Proposal

  • Introduce a reason: option in the ServiceResponse object.
  • Add static analyzer to block new usages of http_status.
  • Create issues to retire use of http_status in favour of reason.
class MyService
  def execute
    # do some work
  rescue Gitlab::Access::AccessDeniedError => e
    ServiceResponse.error(message: e.message, reason: :access_denied)
  end
end

# how to use it?
response = MyService.new(...).execute

if response.success?
  head :ok  
elsif response.reason == :access_denied
  head :forbidden
else
  head :bad_request
end
Edited by Fabio Pitino