Skip to main content

Handling inconsistent state of domain objects [Resolved]

In general, I would my domain objects (customer, contract, etc.) to be always in a consistent state. However there are situations, where they might be in an inconsistent state, and nevertheless I want to work with them:

  1. When I load the domain-object from the database and it is in an inconsistent state (maybe someone changed a field directly on the database)
  2. When I create them in a Creation-Dialog and I need to handle some validation errors.

Are there any best practices how to handle these contradicting requirements?

Question Credit: Martin Böschen
Question Reference
Asked March 10, 2018
Posted Under: Programming
3 Answers

When I load the domain-object from the database and it is inconsitent (maybe someone changed a field directly on the database)

The best thing to do is make sure that corrupt data can't be inserted into the db by adding constraints. Foreign keys, unique or length constraints etc.

When I create them in a Creation-Dialog and I need to handle some validation errors.

Write a validator on on your objects and validate them before inserting/updating.

Normally, I don't validate my items retrieved from the DB as they were already checked at insert/update time. You can of course also validate them while retrieving and handle it. Handling it with an exception makes sense here as this is something that should never happen.

credit: Carra
Answered March 10, 2018

In general, you have the right idea. Assume that "(...) they might be in an inconsistent state, and nevertheless I want to work with them." is false and that you never-ever want to see your objects in an inconsistent state. Let's run with this idea, and see where it takes us.

When loading from the data base

When you load an object from a database you have to construct a value that will be an instance of the desired type from an instance of some type that represents the result you got from the database. You must deserialise the object. In my opinion it makes sense to validate that the invariants required by the hold - and a good place to do it is the constructor.

class Foo {
    Foo(Some_type_representing_db_result const);

If you need to construct Foo objects from Some_type_representing_db_results, you write an appropriate constructor to do that for you (I illustrate the answer using C++, but I'm sure you can adapt the examples to your language of choice). Throw an exception if the constructor cannot establish the required invariants - there is no need to force yourself to weaken them to accommodate some possibly broken values. You would compromise your program's correctness this way.

If you have to deal with broken objects I'd suggest creating a different data type, with weaker invariants; e.g. Foo and WeakerFoo. This way the compiler will warn you if you try to use a WeakerFoo as a non-broken Foo.

When handling validation errors

Take advantage of the fact that you do not have to construct the object immediately. Validate the fields one-by-one, and when you're sure they have acceptable values - combine them into your desired type using that type's constructor.

int count_of_stuff = a_form.count_of_stuff();
std::string name_of_stuff = a_form.name_of_stuff();

// These two functions should throw descriptive exceptions that
// will be useful to you.

Foo correct_foo { count_of_stuff, name_of_stuff };

This is really the same situation as the database one. It's only different because you have a user to whom you can report per-field errors.

A side note

Try to embed invariants into your types. If you know you have a type whose members must conform to some specification, create a type for them. Why would you want to use an int and have to remember validate it explicitly when you can use NumberForWhichSpecificRangeIsValid and have it validate itself?

credit: Mael
Answered March 10, 2018

These are business decisions.

For data in the database, it's notable that data can also fall out of"validity" as business rules change. When they do change, or when the business rules are violated by a bad actor, how does the business deal with it's paper files?

Likewise, on data intake, how would the business deal with being handed the paper version of the data? I suspect some invalid values would require handing the form back to the customer, decorated with error messages in red ink. For other invalid entries, the business might prefer their reps to smile, say thank-you, and quietly make some assumptions behind the scenes.

Pose your questions to "the business" for guidance.

Ideally, come to the discussion armed with a knowledge of what options are possible digitally that couldn't be done on paper and vice versa. Consult UX experts, as needed, for creative solutions to poke customers nicely for corrections -- and to make them feel rewarded for fixing their mistakes. Etc.

Most importantly, don't make business decisions without "the business." So, "best practice" is to consult the business (and/or a domain expert, which you someday will be).

credit: svidgen
Answered March 10, 2018
Your Answer