3

I'm working on building an API interface on-top of a domain-driven architecture.

The domain layer has a bunch of specific exception classes (i.e. NameIsRequiredException, CannotPublishDraftException, etc) that get thrown.

We want an approach to cleanly handle these different types of errors without a whole bunch of try / catch blocks in our controllers.

We thought about potentially using the name of the field in the domain layer in the API response (i.e. a custom FieldRequired exception which has a field name property). However, this breaks down since we don't expose all the fields in our API, or the API field names might not match the names of the field in the domain layer (i.e. description vs desc) - plus we're coupling the names of the fields in our API to the names of the fields in our domain model.

Another approach was to have custom exception handlers (in Spring) to return specific errors for each possible exception (which will probably require us to have very specific exceptions in our domain layer). This approach will probably work but seems like it might have overhead / possible performance issues.

Wondering if there were any other strategies to dealing with this situation.

  • 1
    My first reaction is YAGNI (You Ain't Gonna Need It). Do you really expect your API users to be interested why their API call failed (for other than logging purposes)? The only aspect I can think of, is distinguishing between retriable failures and logics failures where a retry won't help. – Ralf Kleberhoff May 2 '18 at 10:48
2

There is no need for writing try-catch-Blocks all over the controller.

1) I would derive those Exceptions from a common ancestor

2) There is ExceptionHandler which catches exceptions of a certain kind and gives control to you, what to do next. In the exception, you could carry a message, which is returned, via the reponse's body.

Regarding to security concerns or leaky abstractions, I see no problem in providing in the response's body a reason like missing required field "password" or Could not publish. Document is already published.

Of ocurse you should avoid turning things inside out.

4

The proper api response for any failure in the domain layer should be 500 server error.

The task of checking if the user provided the correct input should happen somewhere else, in the application layer or even in the client side (client side validation)

In you code you need to make sure you only pass valid data to the domain layer. If it happened that a developer allowed passing wrong values then it is definitely a bug and the proper response code should be 500

  • 3
    to extend on this, you rarely want to expose external to your system the sepecific error underneath that 500, as it opens up avenues of attack on your system. Consider the difference between someone trying sql injection attacks getting "500, there was an error processing your response" and "500, table users does not exist". The latter leaks a lot of useful information about your database that subsequent requests can ferret additional info out of. – Marshall Tigerus May 2 '18 at 15:02
  • Does this apply to a DDD approach as well? Obviously there's going to be some basic checks at higher layers, but when it comes down to business constraints (i.e. can't publish a draft message, etc), wouldn't that lead to a duplication of business logic? – NRaf May 2 '18 at 21:06
  • any failure in the domain layer should be 500 server error What about something isn't found aka NotFound ? – Thomas Junk May 4 '18 at 16:37
  • 1
    I disagree. Superficial validation failure should be returned as 400 Bad Request, the 422 Unprocessable Entity works very well for domain logic related failures. 500 indicates a general API error which should be addressed by API developers. – Andy May 4 '18 at 18:21
  • 1
    @ThomasJunk, what I mean by superficial validation is e.g. sending a string in place of a property which expects an integer. Such request cannot be processed by the deserialisation logic because the data types do not match, hence cannot be understood by server resulting into 400 Bad Request. – Andy May 23 '18 at 15:19
0

I was doing this quite extensively at the time I was still trying to make layered architectures work in projects. Without knowing your exact circumstances I suspect it is something similar.

Short answer: You shouldn't do either of those options, since both leak abstractions, fields or just general knowledge about how things work in other objects.

If you are doing a single application with specific business logic (not a library, or something for general use), just write that logic into the business objects. If the business object decides it can not do something, just return some form of error description suitable for your end-user (whether it is humans or machines), without the need to interpret it in other objects of your application.

In other words, you don't need to have an intermediate abstraction of errors if you don't have any good and specific reason to have it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.