I have a question about the clean architecture I use to write my project, I need to tackle the subject of exceptions.
In the project I am using CQRS which is located in the application layer and which orchestrates the whole operation and component connection. In the domain layer, I have domain objects that have some logic, if there is an error this layer throws an exception BusinessRuleValidationException, or NullReferenceException in this layer I only use these 2 exceptions, but now I have an infrastructure layer which, if necessary, also throws exceptions, but there are more of them in this layer and they will probably continue to grow as the application develops.
Now the question is how to solve this problem, which are exceptions that I wouldn't want to keep remembering about handling every single exception inside api using exception middleware. My idea is to leave the exceptions in the domain layer as they are, because there are only 2 and write a decorator to MediatR that will catch exceptions from the domain layer only and make them consistent across the api, i.e. add a unified response so that exceptions are not sent to the user from its internal message, only consistent for everyone, in the form of Result using the Fluent Results bundle. Instead of throwing exceptions in Infrastructure, use a Fluent Results package that will return errors instead of exceptions and eventually return Result from all MediatR Handers. I would not like to use Fluent Result inside the domain layer because it is kind of breaking the rule that the domain layer doesn't have any references to other things.
The next question, if I decide to solve the problem in one way or another, the results of errors from the Infrastructure should immediately contain an error code such as Api should return to the end user, e.g. if occurs an error because it is impossible to connect to the database, the infrastructure should return, the message (Result) under the title "Could not connect to the database" with the code "500", or rather this error code should be returned from the api itself, ie the controller gets an error (Result) and it decides what code to give to this error.
Thanks for help :)
Exceptions aren't a problem that you need to solve.
Exceptions are there to say something unexpected has happened, and there are many many things that can go wrong: incorrect input, timeouts, not authorised, not authenticated, missing data, etc, etc.
You can structure your code, where it reuses .net exceptions as much as possible, and if you have to add something that is unique to your business domain, you put that in the layer that throws those exceptions. If this happens at the networking level, you put exceptions there. If this is domain logic validation - you can keep it in the domain core.
Consuming code may or may not catch the exception. If it catches the exception, the code import the exception from the current layer or the layer below.
PS: don't throw null reference exceptions
Related
We are creating a new .Net Core Library, an application service that resides within a Clean Architecture.
The new service will read new customer details via the RepositoryService layer and Post them via a RestApiService Layer to multiple systems including : CreditCheck system, Billing system etc..
Within the new Application Service we want a consistent way of handling responses from RestAPi service. Response data includes:
- Return values: entities returned by restAPI service
- Exceptions like an error 500, time outs.. that have bubbled up from the RestAPi.
- Data Errors messages such as Customer already exists, bank details invalid
- Warning messages ... "Processing continues as normal, but order is flagged"
It is the last two that are foxing us, because this is different to the interaction with the Repository layer which has no business logic, so basically returns a success or an exception.
Microsoft and SOLID virtually state that the use of exceptions handling is the way to go , whether it be exceptions, errors or warnings.
But in this scenario not clear how this will work ..
a. We loose the option of handling and forwarding on the return values.
We really don't fancy storing all this in the exception message
- Whilst not a show stopper, we fear the code will be more difficult to read than it needs to be.
- Exception handling is expensive,but not worried too much on this score with number of transactions.
We are drawn to some how using FluentValidation or a hybrid version, it will need to work with Logging and RepositoryService as we will need log and decode stuff .
We really don't fancy repeating the RestAPi Service layer approach i.e. handling HTTP exceptions separately, then processing return values which are basically extended entities with Errors Status , Error codes and messages.
We are not expecting the above to be specific to our company. The issue seems to be around handling messages from a 3rd Party Business logic layer , within our Application Service.
So our question is how can we best handle Errors warnings along side data in the application service layer and still have SOLID testable and maintainable code ?
Microsoft and SOLID are right.
The correct way to go are Exceptions as per standard practices (and c#), independently of another considerations, like performance for instance.
Generally speaking there are two different types of "Errors", technical, and business logic related.
Failing to Connect to a DB, receiving a 500 from a REST Service, etc... are technical, and as they could be transient you can try to recover from this situation, sometimes without success, what finally causes the Business Orchestration/Process failure.
Business Logic errors, like 'Customer already exists', 'Bank details invalid','Input data is not in the valid format' are non-transient, and determined solely by Business Rules (some implicit, other explicit) and will stop stop your process as well without possibility to recover, simply because something is not in the proper/expected state.
There is a reason we use Exceptions (as technical artifacts) and is to handle these hard-stops 'properly'.
Every time you throw an Exception the application traverse back the stack up to the first available Exception Handler able to handle such Exception, returning the control to you and to a known location where something will happen (telemetry, rethrow, show a dialog to the user...)
Any mechanism trying to substitute this propagation (Error) must rely, for instance, on hijacking the return value of the methods to provide back a status, or forcibly include an out parameter to all your methods signatures, which will have awful collateral effects in your implementation.
Yes, sometimes your current design look 'too flat' that you are tempted to hijack the return value here and there, creating a highly coupled component, but you never can tell how the system complexity will grow, meaning that at some point you will extend your system with additional layers, and most likely the 'substituting mechanism' approach will not fit , and you are forced to support them, the regular way and the imaginative one.
So, trying to implement a custom solution will create a tightly-coupled technical requirement that must be supported all across the board, what in architecture terms is simply 'not good' (not a good practice)
IF any Service you are consuming is not able to produce a meaningful 'error' information, well structured, then, the Service is not well designed, and the problem flips to the Client side (Talking in SOA terms).
The only 'most correct' solution left to not introduce chaos in your Client is by creating a Proxy to the Service RESPECTING the rules required by your implementation approach.
My recommendation about Error Handling is very simple, stick to the rules that are well know and have been in place for decades and do not try workaround this by yourself, or you are going to face too many issues, create a Proxy for each service and integrate them properly in your code base (unit testing and such)
In regards to warnings, there is no recommendation from anybody to handle this by using Exceptions, those 'warnings' in your question is a normal output when interacting to the Service/a logical state supported by the Business Logic, thus, the place to handle this state is in the Data Contract coming back from the Service, again, if the Service is so horribly designed that is replying you with just a text string containing the word 'warning', and you are forced to parse this string to notice the situation, then your Proxy to the Service should check for it and provide a meaningful and well structured output to the Client.
I am building a Data Access Layer which is going to be used in two types of applications.
Applications that do not care about the specifics of the error. If an exception occurs then most probably is going to just get logged and the user may not be aware of it.
Example: Simple barcode stock taking application. The user inputs the barcode and if a connection is available to the database, then the system provides some extra info, if not then only the barcode is recorded locally. In this case I do not want detail exception handling.
Applications where I care a lot about the details of the exception.
What strategy do I have to follow when building my DAL in order to accommodate both categories?
Right now I am building an application from the first category and all I have done in my DAL methods is to just let the exception bubble up to the presentation layer, where I have several try..catch blocks in order to make the simple handling of logging, letting the user be unaware of he error.
Error handling is geting information from the part of code that knows to the person who needs to know. The DAL knows how to pritty print the trace of, say, a SqlException, SqlCommand and the SqlParameters collection. But the UI knows the entire call stack that caused the exception. The user probably doesn't know what to do with the information, so you should log to a separate channel, such as email to the developer or a log in the database.
I would also recommend using a real application (or several real applications) as tests of your library if you are writting an error logger. For example, you could wire up your error logger to a variety of apps on codeplex and see what the pain points are in troubleshooting bugs, or try dogfooding and using your error logger to log errors is your own library.
I have an n-tier web application, and I want to catch a specific type of exception in every method coming from the DAL (Data access layer) and rethrow it as a new exception of a specific type.
There are many methods in my DAL, so I don't want to start wrapping each one with try/catch.
I think this is possible using the Exception Handling Application Blocks, but i couldn't find any good documentation of how to do this...
I'm not familiar with the previous versions of the application blocks neither.
Has your DAL repositories got an interface?
I would implement the interface using a decorator pattern.
All the decorator does catches the exception and then builds a new exception and throws that out to the upper tier
As a point of note, in our n-tiered applications we always let exception just get thrown naturally and catch them in once single place and log them. We only create specific exceptions if we absolutely have to and that would be rare enough.
The reason for this is maintainability of code. Code can easily become unreadable when try/catches exist everywhere.
If I have a multi-layer Winform app with a Presentation, Business and Data Layer for example, and I encounter an error in either the Business Layer or Data Layer for which the only logical action is to log the error and inform the user that an error has occurred where should the logging take place?
Should I put the the methods in the Business and Data Layers in try catch blocks like so
try
{
DoSomethingThatMightGiveErrors();
}
catch(Exception ex)
{
logger.log(ex.ToString());
throw;
}
Or should I just let the errors bubble up to the presentation layer and handle the logging and informing the user there?
I'd put the logging at the business layer level and then rethrow the error. If this layer is used in another project in the future, it's already doing the logging. Rethrowing the exception allows consumers of this layer to convert the error into a friendly message.
EDIT: I think it depends a bit on the kind of logging: if you're logging to a central database, which is independent from the UI, put the logging in the business logic layer. If the logging is specific to the UI, for example writing a log file to the application's directory, put it in the UI.
If you are talking about unhandled (non-business related) exceptions just let them propagate to the UI layer where you could catch/log/inform the user.
My preference would be to place it in the business layer.
If you ever change the nature of
the presentation layer (i.e.
winforms to webforms) the logging
code won't need to be duplicated.
All of your logging will be a lot easier to find and maintain, as you can always scan the business class's list of methods and inspect/tweak them for logging. If you put the logging in your presentation layer, the logging calls will be scattered all over the place - a single business class could have logging calls made in many different presentation classes.
That depends on your requirements, like, for instance, do you want to put your users off your application as a result of errors showing up via bubbling up to the presentation layer? How often would these errors occur in unexpected situations?
This is a loaded question and every application is different, the most basic thing I can say is to use the try catch clauses in the business/data layers and ensure that you inform the users of certain situations where an error could be expected (You do have this in documentation?)
Other than that, check with the requirements and feedback from end users...if you allow the errors to appear on the presentation layer, the worst case is the user will refuse to work with it as a result of errors spewing out...
Hope this helps you,
Best regards,
Tom.
What is the best practise for returning an error of a business rule in a BLL? SHould I just raise exceptions and catch them in the presentation layer, shoudl I return some kind of object that holds any exception type info?
The word "returning" is tricky here.
The primary virtue of multi-tier design is orthogonality. You should be able to call the classes in your BLL from an entirely different UI than the one you're currently using and handle logging completely differently.
If an exception is able to be handled without user intervention or notification, you should generally do so within the BLL. If the exception needs to be brought to the user's attention or logged, let it bubble up to the UI, which can implement notification and logging without building such things into the BLL.