9:14 Oh! Oh! Oh! Don't do that! ... Don't do that ... Application should be unaware of the representation layer .... So, don't use the http statuses for these ... It is a classic abstraction leacking ... I like your videos! Really like! I'm excited that you mention the Result vs Exception patterns (also known as Error (Code) vs Exception). But, please, don't use the http codes outside of the http-based representation layer.
I generally disagree with throwing exceptions in non-exceptional circumstances. However, in this example I more strongly disagree with throwing an exception in a service layer that contains an HTTP response code. You are assuming you will always use your service layer with a web-based API, and tying it to this usage. A service layer should be just that - services. An API layer, such as WebAPI in this case, should have the responsibility of returning the appropriate HTTP response code.
Please note that we’re talking about simple, small to medium applications that are mostly CRUD. I share your opinion for larger, more complex applications. For the smaller apps, personally, I’d choose the higher coupling, higher cohesion approach and refactor if needed
Another idea is to annotate the exception classes with a custom attribute. This could also be evaluated in the GlobalErrorHandler. With this other consumers can ignore the attribute and you don't "clutter" the exception classes with adapter specific values.
The application size is not a criterion to choose between exception throwing and result patterns. If we are talking about small applications, you can choose either one, but not both. If you are writing a large application, you should choose both, but for different reasons. For example, any class from the infrastructure layer should either: - return an error result expressed in domain terms, or - throw an exception expressed in infrastructure terms. The error results pass through all layers with explicit handling. Sometimes, they can cause an exception, indicating an unexpected assumption mismatch (or failed assertion). On the presentation layer the error results must then be translated into messages for the client according to the client interface. Exceptions pass through all layers via automatic propagation and reach the error boundary in the client interface layer. This error boundary is responsible for reporting (logging) the error and sanitizing its content before the error is presented to the client. Using exceptions to model error results leads to unnecessary try ... catch blocks and re-throwing of unhandled exceptions, because typically any method returning an error result could also throw an exception that comes from the underlying layers.
Hello and thank you for this informative video! I have a question regarding the UseGlobalErrorHandling middleware. How can we adapt the exception handling to differentiate the information returned based on whether we are in a development or production environment? Do you have any advice or best practices on this topic? Thank you in advance for your response.
You can register different error handling middleware depending on the environment. For example: if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/error"); }
Sorry if its a silly question but how come did you make a new propperty for the error message, and not use: public class ServiceException(int statusCode, string message): Exception(message: message)?
One thing I disagree with is ever returning a 404 on a List (collection) endpoint. I'm not sure why your List asked for a product Id. But a list should return an empty collection if no resources match the predicate.
Exception should only ever be used IF AND ONLY IF ITS THE ONLY OPTION. Exceptions are incredibly memory and processing intensive and should be reduced as much as possible so even suggesting to use it just because it’s simpler is one heck of a quick way to make people fail an interview
Both approaches are good and valid. Personally, if I get to choose, I return 404 for *parent* resource not found, and 200 ok and empty list for collection not found/empty
Using exceptions for logic branches that you KNOW how to handle without exceptions, such as by returning a suitable response, is not a good idea. It creates a different user experience in most cases, and it creates far more overhead in terms of error logging, monitoring, unit testing, etc. Nothing worse than a unit test, or functional/integration test, that has to say "did this throw an exception?" .. "yes, test passed" ... just stupid :)
I generally agree and have videos on the channel relaying this exact point. The truth is in the middle. Both approaches have great benefits and drawbacks. I’m demonstrating what I would personally use for a small-medium, mostly CRUD app when looking to get things up and running as fast as possible.
What I can't really buy on the cons of Exceptions is the cost of them, sure, it has some resource cost, but who designs a product to do the bad incorrect stuff all the time? There's supposed to be validation on input on the frontend, the one on backend is just a safeguard, so will have no Exceptions there if the frontend has done it's job, so then the only exceptions coming are the ones where something goes bad during processing?
When using exceptions for flow control, by definition you’re using exceptions for flows that are not exceptional. Depending on the scale and requirements of the app, this can be significant
Not really, if you look closely, it's as much code as the exception approach. But I prefer this approach because it's more readable (the return type tells you everything about the method)
@@parlor3115 Not really, since in every endpoint you will need to add code to handle if you should return the result, or the error if there is anything. Whilst a global exception handler is just that, global, so you do not have to do anything special for it to work. So requiring 5 to 10 extra rows of code is not as much code as no extra rows. *Edit* And then we have the extra try+catch code on top of that...
@peth You only handle at the level you want to access the success data in and you have to handle either the exception (try catch) or the return. Otherwise, you'll just let the return propagate as is. And in case of generic and expected errors (validation, record not found, ...), then you'll return those at the last step (at the action method), where an after middleware is dispatched that will check the response and handle it accordingly just like a global error handler. With this, you'll be right the same amount of code while saving on performance.
Are you also responsible for the client-side development? If so, consider creating a shared project to house a mapping system and resource files. The API will return a code indicating a problem, which will act as a key in the resource file. This approach will facilitate easy management of multiple languages.
That's funny, I completely and fundamentally disagree. IMO, if you have a simple application, you can use return codes, but complex applications with lots of shared code and deep stacks of function calls benefit enormously from Exceptions. Exceptions simplify error handling and code readability MASSIVELY over returning error codes. Exceptions were a response to return codes' lack of scalability because return codes require that EVERY time a function is called, whatever calls it has to have logic to handle the error condition right there. Exceptions allow you to simply pass the error up the chain until something that cares or knows what to do will actually handle it. Further, exception handling is done outside the general flow of logic so someone reading the code can actually understand what is 'supposed' to be happening and there's no chance that anything UP the stack is going to continue executing in a faulty state. On top of that, IMO, it's a TERRIBLE idea to have multiple outputs from a given function. It's bad enough to have the possibility of null or an object, but to then throw in the possibility of a third type to check and unpack the result of every function call? How is that scalable? It's a rat's nest of error handling! That's the beauty of an 'out' parameter is you can have the function return a specific type of result and then use the object if it's valid or else handle the case of it not being valid. The other great power of an Exception is that you can add context as the stack unwinds at the levels where it makes sense. Return codes can only change the context and you might lose vital information about what happened in the error situation. You're basically left with the choice of a user being told that there was a divide by zero error OR that the review didn't get added with no information about WHY. For instance, in a complex application a UI action will likely start a cascade of function calls splitting off into different directions to handle the different subtasks associated with with whatever the user just asked for. An exception deep in the bowels of that can say there was a divide by zero error, but then that can propagate to a failure to generate a star rating value which can then be caught closer to the UI level and told that the review couldn't be added. When you're building the message for the user and the message for the log, you'll have different info you can use to generate useful information in both contexts right at your fingertips without generating multiple log entries that you have to then piece together. IMO, you should throw Exceptions if the code you're in can't handle the current logical situation. For instance, adding a review to a product that doesn't exist. But if the code you're in CAN handle the situation, like perhaps you're adding an item to a list of items that share some property, but nothing has been added with that particular property yet, you should create the list for those items--no need for an exception or return code. Not using exceptions and relying on return codes is going backwards. And returning multiple data types from a function has always been a really bad idea from a readability and usability standpoint. The only real use case for return codes is in headless programs or highly performant libraries; but you better be sure the tradeoff in readability and debuggability is worth it.
Thanks for taking the time to share your thought and experience. My opinion around this topic is shaped from suffering bad implementations of exceptions for flow control in numerous large complex projects within Microsoft. I elaborate my opinion in more detail in this video: th-cam.com/video/2hiKyFi1mgI/w-d-xo.html In any case, drawbacks and benefits to both approaches. They can both be implemented well and horrible. Plus, personal taste etc'.
@@amantinband I want to add some text to this big comment. In big applications we ussualy work with diffrent nuget-packages, call low-level code of c/c++ dll from c#, send http-queries and so on. Often all this stuff generate exceptions (and some authors of libraries say that exception handling is the normal flow of using their packages). So in that case you need handle both: error codes and standard c# exceptions. These approaches lead to complicated error handling, lots of exception hadling code and of course bugs. And last but not least - your entire architecture will strong link with lib. (I hope you know that authors can change package license, stop maintenance and even do what happened to the package "moq") P.S. This is not about you Amantin))
Simple and good analysis of what approach to choose.
9:14 Oh! Oh! Oh! Don't do that! ... Don't do that ...
Application should be unaware of the representation layer .... So, don't use the http statuses for these ...
It is a classic abstraction leacking ...
I like your videos! Really like!
I'm excited that you mention the Result vs Exception patterns (also known as Error (Code) vs Exception).
But, please, don't use the http codes outside of the http-based representation layer.
I generally disagree with throwing exceptions in non-exceptional circumstances.
However, in this example I more strongly disagree with throwing an exception in a service layer that contains an HTTP response code. You are assuming you will always use your service layer with a web-based API, and tying it to this usage.
A service layer should be just that - services. An API layer, such as WebAPI in this case, should have the responsibility of returning the appropriate HTTP response code.
Please note that we’re talking about simple, small to medium applications that are mostly CRUD.
I share your opinion for larger, more complex applications.
For the smaller apps, personally, I’d choose the higher coupling, higher cohesion approach and refactor if needed
@@amantinband especially if we are talking about small CRUD application, we should not use http-codes outside of the http-based representation layer
Thank you very much. Maybe Next Video how you would log and which framework you would use.
It’s coming
Another idea is to annotate the exception classes with a custom attribute. This could also be evaluated in the GlobalErrorHandler. With this other consumers can ignore the attribute and you don't "clutter" the exception classes with adapter specific values.
The application size is not a criterion to choose between exception throwing and result patterns. If we are talking about small applications, you can choose either one, but not both. If you are writing a large application, you should choose both, but for different reasons.
For example, any class from the infrastructure layer should either:
- return an error result expressed in domain terms, or
- throw an exception expressed in infrastructure terms.
The error results pass through all layers with explicit handling. Sometimes, they can cause an exception, indicating an unexpected assumption mismatch (or failed assertion). On the presentation layer the error results must then be translated into messages for the client according to the client interface.
Exceptions pass through all layers via automatic propagation and reach the error boundary in the client interface layer. This error boundary is responsible for reporting (logging) the error and sanitizing its content before the error is presented to the client.
Using exceptions to model error results leads to unnecessary try ... catch blocks and re-throwing of unhandled exceptions, because typically any method returning an error result could also throw an exception that comes from the underlying layers.
3:37 I concur. In domain-centric, i.e. non-CRUD applications/modules, exceptions don't fit well.
Great Videos, Please can you do one on Composition and Or with Strategy Pattern, I have been struggling with these for months.
Hello and thank you for this informative video!
I have a question regarding the UseGlobalErrorHandling middleware. How can we adapt the exception handling to differentiate the information returned based on whether we are in a development or production environment?
Do you have any advice or best practices on this topic?
Thank you in advance for your response.
You can register different error handling middleware depending on the environment. For example:
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
Sorry if its a silly question but how come did you make a new propperty for the error message, and not use:
public class ServiceException(int statusCode, string message): Exception(message: message)?
I do like ErrorOr I use it in all our projects
One thing I disagree with is ever returning a 404 on a List (collection) endpoint. I'm not sure why your List asked for a product Id.
But a list should return an empty collection if no resources match the predicate.
If it's up to me, I choose 404 for parent resource not found, but both are fine and valid approaches
@@amantinband Oh so you are listing some child resources of a product. I guess I could see returning a 404 in the case of the missing product.
Exception should only ever be used IF AND ONLY IF ITS THE ONLY OPTION. Exceptions are incredibly memory and processing intensive and should be reduced as much as possible so even suggesting to use it just because it’s simpler is one heck of a quick way to make people fail an interview
Sounds like you’ll enjoy this video: Exceptions are evil. This is what I do instead.
th-cam.com/video/2hiKyFi1mgI/w-d-xo.html
So is this all t here is to this series? Seems like there's a lot more to cover.
Why not just return either the record, or 200 OK with empty list/no record, if it was not found? Is that not okay?
Both approaches are good and valid. Personally, if I get to choose, I return 404 for *parent* resource not found, and 200 ok and empty list for collection not found/empty
Using exceptions for logic branches that you KNOW how to handle without exceptions, such as by returning a suitable response, is not a good idea. It creates a different user experience in most cases, and it creates far more overhead in terms of error logging, monitoring, unit testing, etc. Nothing worse than a unit test, or functional/integration test, that has to say "did this throw an exception?" .. "yes, test passed" ... just stupid :)
I generally agree and have videos on the channel relaying this exact point.
The truth is in the middle. Both approaches have great benefits and drawbacks. I’m demonstrating what I would personally use for a small-medium, mostly CRUD app when looking to get things up and running as fast as possible.
What I can't really buy on the cons of Exceptions is the cost of them, sure, it has some resource cost, but who designs a product to do the bad incorrect stuff all the time? There's supposed to be validation on input on the frontend, the one on backend is just a safeguard, so will have no Exceptions there if the frontend has done it's job, so then the only exceptions coming are the ones where something goes bad during processing?
When using exceptions for flow control, by definition you’re using exceptions for flows that are not exceptional. Depending on the scale and requirements of the app, this can be significant
I found ErrorOr Adds a lot of unnecessary code
That's the main drawback of being explicit
Not really, if you look closely, it's as much code as the exception approach. But I prefer this approach because it's more readable (the return type tells you everything about the method)
@@parlor3115 Not really, since in every endpoint you will need to add code to handle if you should return the result, or the error if there is anything. Whilst a global exception handler is just that, global, so you do not have to do anything special for it to work. So requiring 5 to 10 extra rows of code is not as much code as no extra rows. *Edit* And then we have the extra try+catch code on top of that...
@peth You only handle at the level you want to access the success data in and you have to handle either the exception (try catch) or the return. Otherwise, you'll just let the return propagate as is. And in case of generic and expected errors (validation, record not found, ...), then you'll return those at the last step (at the action method), where an after middleware is dispatched that will check the response and handle it accordingly just like a global error handler. With this, you'll be right the same amount of code while saving on performance.
Using try catch is out dated way?
How would you translate the error messages on apis with multi language support?
How would you do that with exceptions?
I would probably create a service class that returns a message according to the language. And have message templates.
@@tchial0 there's no difference in this
Are you also responsible for the client-side development? If so, consider creating a shared project to house a mapping system and resource files. The API will return a code indicating a problem, which will act as a key in the resource file. This approach will facilitate easy management of multiple languages.
@@krccmsitp2884 Let's say (or code, lol): "var errorMessage = cultureService.GetErrorMessageById(errorMessageId, param1, ...)"
source code please
Hey Mahmoud, the source code is available for patrons and members. The description has all the relevant links
That's funny, I completely and fundamentally disagree. IMO, if you have a simple application, you can use return codes, but complex applications with lots of shared code and deep stacks of function calls benefit enormously from Exceptions. Exceptions simplify error handling and code readability MASSIVELY over returning error codes.
Exceptions were a response to return codes' lack of scalability because return codes require that EVERY time a function is called, whatever calls it has to have logic to handle the error condition right there. Exceptions allow you to simply pass the error up the chain until something that cares or knows what to do will actually handle it. Further, exception handling is done outside the general flow of logic so someone reading the code can actually understand what is 'supposed' to be happening and there's no chance that anything UP the stack is going to continue executing in a faulty state.
On top of that, IMO, it's a TERRIBLE idea to have multiple outputs from a given function. It's bad enough to have the possibility of null or an object, but to then throw in the possibility of a third type to check and unpack the result of every function call? How is that scalable? It's a rat's nest of error handling! That's the beauty of an 'out' parameter is you can have the function return a specific type of result and then use the object if it's valid or else handle the case of it not being valid.
The other great power of an Exception is that you can add context as the stack unwinds at the levels where it makes sense. Return codes can only change the context and you might lose vital information about what happened in the error situation. You're basically left with the choice of a user being told that there was a divide by zero error OR that the review didn't get added with no information about WHY. For instance, in a complex application a UI action will likely start a cascade of function calls splitting off into different directions to handle the different subtasks associated with with whatever the user just asked for. An exception deep in the bowels of that can say there was a divide by zero error, but then that can propagate to a failure to generate a star rating value which can then be caught closer to the UI level and told that the review couldn't be added. When you're building the message for the user and the message for the log, you'll have different info you can use to generate useful information in both contexts right at your fingertips without generating multiple log entries that you have to then piece together.
IMO, you should throw Exceptions if the code you're in can't handle the current logical situation. For instance, adding a review to a product that doesn't exist. But if the code you're in CAN handle the situation, like perhaps you're adding an item to a list of items that share some property, but nothing has been added with that particular property yet, you should create the list for those items--no need for an exception or return code.
Not using exceptions and relying on return codes is going backwards. And returning multiple data types from a function has always been a really bad idea from a readability and usability standpoint. The only real use case for return codes is in headless programs or highly performant libraries; but you better be sure the tradeoff in readability and debuggability is worth it.
Thanks for taking the time to share your thought and experience.
My opinion around this topic is shaped from suffering bad implementations of exceptions for flow control in numerous large complex projects within Microsoft. I elaborate my opinion in more detail in this video: th-cam.com/video/2hiKyFi1mgI/w-d-xo.html
In any case, drawbacks and benefits to both approaches. They can both be implemented well and horrible. Plus, personal taste etc'.
@@amantinband I want to add some text to this big comment. In big applications we ussualy work with diffrent nuget-packages, call low-level code of c/c++ dll from c#, send http-queries and so on. Often all this stuff generate exceptions (and some authors of libraries say that exception handling is the normal flow of using their packages). So in that case you need handle both: error codes and standard c# exceptions. These approaches lead to complicated error handling, lots of exception hadling code and of course bugs.
And last but not least - your entire architecture will strong link with lib. (I hope you know that authors can change package license, stop maintenance and even do what happened to the package "moq")
P.S. This is not about you Amantin))