Let's pretend for a moment that I have an application which has the following layers: a UI, Controller, Business Logic, and Data Access layer. The UI talks to the Controller, the Controller talks to the Business Logic, and the Business Logic talks to the Data Access layer. Let's also pretend I have a table in the database that houses error messages and exceptions that the administrator can use to troubleshoot problems with the application.
If a SQL exception is thrown in the Data Access Layer (e.g. there is a network issue, string would be truncated, etc.), how far up should the exception information make it before being logged? Ideally the log would contain messages from various layers that would give enough information for a developer to track down the issue. Is it OK for a SQL exception in the Data Access layer to get tossed all the way up to the UI and logged there? Or should it be caught locally, logged, and either rethrown or wrapped in another custom exception? Or should the Data Access layer return a special type that has a flag indicating whether there was an issue, and if so, it also attaches exception information. Also, is it a security concern for SQL exception/stack trace information to make it as far as the UI?
I realize this might be a little subjective for a questions but I'm curious as to what the experts say. Please let me know if you need clarification.
No exception should escape out of the service layer. If you remove the UI, wouldn't you want the services to still be functional?
This also makes sense because it's easy to put your logging into aspects and apply them declaratively to the service interfaces.
You can wrap and rethrow if the custom exception adds value.
Related
When there is some internal exception in a Webflux application, why do I want/need to write code to handle these exceptions? I understand handling issues and returning appropriate ServerResponse bodies when the service client incorrectly invokes a service, or when a non-error-condition (i.e., query returns empty cursor, etc.) occurs.
But, other than generating debug information into a logfile, is there anything to be gained by rolling-your-own exception handling components? This approach makes "more sense" to me in a monolithic application, where one is trying to avoid a scenario where the app "just dies".
But, for a service implementation, especially if there's some incentive not to expose too much about the internal implementation to a client, why wouldn't Spring's default error/exception handling (and "500 Internal Server Error" response/message) be sufficient.
So, after some time and thought (and little, but still helpful-and-appreciated feedback), I guess it boils down to:
(a) - It provides a localized context to "do things", like logging information about the exception/error condition, or categorizing the severity of the exception within-the-context of a server-client interaction.
(b) - It provides a localized context to hide/expose information from a client, based on the nature of the exception/error condition and whether the server is deployed in a production or test environment.
(c) - Being localized, it makes maintenance/modification a bit easier, as the handling of exceptions/errors is not scattered throughout the code.
(a) and (c) are enough to make me believe it's worth the effort.
Several posts cover the issue of providing user-friendly messages based on catched exceptions, like Report exception message
I am also of the opinion to wrap exceptions of a lower layer to a defined exception of the current layer to ensure encapsulation. For example a service or domain layer should not expose exceptions from data access layer.
In my UI layer I catch the exceptions defined in service or domain layer and transform them to meaningful user messages in dialogs. I do not use the exception message because it might 1) not be user-friendly and 2) not be internationalized.
Now I am developing a mobile app the first time. I find it useful and Importamt for the users to show them an appropriate message if an action cannot be executed because of connection or network issues (maybe bad or no service or in flight mode etc.)
To show this kind of message I must catch it from the service/domain layer to transform to a user-friendly message and do internationalization etc. But this seams counterintuitive to me to expose these kind of exceptions in this layer.
So is it okay to have NetworkException exposed in domain layer in a mobile app? Or are there any other techniques to handle this scenario? I feel a bit bad about this because it introduces a leaky abstraction, but I have no idea how else to get information on these low level exceptions that are important for users.
Or am I on the wrong path and it is quite okay to tell the user "Operation x cannot be executed", without giving any reason?
Update:
Eric Evans shows this diagram in his DDD book:
There is a connection from UI layer to infrastructure layer. Can this be interpreted as the UI may be aware directly of Infrastructure exceptions?
Your domain should not be throwing any exceptions because of the reasons that you have listed and more. Exceptions are inevitable though, so it should be handled gracefully within the boundary of your domain but should not get out.
For controlled exceptions like 404 from a dependent API or the user data validation failure, you want to return meaningful data like a nullobject or just an domainspecific error object. This also applies to exceptions like network exceptions. Your method should gracefully handle the exception and your domain should return an object that fits the UL
I've been trying to read more about what to do properly catching / handling exceptions, but I don't think I've got it down. In fact, I think I'm getting much more confused and possibly implementing bad code. I don't want to do that.
An example setup that I have been using:
Mobile device makes a call to the WCF Service.
WCF Service retrieves the data from the database, and if any errors occur on the database level, they are logged and I am sent an e-mail.
WCF Service sends data (or a brief description of the exception) to the mobile device.
The mobile device processes the data, and if any error occurs, throws the error up to the UI layer.
For a few of the exceptions, I created custom ones - service exception, authorization exception, so I can properly notify the user. If the service encountered an error or an IOException occurs, the user will be notified that 'the data could not be retrieved.'
If, however, another error occurs - such as a JSON error, or anything like that 'just in case', the error is thrown to the UI layer and simply caught as Exception, since we don't really need to user to know what happened, but that an error occurred.
Is this appropriate exception handling?
Are you seeing any problems?
In general, it makes sense to have some sort of catch-all that allows the user to keep working. This should be combined with appropriate handling for any showstoppers, to let the user down gracefully, and catch anything else that would make proceeding dangerous.
"Appropriate exception handling" is always going to be a) application dependent and b) subjective - so there's no definitive answer.
In general I would say you need to do all of the following:
Specifically address and handle appropriately all likely exceptions.
Provided a catch all to prevent a non-graceful termination.
Notify the user of unexpected errors if there is potential it will effect
their data or usage (i.e. - don't mask errors that might impact user)
Sounds like you've done this so I believe you have a reasonable approach in place.
When should you throw a custom exception?
e.g. I have some code that connects to a server. The code that connects to the server throws an IOException when it fails to connect. In the context of the method it's called, this is fine. It's also fine in the network code.
But as this represents not having a connection (and therefore not working) the exception goes all the way up to the ui. At this stage, an IOException is very ambigous. Something like NoConnectionException would be better.
So, my question is:
At what stage should you catch an exception to instead throw another (custom) exception that better fits the abstraction?
I would expect exceptions to talk in terms of what I've asked the originating method to do. e.g.
read -> ReadException
connect -> ConnectException
buildPortfolio -> FailedToBuildPortfolioException
etc. This abstracts away what's going on under the covers (i.e. are you connecting via sockets etc.). As a general rule, when I create an interface for a component, I often create a corresponding exception or set of exceptions. My interface will be called Component, and my exceptions are usually ComponentException (e.g. RateSource and RateSourceException). It's consistent and easy to export to different projects as a complete component set.
The downside is that you create quite a lot of exceptions, and you may have to perform quite a lot of translations. The upside is that (as you've identified) you get little to no abstraction leakage.
At some point during the hierarchy of method calls (and thus exceptions) you may decide that no recovery can take place (or it's at an inappropriate place) and translate to unchecked exceptions to be handled later.
I know this is tagged as "language-agnostic", but I don't think it really is. Coming from a C++ perspective, I expect very few basic operations to throw an exception - the C++ Standard Library only uses exceptions in a very few places. So my own code is often the first place where exceptions can be generated. In that code, I like a very flat hierarchy - I don't want to be messing with hundreds of catch() clauses later in the code, and have never understood Java and C#'s apparent obsession with creating Baroque heirarchies of class and namespace.
So, for my C++ code - one type of exception, containing a meaningful error message, per library. And one for the final executable.
I think there are two questions hidden here:
a) When should one hide an exception behind a different exception.
b) When should one use a custom exception for this.
a) I'd say: when ever an exception travels across the border of two layers in the application, it should get hidden behind an exception that is more apropriate for the new layer.
Example: because you are doing some remote stuff, you get a ConnectionWhatEverException.
But the caller shouldn't be aware of Connection problems. Since he just wants to get some service performed, so he gets a ServiceOutOfOrderException. The reason for this is: Inside the layer, doing remoting, you might to do something usefull with a ConnectionException (retry, write into a backout queue ..). Once you left that layer, nobody knows how to handle a ConnectionException. But they should be able to decide, what do do, when the Service does not work.
b) When there is no matching existing Exception. There are a couple of useful Exception in Java for example. I use IllegalState and IllegalArgument quite often. A strong argument for a new exception class is, if you have some useful context to provide. For example the name of the service that failed could be an argument of a ServiceFailedException. Just don't create a class for every method call, or anything to that effect. 100 Exception classes aren't a problem, as long as they have different behavior (i.e. at least different fields). If they differ only by name and reside on the same abstraction level, make them one Exception, and put the different names in the message or a single field of that exception class.
c) At least in java there is the discussion about checked exceptions. I wrap those directly in an unchecked one, because I hate the checked kind. But that is more an opinion then advice.
Is there any case where you would get NoConnectionException which isn't caused by an IO issue? Conversely, is knowing whether the cause is IO based or not going to help the client recover sensibly?
When should you throw a custom exception?
I. When you can provide more (diagnostic) information.
Note: this additional information may not be available at the place where the original exception (IOException) was thrown. Progressive layers of abstractions may have more information to add like what were you trying to do which led to this exception?
II. When you must not expose implementation details: i.e. you want the (illusion of?) abstraction to continue.
This may be important when the underlying implementation mechanism can change. Wrapping the underlying exception in a custom exception is a good way of insulating your clients from implementation details (by lifting the level of abstraction)
III. Both I and II
NOTE: Furthermore your clients should be able to tune into the exact level of information they are interested in or rather they should be able to tune out anything they are not interested in. So it's a good idea to derive your custom exceptions from IOException.
What are the best practices for exceptions over remote methods?
I'm sure that you need to handle all exceptions at the level of a remote method implementation, because you need to log it on the server side. But what should you do afterwards?
Should you wrap the exception in a RemoteException (java) and throw it to the client? This would mean that the client would have to import all exceptions that could be thrown. Would it be better to throw a new custom exception with fewer details? Because the client won't need to know all the details of what went wrong. What should you log on the client? I've even heard of using return codes(for efficiency maybe?) to tell the caller about what happened.
The important thing to keep in mind, is that the client must be informed of what went wrong. A generic answer of "Something failed" or no notification at all is unacceptable. And what about runtime (unchecked) exceptions?
It seems like you want to be able to differentiate if the failure was due to a system failure (e.g. a service or machine is down) or a business logic failure (e.g. the user does not exist).
I'd recommend wrapping all system exceptions from the RMI call with your own custom exception. You can still maintain the information in the exception by passing it to your custom exception as the cause (this is possible in Java, not sure about other languages). That way client only need to know how to handle the one exception in the cause of system failure. Whether this custom exception is checked or runtime is up for debate (probably depends on your project standards). I would definitely log this type of failure.
Business type failures can be represented as either a separate exception or some type of default (or null) response object. I would attempt to recover (i.e. take some alternative action) from this type of failure and log only if the recovery fails.
In past projects we'd catch all service layer (tier) exceptions at the very top of the layer, passing the application specific error codes/information to the UI via DTO's/VO's. It's a simple approach in that there's an established pattern of all error handling happening in the same place for each service instead of scattered about the service and UI layers.
Then all the UI has to do is inspect the DTO/VO for a flag (hasError?) and display the error message(s), it doesn't have to know nor care what the actual exception was.
I would always log the exception within my application (at the server side as defined in your question).
I would then throw an exception, to be caught by the client. If the caller could take corrective action to prevent the exception then I would ensure that the exception contained this information (e.g. DateTime argName must not be in the past). If the error was caused by some outage of a third party system then I might pass this information up the call stack to the caller.
If, however, the exception was essentially caused by a bug in my system then I would structure my exception handling such that a non-informative exception message (e.g. General failure) was used.
Here's what I did. Every Remote Method implementation catches all Exceptions on the server side and logs them. Then they are wrapped in a Custom Exception, which will contain a description of the problem. This description must be useful to the client, so it won't contain all the details of the caught Exception, because the client doesn't need them. They have already been logged on the server side. Now, on the client, these Exceptions can be handled how the user wishes.
Why I chose using Exceptions and not return codes is because of one very important drawback of return codes: you can't throw them to higher levels without some effort. This means you have to check for an error right after the call and handle it there. But this may not be what I want.