Handling the internal domain errors as exceptions was a bad idea and I didn’t like since the beginning. For me an Exception must be thrown when there is no other choice, the method can’t deal with that action and the caller has any options to handle this, if the caller does not have any options why throw this exception? For example, the indexOf method of List object returns a -1 if the index is not found, it does not throw an IndexNotFoundException, on the other hand, the subList method throws an IllegalArgumentException if the range of the sublist is not valid, because it can not return a valid subset with wrong arguments, but in this case the input to the method is wrong, the developer commited an error by calling this function with this wrong values.
Exceptions make your app crash and when your app crashes the user gets the idea that your app is crap. But this does not mean that you don’t have to use them, this means that you have to use them properly. In my previous examples I was overusing Exceptions to model every error in my domain layer and I don’t like that idea so I tried to model this expected behaviour as a custom error object.
I tried to model this new approach with the example of the CreateProduct interactor of my previous post:
You can see various controled cases that return a possible error and the client of this interactor should handle them in a proper way for that screen. For example, if the user is not logged in, maybe in your screen you can show a Log in Dialog to let the user make a log in action. If the input fields that define a product are not valid the interactor is going to return a NotValidProductError, this NotValidProductError contains also a list of fields that are not valid, and maybe you can model a reason explaining why this field is not valid (empty, not formatted properly…). In the case of ProductCreator maybe you can return a CreateProductError with a field reason that is another Error that specifies why it happened.
In summary, an interactor returns different possible wrong states that the client can manage in a different way depending of the context. I tried to avoid exceptions because these cases are not unexpected actions, you can give different behaviours for all of them or just inform the user that something went wrong, send a crash to your crash tracker etc…