Refactoring for fun and profit

The first step in getting a misbehaving application in order is refactoring.  Over the past several months, I have been working on a project with a very anemic domain model, which is considered an anti-pattern by most, but apparently the original authors thought otherwise.  The result was a batch of service classes that contained long, complicated methods for handling all data manipulation of the domain objects, and a batch of domain objects that had no idea about their own data.

For example, I had a situation similar to the following:

public class Person
{
public Guid Id {get; set;}
public string Name {get; set;}
public DateTime BirthDate {get; set;}
}

public class PersonService
{
public void UpdatePerson(Person oldPerson, Person newPerson)
{
// Copy all fields from newPerson into oldPerson
}

public bool ValidatePerson(Person person)
{
// Validate data in Person
}
}

This is a simple example, and on the surface, it looks like it would be easy to maintain.  The problem here is one of cognitive load.  The developer must remember to make changes in multiple classes every time a property is changed in the Person object, and the PersonService will tend to obtain longer, more complicated logic than there would be in a self-contained object.  You also run into trouble with property visibility, as all property setters are required to be public (or internal, if the Service is guaranteed to remain in the same assembly), opening you up to future side-effects from other areas of the application code.

After refactoring, you might end up with something like the following contrived example:

public class Person
{
public Guid Id {get; private set;}
public string Name {get; private set;}
public DateTime BirthDate {get; private set;}

public Person(Guid id, string name, DateTime birthDate)
{
Id = id;
Name = name;
Birthdate = birthDate;
}

public Person(Person copyFrom)
{
// Basic copy constructor
}

public void UpdateName(string newName)
{
// This can also be done in the property setter, but I'm putting
// this here as an example.
if (ValidateName(newName))
Name = newName;
}

public bool Validate()
{
// Return validation status
}
}

So what do we gain here?  We no longer have to worry about other parts of the codebase unexpectedly changing any of the internal variables.  The object contains the smarts to do its own validation.  Changing the Name can be validated before completion.  The object has become smart enough to handle its own properties.  The Service class goes away (perhaps to reemerge as a proper Domain Service later).  We can now rely on every instance having a consistent internal state.

Our app has just gotten a little bit greener.  There is much more to go.

No Comments

Add a Comment