Hi!
As at some previous occasions, I will answer my own question. I will do it with an example that uses pure Scala.
In this very simplistic example, there is one domain class that is used by an application service. In addition there is a client of the service.
The domain class is written to throw different kinds of generic exceptions:
Then there is the application service class that calls the above domain class.
As you can see, the service class do not contain any exception handling or translation. This is the goal of this exercise - I do not want to clutter the service class, containing application logic, with error handing, logging, exception translation etc. Despite this, I want the service to throw application-specific exceptions to clients in case of errors.
As an interlude and in order to make the example complete, here are the classes implementing the application-specific exceptions:
Then there is a client of the application service, which also is the starter class of this example:
If we run the example program in its current incarnation, we obtain the following output:
Trying with a message that is too short
Unexpectedly received another exception :java.lang.IllegalArgumentException
Trying with a number that is too small (negative)
Unexpectedly received another exception :java.lang.IllegalArgumentException
Trying with a number that breaks an assumption
Unexpectedly received another exception :java.lang.AssertionError
Trying with good parameters, which should result in an exception that should be passed through
Received IOException as expected: cannot read data
In three of the four cases, the client received an exception that I did not want it to receive.
So, how do we fix this without adding code to the service class and use only pure Scala?
The solution I have devised uses traits. I separate all the exception translation code into the following trait:
To bring the above trait together with the service class, I implement a child class to the service class:
Finally, we modify the client to instantiate the child service class instead of the original service class. Note also that I have uncommented one import:
If we now run the example program, we get the following output:
Trying with a message that is too short
Received a MessageTooShortException as expected: The message 'a' is too short
Trying with a number that is too small (negative)
Received a NumberTooSmallException as expected: The number '-1' is too small
Trying with a number that breaks an assumption
Received a AssumptionBrokenException as expected: Assumption broken: assumption failed: the result of doubling the number must not become 4
Trying with good parameters, which should result in an exception that should be passed through
Received IOException as expected: cannot read data
I succeeded in separating application logic, in the application service, and exception translation!
There is more, but interested readers need to wait for my next book to read about the details.
Best wishes!