1

how could we advise the web client about a non-recoverable exception thrown by the persistence adapter ?

At first sight, I would

  1. define a domain exception to be thrown by the persistence adapter and
  2. use an exception handler, within the myproj.adapter.in.web package, to catch that exception an send an HTTP error code along with a message.

Since the hexagonal architecture dictates modules visibility,
enter image description here

I don't see were to define that exception so that it could be seen by both the persistence adapter and the exception handler. According to the above diagram, myproj.adapter.out.persistence and myproj.adapter.in.web have nothing in common.

Would it be acceptable to

  1. put that exception under myproj.port
  2. have both of the input port and the output port interfaces to depend on it, and ...
  3. have both the persistence adapter and web adapter to depend on it through their port interface?

A pragmatic solution of throwing an exception from a third-party library would solve the issue: it's visible to both packages. Still, sometimes, it's necessary to define custom exceptions for passing extra info.

Thanks in advance

4
  • HTTP/1.2 500 Internal Server ErrorCommentedFeb 9, 2024 at 18:41
  • Throw the exception, let some global exception handler in the web framework catch it and return the HTTP status above.CommentedFeb 9, 2024 at 18:42
  • And what's wrong with the exception handler just catching Exception?CommentedFeb 9, 2024 at 18:43
  • Have you considered separate exceptions in the input and output ports? And then having the services catch and adapt?CommentedFeb 10, 2024 at 0:14

2 Answers 2

3

Exceptions are pretty much by definition an exception to the normal workflow. This includes the path that they take when bubbling up and if/how they get caught by the system.

You could follow normal procedure, and not let any of the persistence information leak. If there are privacy concerns between your persistence layer and the API layer, then this might be a requirement. Usually, however, there isn't a privacy concern within the backend runtime itself, and having the domain/application layer catch an throw a completely different exception, while possible, just destroy the stack trace and does more bad than good.

have both the persistence adapter and web adapter to depend on it through their port interface?

The question is whether your API needs to know the specific exception type in the first place?

In general, I would already advise you that your API layer should have a last line of defense that catches any and all exceptions (as close to the API controller method as you can), and instead returns an (otherwise informationless) 500 HTTP error (while also logging the exception to your logs).

This means two things: (a) you don't need to know the specific exception type in order to handle it and (b) you can confidently let the exception bubble up and trust that it will be caught and handled by the API, there's no need to add more middle men unless there's a concrete reason for having them.

    3

    Generally, services have a troubleshooting layer, which is typically not depicted.

    When the application layer catches the exception from the persistence layer, a unique random Troubleshooting ID should be generated and associated with this catch. This ID should be written to the troubleshooting log.

    When using an end-to-end troubleshooting solution such as OpenTelemetry, the Troubleshooting ID is the Root Span ID corresponding to the failed API request.


    Generally, a different exception should be returned to the web adapter. That said, it is also a pragmatic choice to reuse an existing exception type, as long as custom property fields can be inserted.

    As @Flater explains, it is okay for the web adapter exception to link to (nest) the internal persistence exception, since there is no risk of leakage.

    However, the exception for the web adapter has to include extra information, as I explain below.


    When thinking about what to return, start with (i.e. ask for) a requirement analysis on the web presentation layer, which is not depicted on your architecture diagram. Your web adapter should basically provide the bare minimum information to their web presentation layer to allow them to do their part.

    If there is no human end-user (it's all machine-to-machine), the OpenTelemetry Root Span ID is the only thing you should mention in your HTTP 500. (If the service is launched in debug mode by the developer, they would not need to go into OTel - they can easily check the inner exception.)

    Here is a hypothetical requirement analysis for error handling on behalf of their web presentation layer, assuming that their end-users are non-technical humans.

    1. (always) Troubleshooting ID and the exact timestamp
    2. (optional) Any reasons of failure that can be understood by the end-user. Do not include technical information. And if there's no user-understandable reason, just omit it.
    3. (optional) Any failed validations or data-related warning signs observed from any layer. This can provide hints to the end-user so that they can correct the problem on their end. If there's nothing they can correct on their side, omit it.
    4. (optional) If the persistence failure implies that some user changes have been lost or reverted, it is important to notify them, and it is also important for the web presentation layer to refresh their display to accurate reflect the state of data in the system.
    5. (only if the web interface has a "developer mode") A link that opens up a new window (to a backend log aggregator) where an authenticated developer can browse the relevant error messages associated with the Troubleshooting ID. Generally, such developer-oriented information shall never be sent through the web presentation layer.

    Therefore:

    1. Do include a troubleshooting ID and timestamp, for technical support ("correlation") purpose.
    2. Ask the web presentation layer team how they would like to handle such situations. Then, design yours to satisfy their minimum requirement.
    3. It is up to you to decide whether to use English string literals for user-oriented error guidance messages.
      • Doing so can increase the maintenance effort because you'll be asked to update the strings ("exception messages") more often than you'd like.
      • Not doing so requires more cooperation between you and the web presentation layer team; on the other hand it provides UX with the flexibility they need for user-oriented language use.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.