Is it done to inform client?
Precisely that.
Wouldn't that cause bad user experience?
"Client" is not synonymous with "end user". The only exceptions that reach the end user are called unhandled exceptions. Exceptions bubble up the call stack, but they should be handled (at least once) at some point, and they should not bubble up to the end user.
I really don't get the point why we throw the same thing when we are inside it already
Because it's not just a matter of whether it's handled or not, but also who handles it.
In complex codebases, one operation (e.g. "buy this product") can be orchestrated as a sequence of multiple operations (e.g. confirm availability in stock, make order, process payment, packaging, schedule delivery). In a properly loosely coupled codebase, those suboperations have no direct knowledge of the "parent" operation that they belong to. Therefore, when the suboperation handles the exception, it's not able to account for how the parent operation wants it to be handled.
So instead, the suboperation lets the exception bubble further, and then it's the parent operation's job to catch the exception again and handle it the way it wants to.
For example, the credit card payment suboperation may just report the error to the payment platform provider, whereas the "purchase product" parent operation will then decide to send the customer an invoice instead since the direct payment didn't work.
It really helps to think of this as a company with humans. Just like your layer communicate with one another, the employees of a company communicate with one another. So let's use an example.
Manager
has instructed Driver
to deliver Parcel
to Customer
, and provided him with the key to a specific Truck
(truckId
). As it turns out, there is no Truck
in the parking lot which fits that key. That is the equivalent of the exception being thrown.
As is customary, the Driver
makes an official report of this to the police. This is the equivalent of writing to the log.
Okay, the report has been logged. Is that the end of it? Because don't forget that the Manager
is still in the office, thinking that the Customer
will receive their Parcel
.
Should the Driver
still go back to the Manager
to explain that he couldn't fulfill his task?
This is where you have options:
- Maybe the driver simply explains precisely what happened (= rethrow the same exception)
- Maybe the driver isn't allowed to explain internal logistics, so they simply explain that the delivery can't be done (= throw a new exception)
- Maybe the manager doesn't quite understand internal logistics, so they simply explain that the delivery can't be done and if the manager asks about it they can explain precisely what happened (= throw an exception, with the original exception as inner exception)
But what you're suggesting, i.e. not even continuing throwing an exception because it has already been handled, is more akin to:
- The driver doesn't tell the manager that the parcel was never delivered.
That's not to say that it is inherently wrong to do so, but it is very contextual whether it is wrong or not, and it's more often correct to not swallow the exception.
Now, as is the case for most analogies, this is of course oversimplified. But it highlights the main purpose: alerting the consumer (i.e. those higher ups on the call stack) that the expected outcome has not been achieved, so that those higher ups are then able to respond to this issue however they choose.
The reason you need to let it bubble up is because the "lower" handler might not have all the necessary contextual information that the "higher up" handler does.