I have a three layers C# software : UI, Business, Database.
I have some business rules that I want to implement into my business layer. For instance, if two objects have the same name, the business layer needs to throw an exception to the UI.
My problem is that my application is multi language, and my business layer don't have access to my resource files. So my UI needs to catch the exception from the business layer, and decide what message it will show to the user.
I was thinking about creating a BusinessException class, with a property who tells the UI which key to take in the ressource file. Do you thing it a good way to do it? Or do you have better ideas? Thank you!
The preferred solution is to create different exception types that represent the different errors, add any important data as properties to the exceptions and let the UI handle the user facing error messages.
This is ideal if you have a separate UI design team that wants to handle the text displayed to the user, including error messages. To be frank, developers tend to write good error messages for other developers but not for users.
Otherwise, embed some form of message ID in the exception that the UI can look up (as you suggest) or localize the error messages in the business layer.
I ended up with a mix of those two solutions. So I'm using only one class : BusinessRuleException. This class as a property "rule" which is a enum of all my business rules.
When my UI receive a BusinessRuleException, it can catch it, and than compare the "rule" to a ressource file and get a friendly message for the user. If the UI can't find a translation, it will only send the rule as is. So the user still have a change to understand what is going on.
It don't like the solution of a different exception for every different business rule, because it will end up with so much extra code, which doesn't help you understand what is the real work you class is doing.
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'm learning about layered architecture at the moment and I'm wondering how to add a logging system to such a design.
Now let's say we have three layers:
Presentation Layer
Business Layer
Data Access Layer
And assume that only a higher level layer is aware of the layer one level below. For example, the Presentation Layer is aware of the Business Layer but not the other way around.
Where should you implement a general logger class?
If I implement it in a different project, it means all the layers have a dependency on a common assembly, which may or may not be good. Though this can be overcome with dependency injection.
If I implement it in the highest level (in our case the Presentation Layer), it will defy the Single Responsibility Principle.
What is a good place to implement a logging mechanism?
And after implementing it, what is a way to use such a system?
It should ideally be able to catch uncaught exceptions and save the exception description somewhere.
Where should you catch exceptions? Should they be caught in the highest layer (the Presentation Layer)? Or should they be caught somewhere else?
And what is the way to use to pass a logger to a class? Does it make sense to add a method/constructor overload to everything in the project that accepts an interface like ILogger?
As you can see I'm pretty confused about the subject, and in my current job there's no one that has any knowledge about enterprise application design / layered design, even though they are designing enterprise applications. So any help showing me the right direction will be appreciated.
Logging is a cross-cutting concern. This means that it encompasses all layers of your architecture, and it makes sense to implement it as a separate library. However, this would only make sense as an exercise, since there are already very good solutions like Log4Net, NLog, and even .NET's own TraceSources.
I tend to prefer those which support hierarchical logging (e.g. log4net). This make it much easier to configure the desired tracing level in production systems. E.g. you could set general tracing level for MyApp.SomeNamespace to Warning, but also have set a specific type like MyApp.SomeNamespace.AnInterestingClass to Debug.
I am not sure I understood the "what is a way to use such a system" part.
You use logging everywhere it is needed, in all layers of your app, in each method which needs it. I am under impression that you have an idea of centralized place where all errors are handled and logged, but these are separate things.
It should ideally be able to catch uncaught exceptions and save the
exception description somewhere.
No, it shouldn't. Loggers write stuff to logs, they don't handle exceptions. Logging is not used only for reporting errors. You also want to log the execution of your application and many internal information (but with varying tracing levels), for the sake of troubleshooting the system in production or post mortem analysis.
Where should you catch exceptions?
At all levels. Many methods in your code will be handling the exceptions relevant to current context. I suppose that you really want to know where to handle the exceptions which were not caught elsewhere - some kind of catch-all handler. For this, often it makes sense to do it in the topmost layer, i.e. in your .exe or, more generally, in the layer which contains the types which represent the application itself. There are many ways to do it - from simply registering the handlers for the unhandled exceptions (ThreadException/UnhandledException) to HandleError/Application_Error in ASP.NET MVC to using something like exception handling application block, which I personally dislike (as most of Enterprise Library).
And what is the way to use to pass a logger to a class? Does it make sense to add a method/constructor overload to everything in the project that accepts an interface like ILogger?
It depends on your implementation. It seems that you want to go down the dependency injection path. Since logger is not an essential dependency (i.e. it is not related to functional behavior of types, but to the implementation), I would prefer to handle it via property injection as an optional dependency, instead of doing it via constructor which, IMO, should be used only for primary dependencies - those which are required for the type to function properly.
However, you might not want to use DI. Then you need some other way to get to logger. One option is discussed here.
I have a flow chart that I'm implementing and it has 4 or 5 paths through it depending on user input and the results of some processing. Naturally, I don't want all this logic this in my Windows form, I just want to call a method on the class in the form. Is it bad design to have my business logic class reference System.Windows.Forms and show dialogs and MessageBoxes to get the input it needs to process and return a result?
Yes, this is bad design. Your class should offer a mean to communicate with the form and get data back. Just create events and let the Form subscribe to them, getting the information to create the dialogs from a custom EventArgs class. After it gets the input, just push the same class back with the additional information via a second event.
This should resemble the MVP pattern.
Yes, this is a bad idea. You are effectively coupling your business logic very tightly with the presentation. You (probably won't) be able to re-use business logic easily under other circumstances, and you won't be able to replace the UI without touching the business logic.
You need to have the UI and business logic layers communicate, and let the UI layer handle, well, the UI.
I think it's bad design. When you separate components of your application, a rule of thumb is to keep them separate enough so that you can run them on different computers.
Yes. Because it means your busienss object is simply not a business obejct.
Use a MVVM pattern and put the logic into the viewmodel.
It's bad design, because you may need to run your business logic in a situation with a different UI or no UI at all (such as in a server, or a batch process). That's what separation of business logic and UI is all about. If possible, it's better to get all the necessary user input up-front in the UI class before handing things off to the business logic. However, if it's necessary to have the business logic prompt for more information, then it's better for the business logic API to take a callback method delegate, which it can call to request the further input. Then the UI layer can decide how best to request from the user.
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.