4

Consider an example where the user enters values for customer object using a form in two aspx pages. With the first approach both the aspx pages need to validate that ID is greater than 0 and FirstName is not empty before calling the constructor. With the second option, both the pages can call the Validate function and show the error messages to the users.

Based on the above example I prefer the second option. However, when I'm researching the on the web, I keep seeing that it is more object oriented to throw exceptions right away and not let the object accept invalid data. As I said earlier with exceptions, every page calling this constructor needs to validate the inputs are valid. I don't like repeating the logic so I prefer the second option.

What is the preferred option in terms of Domain Driven Design?

Option 1

public class Customer{

   public int ID { get; set; }
   public string FirstName { get; set; }

   public Customer(int ID, string FirstName){
      if (ID < 0)
          throw new Exception("ID cannot be less than 0");
      if (string.IsNullOrEmpty(FirstName))
          throw new Exception("First Name cannot be empty");

      this.ID = ID;
      this.FirstName = FirstName;
   }
}

Option 2

public class Customer{

   public int ID { get; set; }
   public string FirstName { get; set; }

   public Customer(int ID, string FirstName){

      this.ID = ID;
      this.FirstName = FirstName;

   }

    public List<string> Validate(){
        List<string> ErrorMessages = new List<string>();

        if (ID < 0)
            ErrorMessages.Add("ID cannot be less than 0");
        if (string.IsNullOrEmpty(FirstName))
            ErrorMessages.Add("First Name cannot be empty");

        return ErrorMessages;
    }

}
user3587180
  • 1,317
  • 11
  • 23
  • DDD has nothing to do with exception handling. The former relates to _language-neutral design_ whilst the latter is an implementation issue –  May 20 '16 at 03:43

3 Answers3

3

The short answer is NO.

You would throw an exception if the application can't continue executing with the bad data. In your example, the logic is to display an error message on the front end and Option 2 is the cleaner method for achieving this requirement.

Throwing exceptions (even if they are caught) is also an expensive operation. The exception has to traverse the entire call stack before your thread can continue. This will cause performance issue at scale.

Babak Naffas
  • 12,395
  • 3
  • 34
  • 49
  • Performance cost of exception is negligible - because they should only happen in exceptional cases. Don't fear to use exceptions when necessary. – mauris May 20 '16 at 01:00
  • I agree that they should be used in exceptional cases. So IMHO, option 2 is the better way to go. – Babak Naffas May 20 '16 at 01:07
  • The purpose of exceptions is to prevent error handling from obscuring your normal program logic, as happened with return codes. With exceptions your method body has the normal flow, and errors can be handled elsewhere. So says Clean Code. The notion that exception are only for rare unexpected events is popular but incorrect. – pzulw Sep 01 '22 at 21:00
3

I'm sure this question had been answered elsewhere. But here are a few other links for reading:

From the book "The Pragmatic Programmer", the big question relating to the usage of exceptions is "What is Exceptional?".

In that section, I quote:

... exceptions should rarely be used as part of a program's normal flow; exception should be reserved for unexpected events.

While it is debatable whether or not to use exception in your case, I would say no - because you probably need to capture all possible input errors in one request and reflect back on the form for the user to correct those values.

Now that I recall, yes you should use exceptions here. This is how you code defensively. If you already expected valid arguments to be passed into the Customer class, then the code should throw exception to guard against invalid usage of the class (say for example, by another programmer). In that case, you should have another input validator to validate user's input to the application before reaching the Customer class.

Community
  • 1
  • 1
mauris
  • 42,982
  • 15
  • 99
  • 131
  • Thanks for the links. I agree with your answer, but most of the domain driven design examples I was looking at throw exceptions. Is it a common practice for DDD? – user3587180 May 20 '16 at 00:53
  • 1
    Actually now that you mentioned it, you should keep the exceptions here. There should be another input validator somewhere else between the request and this class. - updated my answer – mauris May 20 '16 at 00:55
-1

It is good practice to throw exceptions if you have appropriate exception handling. Don't blindly throw exceptions until the application crashes.

In your option 1 example, an ArgumentException is more appropriate.

public Customer(int ID, string FirstName){
      if (ID < 0)
          throw new ArgumentException("ID cannot be less than 0");
      if (string.IsNullOrEmpty(FirstName))
          throw new ArgumentException("First Name cannot be empty");

      this.ID = ID;
      this.FirstName = FirstName;
   }

Ex:

try
{
    Customer cust = new Customer(-1, string.Empty);
}
catch(ArgumentException aex)
{
    DoSomething(aex); // Log to database, file, or alike
    // Maybe display a message on the user
}
catch(Exception ex)
{
    DoSomething(ex); // Log to database, file, or alike
    // Do generic error process here
}

Your option 2 is more appropriate for data validations. That behavior can be observed as similar when applying DataAnnotations. No exceptions are thrown but error messages are returned.

jegtugado
  • 5,081
  • 1
  • 12
  • 35
  • Wouldn't it be duplicating the logic if I had try catch in multiple pages though? Moreover using a try catch will only catch the first error. Why throw exceptions when the model can return error messages and let the presentation layer handle displaying the error messages? – user3587180 May 20 '16 at 00:47
  • It will depend on how you want to handle the error. if your try catch is on the 1st page and the exception occurred on the second page then the handling is done on the 1st page. But if you have a scenario where you would still want to remain on the 2nd page and handle it there then use it as so. Not all business logic would want to return a string collection of errors. If you want the stacktrace, exception details, etc. then option 1 is what you need. – jegtugado May 20 '16 at 00:51
  • Good point about stack trace!! – user3587180 May 20 '16 at 00:54