{"id":2571,"date":"2016-05-18T11:58:46","date_gmt":"2016-05-18T09:58:46","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=2571"},"modified":"2023-10-05T17:35:03","modified_gmt":"2023-10-05T15:35:03","slug":"chain-of-responsibility-an-elegant-way-to-handle-complex-validation","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/en\/chain-of-responsibility-an-elegant-way-to-handle-complex-validation\/","title":{"rendered":"Chain of Responsibility \u2013 an elegant way to handle complex validation"},"content":{"rendered":"\n<p>Data validation is a key aspect of almost any system, incorrect or hacked data can cause a lot of damage, restoring\u00a0can cost a lot of time, money and stress.\u00a0Properly designed\u00a0system operates\u00a0on\u00a0high-quality, correct, clean\u00a0and checked\u00a0data.\u00a0In this post I will try to introduce the concept of validation using the Chain of Responsibility pattern (CoR), which I use successfull for years.\u00a0More\u00a0theory with examples of this pattern\u00a0can be found <a href=\"http:\/\/www.dofactory.com\/net\/chain-of-responsibility-design-pattern\" target=\"_blank\" rel=\"noopener\" rel=\"nofollow\" >here<\/a>.\u00a0Checking the input data may include simple user form or\u00a0complex data received from an external system. Using this approach we are not afraid of any validation, so we can handle quite\u00a0complex issues.<\/p>\n\n\n\n<p>In this article, we&#8217;ll build a demo using MVC .NET web application with simple form (most of the times corrupted data comes from user input) and the purpose is to ensure that&nbsp;submitted data won&#8217;t kill the program. Presented&nbsp;approach can be used in any type of application. For the purposes of training, client side&nbsp;validation (based on data annotations) will be disabled and will focus only on the server-side validation. Anyway, it is worth being wary and always apply double check on submitted data&nbsp;to avoid hacking.<\/p>\n\n\n\n<p>The following example illustrates how a chain of responsibility can handle validation issue elegant way.<br>Consider&nbsp;simple hotel system where we will be able to order one of the three apartments: one bedroom, one bedroom executive or two bedroom. Of course, date of reservation should be appropriate and chosen room should be avaliable. When the validation conditions are not met, the system should display an appropriate error message on the screen.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><img decoding=\"async\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2019\/02\/Piotr-Luksza-Chains-of-Responsibility-form.gif\" alt=\"Error message OneBedroom Executive is currently not available\" class=\"wp-image-568\"\/><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Business validation rules<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>DateFrom is not null<\/li>\n\n\n\n<li>DateFrom is greater than now<\/li>\n\n\n\n<li>DateTo is not null<\/li>\n\n\n\n<li>DateTo is greater than DateFrom<\/li>\n\n\n\n<li>One bedroom Exclusive is not avaliable<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Demo &#8211; model<\/h2>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class ApartmentBooking\n{\npublic int Id { get; set; }\npublic ApartmentType ApartmentType { get; set; }\npublic DateTime DateFrom { get; set; }\npublic DateTime DateTo { get; set; }\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic enum ApartmentType : byte\n{\n&#x5B;Display(Name = \"One Bedroom\")]\nOneBedroom,\n&#x5B;Display(Name = \"One Bedroom Executive\")]\nOneBedroomExecutive,\n&#x5B;Display(Name = \"Two Bedroom\")]\nTwoBedroom,\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Demo &#8211; important components<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ApartmentBookingValidationContext<\/li>\n\n\n\n<li>DateFromRequiredValidator<\/li>\n\n\n\n<li>DateFromRangeValidator<\/li>\n\n\n\n<li>DateToRequiredValidator<\/li>\n\n\n\n<li>DateToRangeValidator<\/li>\n\n\n\n<li>ApartmentAvaliableValidator<\/li>\n<\/ul>\n\n\n\n<p>One of the most important classes is&nbsp;<em>ApartmentBookingValidationContext <\/em>with one static method Validate().<em>&nbsp;<\/em>This is the&nbsp;place&nbsp;where the magic starts, all validators&nbsp;instances are created&nbsp;here&nbsp;and hooked up in the right order.<\/p>\n\n\n\n<p>As we see every validator contais&nbsp;important method SetSuccessor,&nbsp;it allows us to set the next item in the chain, so the validation gets passed along a chain of objects until one of them handles it. Notice that method Validate() returns collection of messages (errors), so we can&nbsp;use them in other layers of the application, for example pushing the UI notification.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic static class ApartmentBookingValidationContext\n{\npublic static Dictionary&lt;string, string&gt; Validate(Models.ApartmentBooking model)\n{\n\/* Hook up validation chain\nDateFrom not null\nDateTo not null\nDateFrom greater than now\nDateTo greater than DateFrom\nDummy apartment validation base on type\n*\/\n \nDateFromRequiredValidator dateFromRequiredValidator = new DateFromRequiredValidator();\nDateFromRangeValidator dateFromRangeValidator = new DateFromRangeValidator();\ndateFromRequiredValidator.SetSuccessor( dateFromRangeValidator);\nDateToRequiredValidator dateTimeToRequiredValidator = new DateToRequiredValidator();\ndateFromRangeValidator.SetSuccessor( dateTimeToRequiredValidator);\nDateToRangeValidator dateToRangeValidator = new DateToRangeValidator();\ndateTimeToRequiredValidator.SetSuccessor( dateToRangeValidator);\nApartmentAvaliableValidator apartmentAvaliableValidator = new ApartmentAvaliableValidator();\ndateToRangeValidator.SetSuccessor( apartmentAvaliableValidator);\n \nreturn dateFromRequiredValidator.HandleValidation( model);\n}\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic abstract class ValidatorBase\n{\nprotected ValidatorBase Successor { get; private set; }\nprotected Dictionary&lt;string, string&gt; ErrorsResult { get; set; }\n \nprotected ValidatorBase()\n{\nErrorsResult = new Dictionary&lt;string, string&gt;();\n}\n \npublic abstract Dictionary&lt;string, string&gt; HandleValidation( Models.ApartmentBooking model);\n \n\/\/\/ &lt;summary&gt;\n\/\/\/ Set next validation\n\/\/\/ &lt;\/summary&gt;\n\/\/\/ &lt;param name=&quot;successor&quot;&gt;&lt;\/param&gt;\npublic void SetSuccessor( ValidatorBase successor)\n{\nthis.Successor = successor;\n}\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class DateFromRequiredValidator : ValidatorBase\n{\npublic override Dictionary&lt;string, string&gt; HandleValidation( Models.ApartmentBooking model)\n{\nif (model.DateFrom == DateTime.MinValue)\n{\nErrorsResult.Add(&quot;DateFrom&quot;, &quot;Date from field is required&quot;);\nreturn ErrorsResult;\n}\n \nif (Successor != null)\nreturn Successor.HandleValidation(model);\n \nreturn ErrorsResult;\n}\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class DateFromRangeValidator : ValidatorBase\n{\npublic override Dictionary&lt;string, string&gt; HandleValidation( Models.ApartmentBooking model)\n{\nif (model.DateFrom &lt; new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day))\n{\nErrorsResult.Add(&quot;DateFrom&quot;, &quot;Date from should be grater than now&quot;);\nreturn ErrorsResult;\n}\n \nif (Successor != null)\nreturn Successor.HandleValidation(model);\n \nreturn ErrorsResult;\n}\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class DateToRequiredValidator : ValidatorBase\n{\npublic override Dictionary&lt;string, string&gt; HandleValidation(Models.ApartmentBooking model)\n{\nif (model.DateTo == DateTime.MinValue)\n{\nErrorsResult.Add(&quot;DateTo&quot;, &quot;Date to 2 field is required&quot;);\nreturn ErrorsResult;\n}\n \nif (Successor != null)\nreturn Successor.HandleValidation(model);\n \nreturn ErrorsResult;\n}\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class DateToRangeValidator : ValidatorBase\n{\npublic override Dictionary&lt;string, string&gt; HandleValidation( Models.ApartmentBooking model)\n{\nif (model.DateTo &lt;= model.DateFrom)\n{\nErrorsResult.Add(&quot;DateTo&quot;, &quot;Date to should be grater than Date from&quot;);\nreturn ErrorsResult;\n}\n \nif (Successor != null)\nreturn Successor.HandleValidation(model);\n \nreturn ErrorsResult;\n}\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class ApartmentAvaliableValidator : ValidatorBase\n{\npublic override Dictionary&lt;string, string&gt; HandleValidation(Models.ApartmentBooking model)\n{\n\/* Dummy validation *\/\nif (model.ApartmentType == ApartmentType.OneBedroomExecutive)\n{\nErrorsResult.Add(&quot;ApartmentType&quot;,\nstring.Format(&quot;{0} is currently not avaliable&quot;, model.ApartmentType));\nreturn ErrorsResult;\n}\n \nif (Successor != null)\nreturn Successor.HandleValidation(model);\n \nreturn ErrorsResult;\n}\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpPost]\n&#x5B;ValidateAntiForgeryToken]\npublic async Task&lt;ActionResult&gt; Create(ApartmentBooking model)\n{\n\/\/if (ModelState.IsValid)\n{\nvar errorsResult = ApartmentBookingValidationContext.Validate(model);\nif (errorsResult.Any())\n{\nModelState.Clear();\n \nforeach (var error in errorsResult)\n{\nModelState.AddModelError(error.Key, error.Value);\n}\nTempData&#x5B;&quot;ValidationErrors&quot;] = true;\n \nreturn View(model);\n}\nelse\n{\ndb.ApartmentBookings.Add(model);\nawait db.SaveChangesAsync();\n}\n \nreturn RedirectToAction(&quot;Index&quot;);\n}\n \n\/\/return View(model);\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Validation based on&nbsp;Chain of the Responsibility pattern is very handy and gives a lot of benefits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Improves readability (as you see controller is flat &#8211; no spaghetti code)<\/li>\n\n\n\n<li>Handling complex validation&nbsp;by several objects in chain structure, which encapsulates the logic<\/li>\n\n\n\n<li>Flexibility in assigning responsibilities to objects<\/li>\n\n\n\n<li>Easy development and maintenance. So when business requirements change, just&nbsp;modify concrete validator or create a new one and hook it to the chain<\/li>\n\n\n\n<li>Objects are independent, they dont\u2019t know nothing about the chain structure<\/li>\n<\/ul>\n\n\n\n<p>I encourage you to use this pattern in daily work, the source code can be found <a href=\"https:\/\/github.com\/gosudev\/ChainOfResponsibility\" target=\"_blank\" rel=\"noopener\" class=\"ek-link\" rel=\"nofollow\" >here<\/a>.<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;2571&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;2&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;4.5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;4.5\\\/5 ( votes: 2)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Chain of Responsibility \u2013 an elegant way to handle complex validation&quot;,&quot;width&quot;:&quot;125&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 125px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            4.5\/5 ( votes: 2)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Data validation is a key aspect of almost any system, incorrect or hacked data can cause a lot of damage, &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/en\/chain-of-responsibility-an-elegant-way-to-handle-complex-validation\/\">Continued<\/a><\/p>\n","protected":false},"author":3,"featured_media":2740,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1320],"tags":[],"class_list":["post-2571","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hard-development"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2016\/05\/validated-sii-blogersii-1140x540.jpg","category_names":["Hard development"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/2571"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/comments?post=2571"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/2571\/revisions"}],"predecessor-version":[{"id":24850,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/2571\/revisions\/24850"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media\/2740"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media?parent=2571"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/categories?post=2571"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/tags?post=2571"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}