Throwing a generic exception like
FooServiceException makes your method signature easy to read, but you're also assuming the caller is not likely going to want to differentiate. If he did, he could still do so, but he'd have to pilfer through your code to find out which specific implementations of
FooServiceException are thrown. Don't make the caller do that! That is why we can't have nice things!
On the other hand, if your method
getById is throwing
FooConnectionTimeoutException, FooConnectionPoolExhaustedException, FooTableNotFoundException, FooServiceObjectNotFoundException, clearly something is not right about this either. But fear no more, my good OP! There is a solution.
You can organize exceptions by type, then throw a reasonably generic exception for each type possible. In other words, have
FooConnectionTimeoutException, FooConnectionPoolExhaustedException, FooTableNotFoundException all derive from
FooDatabaseException. Your method signature then becomes:
getById(int id) throws FooServiceObjectNotFoundException, FooDatabaseException;
See how nice this is? Callers wanting to cover any and all exceptions would look no further than handling just
FooDatabaseException. Callers wanting to perform a different action upon
FooServiceObjectNotFoundException can do so no qualms.
You can further organize according to necessity if you want to be more detailed, but the level of detail is entirely up to you. The key point here being that you have full control over how specific you want your exceptions to be. As a general rule, try to reduce the number of exceptions thrown to 3 or less. Anything beyond that is somewhat unnecessary, especially if many of the exceptions are related to the same type of issue (unexpected problems, business errors, invalid input, etc.).