Handling Errors
The Request/Response Design Pattern
It is great when things go well.
Our expectations are met, we enjoy the fruits of our labor and life is good! Unfortunately things do not always go well, in your personal life or in your code.
The Test Driven Development (TDD) process teaches us about the “Happy” path and the “Sad” path. The terminology is a little lame but its intention is clear:
Test for the Expected “Happy” Scenario First
… Then create every possible Failing “Sad” Scenario you can think of at the time
The happy path is the easiest to develop. We know what we want and we control all the dependencies and conditions to validate that happy result.
But what if something goes wrong?
We need to not only understand what went wrong but deliver to the calling Client’s Request some way of managing the sad path Response.
We need to deliver to the calling Client information about the conditions that created the sad path response.
As with using for the extension methods for LINQ, you need to bring into scope the proper Namespace that contains the LINQ Extension Methods.
In LINQ it is the System.Linq Namespace
In the Interface Error Handling Pattern you reference the folder namespace that holds the three files above: TMD.Exceptions.Extensions.
This Requirement is Incredibly Powerful
Consider this Data Ops Scenario:
You have a Data Access Layer that supports Web Services, a Database and files such as XML.
You have complied with the Single Responsibility Principle and create three classes to support the three different data access responsibilities.
There are different requirements for the behavior methods for each of the three type but there are some common requirements as well.
The Interface Extensions Pattern allows you to use four different folders that hold four different Namespaces
-
Cloud Services – Access information from Web Services
-
Entity Framework – Access information from an ORM
-
File I/O = Access information as XML data
-
Data Common – Access extension methods common to all three data access scenarios
Within your three data implementations you import the namespace for the common extensions in one using statement and the required data services extensions in the second using statement.
Encapsulation of Data Services by Namespace
… Complies with the Open/Closed Principle
…… You can extend any of the functionality through Namespaces
The Error Handling Example Code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
----------------------------------------------------------! ERROR Handling Pattern: public interface IErrorCommon { bool Success { get; set; } List<ErrorInfoDTO> ErrorList { get; set; } } public class ErrorInfoDTO { public string Message { get; set; } public string StackTrace { get; set; } public ErrorInfoDTO() { Message = "No Error Message"; StackTrace = "No Stack Trace Message"; } public ErrorInfoDTO(Exception ex) { Message = ex.Message; if (!string.IsNullOrEmpty(ex.StackTrace)) { StackTrace = ex.StackTrace; } StackTrace = "No Stack Trace Message"; } public override string ToString() { return string.Format("Error Message is {0} with a Stack Stack Trace of {1}", Message, StackTrace); } } public static class ErrorHandling { public static List ErrorList { get; set; } static ErrorHandling() { ErrorList = new List(); } public static List AddError(this IErrorCommon error, Exception ex) { ErrorList.Clear(); ErrorList.Add(new ErrorInfoDTO { Message = ex.Message, StackTrace = ex.StackTrace }); return ErrorList; } public static List AddError(this IErrorCommon error, string errorMessage) { ErrorList.Clear(); ErrorList.Add(new ErrorInfoDTO { Message = errorMessage }); return ErrorList; } public static List BubbleErrors(this IErrorCommon error, List errorList) { ErrorList.Clear(); if (errorList != null && errorList.Any()) { ErrorList.AddRange(errorList); return errorList; } return new List(); } } ---------------------------------------------------------- |
Interface Inheritance is preferred
over Class Inheritance
as it guarantees a
“Behaves Like” Relationship
and does not offer the opportunity to
violate the Liskov Substitution Principle
Error Handling Implementation in the Request/ Response Design Pattern
The below code demonstrates how the error pattern is used for conditional “if” statements and exception handling using the Try/Catch statement:
The TMD Vehicles Portal
MVC 4 VehiclesViewModelFactory Class
+A TMD View Model Sample use of Error Handling
The View Model used to provide Response DTOs from Request DTOs passed as the single method parameter
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
--------------------------------------------------------- using System; using System.Linq; using Microsoft.Practices.Unity; using TMD.Exceptions.Extensions; using TMD.Portal.DataServices; using TMD.Portal.DataServices.ServicesData; using TMD.Portal.Infrastructure.Common; using TMD.Portal.Infrastructure.IOC; using TMD.Portal.Infrastructure.ServicesDTOs; using TMD.Portal.Model.DTOs.Vehicles; using TMD.Portal.Model.ModelFactoryHelpers.Vehicles; namespace TMD.Portal.Model.ViewModelFactories.Vehicles { public class VehiclesViewModelFactory { private VehiclesDataOps _autoDataOps { get; set; } private IVehicles _vehicleServices { get; set; } private static VehiclesModelFactoryHelpers _modelFactoryHelpers { get; set; } public VehiclesViewModelFactory(VehiclesDataOps autoDataOps, IVehicles vehicleServices, VehiclesModelFactoryHelpers modelFactoryHelpers) { _autoDataOps = autoDataOps; _vehicleServices = vehicleServices; _modelFactoryHelpers = modelFactoryHelpers; } public VehicleCategoryViewResponseDTO CreateVehiclesCategoryInfoViewResponse(VehicleCategoryViewRequestDTO viewRequest) { var viewResponse = UnityIoc.IocContainer.Resolve(); try { VehicleCategoriesServiceRequestDTO serviceRequest = CreateVehiclesCategoriesServiceRequestDTO(viewRequest); VehicleCategoriesServiceResponseDTO serviceResponse = _autoDataOps.CreateVehicleCategoriesServiceResponse(serviceRequest); if (!serviceResponse.Success) { viewResponse.ErrorList.Add(new ErrorInfoDTO { Message = "Create VehiclesServiceResponseDTO Failure" }); return viewResponse; } viewResponse = CreateVehicleCategoryViewResponseDtoByCategory(serviceResponse, viewRequest.Category); if (!viewResponse.Success) { viewResponse.ErrorList.Add(new ErrorInfoDTO { Message = "Create CreateVehiclesCategoryInfoViewResponseDTO Failure" }); return viewResponse; } viewResponse.Success = true; return viewResponse; } catch (Exception ex) { viewResponse.ErrorList.Add(new ErrorInfoDTO { Message = string.Format("CreateVehiclesCategoryInfoViewResponse() Exception Failure: Error: {0}.", ex) }); return viewResponse; } } public VehicleViewResponseDTO CreateVehicleViewResponseDTO(VehicleViewRequestDTO viewRequest) { var viewResponse = UnityIoc.IocContainer.Resolve(); try { var serviceRequest = CreateVehiclesServiceRequestDTO(viewRequest); var serviceResponse = _autoDataOps.CreateVehiclesServiceResponseDTO(serviceRequest); if (!serviceResponse.Success) { viewResponse.ErrorList.Add(new ErrorInfoDTO { Message = "Create VehiclesServiceResponseDTO Failure" }); return viewResponse; } viewResponse = CreateVehicleViewResponseDTO(serviceResponse, viewRequest); if (!viewResponse.Success) { viewResponse.ErrorList.Add(new ErrorInfoDTO { Message = "Create CreateVehicleViewResponseDTO Failure" }); return viewResponse; } viewResponse.Success = true; return viewResponse; } catch (Exception ex) { viewResponse.ErrorList.Add(new ErrorInfoDTO { Message = string.Format("CreateVehiclesCategoryInfoViewResponse() Exception Failure: Error: {0}.", ex) }); return viewResponse; } } #region Helpers VehicleCategoriesServiceRequestDTO private static VehicleCategoryViewResponseDTO CreateVehicleCategoryViewResponseDtoByCategory(VehicleCategoriesServiceResponseDTO serviceResponse, string category = null) { var viewResponse = _modelFactoryHelpers.GetVehiclesCategoryInfoViewResponseDTO(serviceResponse); if (!string.IsNullOrEmpty(category)) { viewResponse.VehiclesCategoryList = serviceResponse.VehicleCategoriesList .Select(auto => new VehicleCategoryDTO { Category = auto.Category, Description = auto.Description }) .Where(x => x.Category == category) .ToList(); viewResponse.ExecutionDurationInMilliseconds = CreateExecutionDuration(serviceResponse.ResponseTimeStamp, serviceResponse.RequestTimeStamp); return viewResponse; } viewResponse.VehiclesCategoryList = serviceResponse.VehicleCategoriesList .Select(auto => new VehicleCategoryDTO { Category = auto.Category, Description = auto.Description }) .ToList(); viewResponse.ExecutionDurationInMilliseconds = CreateExecutionDuration(serviceResponse.ResponseTimeStamp, serviceResponse.RequestTimeStamp); return viewResponse; } private static VehicleViewResponseDTO CreateVehicleViewResponseDTO(VehiclesServiceResponseDTO serviceResponse, VehicleViewRequestDTO viewRequest) { var viewResponse = _modelFactoryHelpers.VehicleViewResponseDTO(serviceResponse); if (!string.IsNullOrEmpty(viewRequest.Category)) { viewResponse.VehiclesList = serviceResponse.VehiclesList .Select(auto => new VehicleDTO { Make = auto.Make, Model = auto.Model, Series = auto.Series, Year = auto.Year, Category = auto.Category, DisplayName = auto.DisplayName, Cost = auto.Cost, Wholesale = auto.Wholesale, Msrp = auto.Msrp }) .Where(x => x.Category == viewRequest.Category) .ToList(); viewResponse.ExecutionDurationInMilliseconds = CreateExecutionDuration(serviceResponse.ResponseTimeStamp, serviceResponse.RequestTimeStamp); return viewResponse; } viewResponse.VehiclesList = serviceResponse.VehiclesList .Select(auto => new VehicleDTO { Make = auto.Make, Model = auto.Model, Series = auto.Series, Year = auto.Year, Category = auto.Category, DisplayName = auto.DisplayName, Cost = auto.Cost, Wholesale = auto.Wholesale, Msrp = auto.Msrp }) .ToList(); viewResponse.ExecutionDurationInMilliseconds = CreateExecutionDuration(serviceResponse.ResponseTimeStamp, serviceResponse.RequestTimeStamp); return viewResponse; } #region Utilities private static VehicleCategoriesServiceRequestDTO CreateVehiclesCategoriesServiceRequestDTO(VehicleCategoryViewRequestDTO request) { return Utilities.CreateDtoTransform<vehiclecategoryviewrequestdto, vehiclecategoriesservicerequestdto="">(request); } private static VehiclesServiceRequestDTO CreateVehiclesServiceRequestDTO(VehicleViewRequestDTO request) { return Utilities.CreateDtoTransform<vehicleviewrequestdto, vehiclesservicerequestdto="">(request); } private static int CreateExecutionDuration(DateTime responseTimeStamp, DateTime requestTimeStamp) { return (responseTimeStamp.Millisecond - requestTimeStamp.Millisecond); } #endregion #endregion } } --------------------------------------------------------- |
This Class complies with all principles except in does not use the Messaging system to remove the “Magic Strings” as that would have complicated showing the error handling code.
Wisdom Pearl # 136 – Taming Murphy’s Law
Anything that Can Go Wrong … Will Go Wrong
…You should Always Design for the Worst Scenario
…… And Hope for the Best Scenario
The following two tabs change content below.
I am a Principal Architect at Liquid Hub in the Philadelphia area specializing in Agile Practices as a Certified Scrum Master (CSM). I use Test Driven Development (TDD) and Acceptance Test Driven Development (ATDD) with Behavior Driven Development (BDD) as my bridge to Agile User Stories Acceptance Criteria in a Domain Driven Design (DDD) implementing true RESTful services
Latest posts by Brad Huett (see all)
- DevOps: A Bridge to Your DevOps Culture - March 25, 2016
- Embracing Test Driven Development (TDD) - March 25, 2016
- DevOps: Delivering Agile Projects - March 25, 2016


