Exceptions should be reserved for exceptional situations - either something totally unexpected (i.e. a bug), or something which completely prevents your code from performing its function (e.g. your database is unavailable). In either case, this isn't something that a user can deal with and so I simply log as much information as I can and display a generic message to the user to let them know what the support process looks like (e.g. contact your administrator).
Don't have each layer throw its own type of exception - the only reason to create a new exception type is if you want to add more contextual information in the form of properties on the exception, or if you want to be able to catch that exception (which is very rarely - if your application is catching an exception its an indication that perhaps the exception is not all that exceptional).
I Don't catch an exception unless I can either:
- Add information, e.g. if I'm processing a list of items I might catch all
exceptions and rethrow a wrapper exception that includes information about the item being processed
- Handle the exception, e.g. catch a specific exception that I know I can recover from (normally thrown by a 3rd party assembly), or in the outermost layers so I can log the exception and report an error to the user
If you catch and rethrow an exception be careful you don't loose any information which might help you diagnose the issue, especially the call stack. Catching and re-throwing all exceptions as a generic ServiceException
is a surefire way to make bugs a lot more difficult to diagnose.