5

While some platform in some languages already address this issue, I would like to keep this semi-language agnostic and to focus on patterns associated with this issue.

I have a data model that contains FirstName, MiddleName, and LastName (to keep things simple). First and Last names are required and may have other rules to ensure their validity. Middle name is optional.

By default, the model is empty and thus, invalid.

When a value changes, an event can be triggered to validate the field that changed.

My question is what patterns can be used to best manage the state of the object since, based on the scenario, I'm doing field level validation. Should I move to model level validation on field change which would address the state issue or is there an alternate way?

3 Answers 3

3

This is a common problem in data validation; When entering new data into the system, before any information is entered by the user, the default working state of the object (mostly nulls) is an invalid state for virtually all other processes of your system.

Validation is always context-dependent. If you have, for instance, a business requirement that all entered "people" records must have at least a first and last name, then neither field-level validation (which would only be run when setting a field, and so would not catch a failure to set the other field), nor model-level validation (which, if invoked real-time, would return a validation error for this rule when entering the first name because the last name is invalid) would work all the time when implemented naively.

Instead, your domain model has to be made intelligent enough to know when it is ok to be in an inconsistent state, and when it is not. Usually, this is accomplished by providing some sort of scope or context identifier into your validation routines:

  • When entering a single field, you can, and should, only validate rules the define the behavior of that single field. You simply cannot require the user to enter a last name when they are attempting to specify a first name in a new record, and vice-versa.

  • At certain points, you may know that a subset of your object should now be in a consistent state. This may happen when filling out a multi-page form and attempting to continue to the next page; at that point, you may validate rules that involve one or more fields on the current page. This includes all field-level validations, but also additional multi-field validations, such as making sure date ranges composed of a start and end date are valid (end date after start date for instance), and that a person has both a first and last name.

    Sometimes, if one page's data depends on data in a previous page, you may be able to make these validations as well, but in that case those validations should not prevent a person going backwards to fix a mistake; it should only prevent the user continuing now that there is an obvious inconsistency. Understand that allowing real-time validation of values within a page or across multiple pages may introduce undesirable coupling; the validation rules must incorporate logic that is dependent on the structure of the View layer.

  • Finally, when persisting an object (or retrieving it from persistence for use behind the scenes), it must be wholly consistent. That includes all field validations, all page-level validations, and additionally all rules involving data spread across multiple pages.

Rules that the model must meet at one of these levels may prevent proper execution of the program when run at a lower level. It is often possible to include the scope or level of validation in a suite of rules that can be run with one method call, thus allowing the rule to pass if the data is "consistent enough" for a particular level of validation. However, doing so often couples the domain (or controller) to a very specific View, making the design brittle; if you want to move a field to a different page of the View, the validation routines back in the controller or data model may have to change to reflect this. There may not be a good way around this if you want to validate each rule as soon as possible.

1

I would do both field and model level validation. On the UI side, check that the required fields are populated and display an error if not. Simplistic -- and intentionally so.

Then inside the model, create a #validate function. This checks the state of the object and returns true if the model is valid, false if not. Your basic pattern is to validate first, and if successful, apply the changes. If validation fails, throw out the changes. It can also be helpful if the validation returns an error message saying why validation failed.

If your model is composed of many different objects. each one gets its own validate call, and the top of the tree just gathers the results from each of the sub-parts.

0

I would suggest you move away from field level validation, and in fact, move away from exposing the structure of the fields as much as possible.

Instead, focus on the OO design model of "the instance owns data, and models all behaviour" - have a "change name" method that takes the appropriate inputs, and performs the internal mutation, hiding the details of implementation from the consumer.

Generally, I suggest, favour methods that are atomic, and that capture intent: SetName, rather than SetFirstName. That way processing the action is complete, and your object is valid before, and after, the method call.

Those model object methods also map fairly naturally to the API you will end up exposing to the user - they want to change the name, so model it as a single call from the UI through to the model and back.

This helps insulate the parts of your system from each other, and avoids accidentally coupling parts of the software together through exposing internal data like "how I store the name" to the consumers of the object.

3
  • While this is true; how would this model be of any use to the consuming code if an invalid value was attempted to be placed when SetName is called? Commented Feb 13, 2012 at 22:46
  • Consuming code must expect this call to fail with an exception of some sort.
    – KeithS
    Commented Feb 13, 2012 at 22:50
  • @JamesEggers- KeithS has it exactly right. Your object should always be valid - before that SetName call, and after that SetName call. Having an object that is "in an invalid state" is a design smell, and suggests you are not actually encapsulating behaviour inside the object. Commented Feb 13, 2012 at 23:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.