Input validation in an object oriented console application, where does it belong?

Design question...

I've been tasked with creating a small console application in C# and want to follow best encapsulation practices.

I have a UI layer, a Controller/Command Processor layer, a Business Rules layer, and a Data Access Layer.

The application allows the user to create a record, fill in some fields and then insert it into memory with the rest of the records.

Assuming some of these fields have a limited number of valid options, where is the most logical place to validate that the user input matches one of these options?

Is it good design to have a sort of temp record that lives in the UI, fill it out as I go with the users data and check the return values from the setter methods in the record class? Should my record class not implement the validation and rather validate that the user input matches an option in the business rules layer instead?

The application is being written in C# if that influences the answer. My current thinking is to use properties in the record class and have the temp record in the UI, then only set the underlying member in the record class if the user input matches an option. The UI would then compare the user input to the value in the record using a get method, and prompt the user to re-enter the data if the values didn't match (the set didn't succeed.)

Is there a better way that follows better design principles?

Thanks in advance.

Answers


If I understand the layout correctly, I would recommend something like this:

The Controller/Command Processor would validate commands ensuring that they follow the appropriate syntax.

The Business Rules layer would ensure that all data entered is valid data. Before entering data into a record, it would be validated.

If you have data connection commands, the Data Access Layer would ensure that the connection information entered is valid.


I'd advocate for strengthening the typing of your application. Your business rules should only use strongly-typed, validated data. They don't need to fiddle with the quirks of your UI.

Let's look at the facts of an hypothetical situation: the user has to enter a car serial number. The constraints on a serial number is that it has 15 characters, and the first two are "VF".

Your UI is accepting a sequence of characters from the User, whether it be from a TextBox or the console input. At this level, it makes sense to store this sequence of characters into a string object, since it is what it is: a sequence of characters.

On the business rule side, on the contrary, it should already be validated. Indeed, only a subset of the valid strings are of interest to the BRs. Therefore, your BRs should manipulate another type that is not string. A user-defined type, in C#, is either a class or a struct.

So let's write this type. I choose to create a class, because there are less syntactic caveats.

class CarSerialNumber {
}

A car serial number is semantically a value, not an object. It has value semantics. Its value can be stored into a string, and we have to redefine Equals(object), GetHashCode(), and it is equatable to itself, so it can implement IEquatable.

class CarSerialNumber : IEquatable<CarSerialNumber> {
    string _value;

    public override bool Equals(object right) {
        if(this == right) {
            return true;
        }
        if(right == null) {
            return false;
        }

        if(!right is CarSerialNumber) {
            return false;
        }

        return Equals((CarSerialNumber)right);
    }

    public override int GetHashCode() {
        return _value.GetHashCode();
    }

    public bool Equals(CarSerialNumber right) {
        return _value == right._value;
    }
}

It should be constructed with a string value, and be immutable.

class CarSerialNumber : IEquatable<CarSerialNumber> {
    public CarSerialNumber(string value) {
        _value = value;
    }

    readonly string _value;

    // ... rest ....
}

And there we just found the hinge. This constructor is the turning point from the string to the CarSerialNumber. Moreover, a CarSerialNumber should not be constructible with an invalid string. As the typesystem cannot guarantee at this point that the parameter string is a valid car serial number, the constructor has to bail out with an exception.

Therefore, validation belongs RIGHT HERE.

class CarSerialNumber : IEquatable<CarSerialNumber> {
    public CarSerialNumber(string value) {
        Validate(value);
        _value = value;
    }

    private Validate(string value) {
        if(value == null) { // Never forget this one ;)
            throw new ArgumentNullException();
        }

        // Rest of validation, throwing exceptions on failure.
    }

    // ... rest ....
}

You may strongly want to consider making this type sealed. Inheritance of such types may include other traps. (Like magical equatability of incompatible subtypes)

Other advantages of creating such types "where a simpler type would do" are these:

  • When used as a parameter type, it documents what the function expects, better than the parameter name does. (or at least, it's a complementary documentation)
  • Types created that way are incompatible with one another. The compiler will warn you if you try to pass a CarModel to a function expecting a CarSerialNumber.

Other solutions for stronger typing include the use of enums when you know all possible values at compile time.


Need Your Help

can we open pdf using https URL using UIWebview

iphone objective-c xcode https

I'm making an application in which my application needs to using an https URLS.I want to display PDF File using https url in view(or)UIWebview. how do i do that? Please explain me ...Thanks in adv...

Is everything in Ext js blue?

javascript css extjs

Sure, the blue is nice and it has a nice office look but you wouldn't want all applications to have the same color. Is it easy customize the look in ext js?

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.