So as I'm working with WebAPI and trying to maintain thin controllers, I'm trying to figure out where to handle exceptions.
So a simple example would be to retrieve a user by email. My API needs to first verify that the user exists. If not, it needs to return 404.
To maintain a thin controller, I want to simply hand off the request to a service somewhere. I want to avoid injecting repositories into my controllers. Now it is the services responsibility to first check if the user exists. If it does, return the user. Where I'm getting confused is when the user doesn't exist.
When the user doesn't exist, is it then the services responsibility to throw an HttpException? If so then, this service would not be considered a domain service but maybe a special rest validation service? If so, does it grab the user then pass the user to a domain service when the user does exist to avoid multiple calls to the datastore?
What about a more complex scenario where I might want to add an item to a shopping cart. The controller will hand off the post request to the correct service. The service will then validate some business rules: cart exists, max order not exceeded, etc.
These are two different types of validation. It feels right to separate them into separate validation services or I end up mixing domain validation with rest validation.
I guess I'm trying to figure out how to maintain the speration of concerns and translate domain validation to rest validation.
If anyone can shed some light on or point me in the right direction that would be great!
My general choice in such situations is using a domain specific exception in service/repository, such as
UserNotFoundException. I don't think that it is a good idea for the service to meddle with the dynamics of the web container. You might want to reuse same service for a console application, etc.
Then you might catch this domain specific exception and generate a 404 page. I agree with your choice to keep it out of controller. You can specify error handlers for exceptions. There are different levels of error handlers. You can define one for each controller or you can define one for your whole application. Or you can use annotations to mark controller actions that will use your error handler.
I think the shopping cart case also supports this design. You use validation services for validating your cart. If the validations are not successful, then the shopping cart service throws a
ValidationException. Your error handler might catch the
ValidationException and generate appropriate response codes.
One nice thing about error handlers is sometimes changing requirements force you to run some business logic when a certain error occurs. If you isolate error handling to error handlers, then it'll be easy to manipulate the logic of error handling.