Thoughts on the blues - once again about exception handling
Already written a lot about exception handling in C #, it is written well and in some places in detail, but I will try to make my modest contribution to this issue. This article is simply an attempt to better comprehend and systematize within the framework of one, even very conditional concept, possible approaches to the problem. The good practice of exception handling, in my opinion, is covered rather poorly and does not create a complete picture when, how and where to work with exceptional situations in the code.
It would be nice for the developers from the very beginning of the project to keep in mind a certain “road map” that would cover all stages of development. “Death is part of life,” Mom told Forest Gump, contingencies are part of the regular functioning of the system, for this reason, consider the possibility of these very contingencies, classify them, start working with them already at the design stage, adequately reflect in the code base, and thereby organically woven into the structure of the project, it seems to me as important as the implementation of the entire set of full-time functional.
As usual, first try to define the terms. Take a look at MSDN - “ The C # language's exception handling features provide a way to deal with any unexpected or exceptional situations that arise while a program is running. ” A very interesting place in the wording - unexpected or exceptional situations. At first glance it sounds like synonyms, but if you think about it, the difference is quite palpable - an exceptional situation during the execution of a certain use case, and some kind of system behavior, the “system use case”, which in itself is an exceptional situation. I will explain in more detail what I mean. The standard design workflow begins with use case diagrams, which, roughly speaking, project nouns from the specification onto actor entities, and verbs on use case diagram entities. And, with a careful approach to the formalization of the scheme, the entry of the system into each new state (illustrated by the use case), and exit from it must occur in compliance with the fulfillment of preconditions and postconditions. For each state, this set is unique, a common feature is that the implementation of this entire set ensures that the system is in the correct, consistent state.exceptional situations .
Further, it is simply necessary to mention such a wonderful concept as the “samurai principle”. Its essence is to get the most clearly determined, predictable code behavior. Using ArgumentException, we have a semblance of a filter that will filter out an incorrect set of input data (also a variation on the topic of preconditions), further along the code, all dubious places are also surrounded by exceptions, so that the code crashes as beautifully and predictably as possible. This technique already refers rather to the encoding stage, let's say, to the most secure implementation of the provided functionality. Here it is already much more appropriate to mention unexpected situations, since there are no clearly defined preconditions and postconditions, as when working with business requirements, there is simply a code that provides certain services, and a code that consumes these services.
Now to the main thing - what follows from all this from a practical point of view. Exceptions of the first kind that inform about violation of the set of pre- and post-conditions should be handled as close as possible to the client - the code closest to the client side knows the expected behavior the most. It is this code that should be given the responsibility of handling such exceptions. As for exceptions of a different kind, unexpected situations, the code that implements one or another functional — the code of the repository, if you are working with the database, for example, should work with them. And in this case, the handler on the contrary should be as close as possible to the place of the exception.
In general, instead of concluding again, this little note is just thinking about the “negative” scenarios of the system’s behavior, and how to work with them in the best way.
It would be nice for the developers from the very beginning of the project to keep in mind a certain “road map” that would cover all stages of development. “Death is part of life,” Mom told Forest Gump, contingencies are part of the regular functioning of the system, for this reason, consider the possibility of these very contingencies, classify them, start working with them already at the design stage, adequately reflect in the code base, and thereby organically woven into the structure of the project, it seems to me as important as the implementation of the entire set of full-time functional.
As usual, first try to define the terms. Take a look at MSDN - “ The C # language's exception handling features provide a way to deal with any unexpected or exceptional situations that arise while a program is running. ” A very interesting place in the wording - unexpected or exceptional situations. At first glance it sounds like synonyms, but if you think about it, the difference is quite palpable - an exceptional situation during the execution of a certain use case, and some kind of system behavior, the “system use case”, which in itself is an exceptional situation. I will explain in more detail what I mean. The standard design workflow begins with use case diagrams, which, roughly speaking, project nouns from the specification onto actor entities, and verbs on use case diagram entities. And, with a careful approach to the formalization of the scheme, the entry of the system into each new state (illustrated by the use case), and exit from it must occur in compliance with the fulfillment of preconditions and postconditions. For each state, this set is unique, a common feature is that the implementation of this entire set ensures that the system is in the correct, consistent state.exceptional situations .
Further, it is simply necessary to mention such a wonderful concept as the “samurai principle”. Its essence is to get the most clearly determined, predictable code behavior. Using ArgumentException, we have a semblance of a filter that will filter out an incorrect set of input data (also a variation on the topic of preconditions), further along the code, all dubious places are also surrounded by exceptions, so that the code crashes as beautifully and predictably as possible. This technique already refers rather to the encoding stage, let's say, to the most secure implementation of the provided functionality. Here it is already much more appropriate to mention unexpected situations, since there are no clearly defined preconditions and postconditions, as when working with business requirements, there is simply a code that provides certain services, and a code that consumes these services.
Now to the main thing - what follows from all this from a practical point of view. Exceptions of the first kind that inform about violation of the set of pre- and post-conditions should be handled as close as possible to the client - the code closest to the client side knows the expected behavior the most. It is this code that should be given the responsibility of handling such exceptions. As for exceptions of a different kind, unexpected situations, the code that implements one or another functional — the code of the repository, if you are working with the database, for example, should work with them. And in this case, the handler on the contrary should be as close as possible to the place of the exception.
In general, instead of concluding again, this little note is just thinking about the “negative” scenarios of the system’s behavior, and how to work with them in the best way.