November 2011

Volume 26 Number 11

The Cutting Edge - Design of a Domain Model

By Dino Esposito | November 2011

Dino EspositoThe recent release of the Entity Framework 4.1 and the new Code First development pattern breaks a cardinal rule of server development: Don’t take a single step if the database isn’t in place. Code First tells developers to focus on the business domain and to model it in terms of classes. In a way, Code First encourages the application of domain-driven design (DDD) principles in the .NET space. A business domain is populated with related, interconnected entities, each of which has its own data exposed as properties and may expose behavior through methods and events. More importantly, each entity may have a state and be bound to a possibly dynamic list of validation rules.

Writing an object model for a realistic scenario raises some issues that aren’t addressed in current demos and tutorials. In this article, I’ll take the challenge and discuss the building of a Customer class, touching on a number of design patterns and practices along the way, such as the Party pattern, aggregate roots, factories, and technologies like Code Contracts and the Enterprise Library Validation Application Block (VAB).

For reference, I recommend you take a look at an open source project of which the code discussed here is a small subset. Created by Andrea Saltarello, the Northwind Starter Kit project (nsk.codeplex.com) aims to illustrate effective practices for architecting multilayered solutions.

Object Model vs. Domain Model

Debating whether to employ an object model or a domain model may seem pointless, and for the most part it’s just a matter of terminology. But precise terminology is a key factor in ensuring that all members of a team have the same concept in mind when they use specific terms.

For nearly everybody in the software industry, an object model is a collection of generic but possibly related objects. How is a domain model different? In the end, a domain model is still an object model, so using the two terms interchangeably may not be a terrible mistake. Still, when the term “domain model” is used with a certain emphasis, it may carry with it some expectations about the shape of constituent objects.

This use of domain model is associated with the definition given by Martin Fowler: an object model of the domain that incorporates both behavior and data. In turn, the behavior expresses both rules and specific logic (see bit.ly/6Ol6uQ).

DDD adds a bunch of pragmatic rules to a domain model. According to this perspective, a domain model differs from an object model in the intensive use of value objects it recommends in lieu of primitive types. An integer, for example, can be many things—a temperature, an amount of money, a size, a quantity. A domain model would use a specific value object type for each different scenario.

Furthermore, a domain model should identify aggregate roots. An aggregate root is an entity obtained by composing other entities together. Objects in the aggregate root have no relevance outside, meaning that no use cases exist in which they’re used without being passed from the root object. The canonical example of an aggregate root is the Order entity. Order contains as an aggregate OrderItem, but not Product. It’s hard to imagine (even though that would be determined only by your specs) that you’d need to work with an OrderItem without it coming from an Order. On the other hand, you may well have use cases in which you work with Product entities without involving orders. Aggregate roots are responsible for maintaining their child objects in a valid state and persisting them.

Finally, some domain model classes may offer public factory methods to create new instances, rather than constructors. When the class is mostly standalone and not really part of a hierarchy, or when the steps that create the class are of some interest to the client, using a plain constructor is acceptable. With complex objects like aggregate roots, however, you need an additional level of abstraction over the instantiation. DDD introduces factory objects (or, more simply, factory methods on some classes) as a way to decouple client requirements from internal objects and their relationships and rules. A very clear and concise introduction to DDD can be found at bit.ly/oxoJD9.

The Party Pattern

Let’s focus on a Customer class. In light of what was stated earlier, here’s a possible signature:

public class Customer : Organization, IAggregateRoot
{
  ...
}

Who is your customer? Is it an individual, an organization or both? The Party pattern suggests you distinguish between the two and define clearly which properties are common and which belong only to individuals or organizations. The code in Figure 1 is limited to Person and Organization; you can make it more detailed by splitting organizations into non-profit and commercial companies if your business domain requires it.

Figure 1 Classes According to the Party Pattern

public abstract class Party
{
  public virtual String Name { get; set; }
  public virtual PostalAddress MainPostalAddress { get; set; }
}
public abstract class Person : Party
{
  public virtual String Surname { get; set; }
  public virtual DateTime BirthDate { get; set; }
  public virtual String Ssn { get; set; }
}
public abstract class Organization : Party
{
  public virtual String VatId { get; set; }
}

It’s never a bad idea to recall that you should aim to produce a model that closely models your actual business domain, not an abstract representation of the business. If your requirements only speak of customers as individuals, then applying the Party pattern is not strictly required even though it introduces a point of future extensibility. 

Customer as an Aggregate Root Class

An aggregate root is a class in your model that represents a standalone entity—one that doesn’t exist in relation to other entities. Most of the time, you have aggregate roots that are just individual classes that don’t manage any child object or perhaps simply point to the root of other aggregates. Figure 2 shows a bit more of the Customer class.

Figure 2 Customer Class as an Aggregate Root

public class Customer : Organization, IAggregateRoot
{
  public static Customer CreateNewCustomer(
    String id, String companyName, String contactName)
  {
    ...
  }
 
  protected Customer()
  {
  }
 
  public virtual String Id { get; set; }
    ...
 
  public virtual IEnumerable<Order> Orders
  {
    get { return _Orders; }
  }
   
  Boolean IAggregateRoot.CanBeSaved
  {
    get { return IsValidForRegistration; }
  }
 
  Boolean IAggregateRoot.CanBeDeleted
  {
    get { return true; }
  }
}

As you can see, the Customer class implements the (custom) IAggregateRoot interface. Here’s the interface:

public interface IAggregateRoot
{
  Boolean CanBeSaved { get; }
  Boolean CanBeDeleted { get; }
}

What does it mean to be an aggregate root? An aggregate root handles persistence for its child aggregated objects and is responsible for enforcing invariant conditions that involve the group. It turns out that an aggregate root should be able to check whether the entire stack can be saved or deleted. A standalone aggregate root just returns true without any further checking.

Factory and Constructor

A constructor is type-specific. If the object is just one type—no aggregates and no complex initialization logic—using a plain constructor is more than fine. In general, however, a factory is a useful extra layer of abstraction. A factory can be a simple static method on the entity class or a separate component of its own. Having a factory method also helps readability, because it makes clear why you’re creating that given instance. With constructors, your power to address different instantiation scenarios is more limited, as constructors aren’t named methods and can only be distinguished through the signature. Especially with long signatures, it’s difficult to figure out later why a particular instance is being obtained. 
Figure 3 shows the factory method on the Customer class.

Figure 3 Factory Method on the Customer Class

public static Customer CreateNewCustomer(
  String id, String companyName, String contactName)
{
  Contract.Requires<ArgumentNullException>(
           id != null, "id");
  Contract.Requires<ArgumentException>(
           !String.IsNullOrWhiteSpace(id), "id");
  Contract.Requires<ArgumentNullException>(
           companyName != null, "companyName");
  Contract.Requires<ArgumentException>(
           !String.IsNullOrWhiteSpace(companyName), "companyName");
  Contract.Requires<ArgumentNullException>(
           contactName != null, "contactName");               
  Contract.Requires<ArgumentException>(
           !String.IsNullOrWhiteSpace(contactName), "contactName");
 
  var c = new Customer
              {
                Id = id,
                Name = companyName,
                  Orders = new List<Order>(),
                ContactInfo = new ContactInfo
                              {
                                 ContactName = contactName
                              }
              };
  return c;
}

A factory method is atomic, gets input parameters, does its job and returns a fresh instance of a given type. The instance being returned should be guaranteed to be in a valid state. The factory is responsible for fulfilling all defined internal validation rules.

The factory also needs to validate input data. For this, using Code Contracts preconditions keeps the code clean and highly readable. You can also use postconditions to ensure that the returned instance is in a valid state, like so:

Contract.Ensures(Contract.Result<Customer>().IsValid());

As for using invariants throughout the class, experience says that you can’t always afford them. Invariants may be too invasive, especially in large, complex models. Code Contracts invariants are sometimes almost too respectful of the ruleset, and sometimes in your code you want more flexibility. It’s preferable, then, to restrict the areas where invariants must be enforced.

Validation

Properties on a domain class likely need to be validated to ensure that no required fields are left empty, no too-long text is placed in limited containers, values fall in the proper ranges and so forth. You’ll also have to consider cross-property validation and sophisticated business rules. How would you code validation? 

Validation is about conditional code so, in the end, it’s a matter of combining a few if statements and return Booleans. Writing a validation layer with plain code and without any framework or technology might work, but it isn’t really a great idea. The resulting code wouldn’t be very readable and wouldn’t be easy to evolve, though some fluent libraries are making this easier. Subject to real business rules, validation can be highly volatile and your implementation must account for that. In the end, you can’t simply write code that validates; you have to write code that’s open to validating the same data against different rules.

With validation, sometimes you want to yell out if invalid data is passed, and sometimes you just want to collect errors and report that to other layers of code. Remember, Code Contracts don’t validate; they check conditions and then throw an exception if a condition doesn’t apply. By using a centralized error handler you can recover from exceptions and degrade gracefully. In general, I recommend using Code Contracts in a domain entity only to catch potentially severe errors that can lead to inconsistent states. It makes sense to use Code Contracts in a factory—in this case, if passed data is invalid, the code must throw. Whether to use Code Contracts in the setter methods of properties is your call. I prefer to take a softer route and validate via attributes. But which attributes?

Data Annotations vs. VAB

The Data Annotations namespace and Enterprise Library VAB are very similar. Both frameworks are attribute-based and can be extended with custom classes representing custom rules. In both cases, you can define cross-property validation. Finally, both frameworks have a validator API that evaluates an instance and returns the list of errors. Where’s the difference?

Data Annotations are part of the Microsoft .NET Framework and don’t require a separate download. Enterprise Library is a separate download; not a big deal in a large project, but still an issue as it may require approval in corporate scenarios. Enterprise Library can be easily installed via NuGet (see the article, “Manage Project Libraries with NuGet,” in this issue).

The Enterprise Library VAB is superior to Data Annotations in one respect: It can be configured via XML rulesets. An XML ruleset is an entry in the configuration file where you describe the validation you want. Needless to say, you can change things declaratively without even touching your code. Figure 4 shows a sample ruleset.

Figure 4 Enterprise Library Rulesets

<validation>
   <type assemblyName="..." name="ValidModel1.Domain.Customer">
     <ruleset name="IsValidForRegistration">
       <properties>
         <property name="CompanyName">
           <validator negated="false"
                      messageTemplate="The company name cannot be null" 
                      type="NotNullValidator" />
           <validator lowerBound="6" lowerBoundType="Ignore"
                      upperBound="40" upperBoundType="Inclusive" 
                      negated="false"
                      messageTemplate="Company name cannot be longer ..."
                      type="StringLengthValidator" />
         </property>
         <property name="Id">
           <validator negated="false"
                      messageTemplate="The customer ID cannot be null"
                      type="NotNullValidator" />
         </property>
         <property name="PhoneNumber">
           <validator negated="false"
                      type="NotNullValidator" />
           <validator lowerBound="0" lowerBoundType="Ignore"
                      upperBound="24" upperBoundType="Inclusive"
                      negated="false"
                      type="StringLengthValidator" />
         </property>
         <property name="FaxNumber">
           <validator negated="false"
                      type="NotNullValidator" />
           <validator lowerBound="0" lowerBoundType="Ignore"
                      upperBound="24" upperBoundType="Inclusive"
                      negated="false"
                      type="StringLengthValidator" />
         </property>
       </properties>
     </ruleset>
   </type>
 </validation>

A ruleset lists the attributes you want to apply to a given property on a given type. In code, you validate a ruleset as follows:

public virtual ValidationResults ValidateForRegistration()
{
  var validator = ValidationFactory
          .CreateValidator<Customer>("IsValidForRegistration");
  var results = validator.Validate(this);
  return results;
}

The method applies the validators listed in the IsValidForRegistration ruleset to the specified instance.

A final note on validation and libraries. I haven’t covered every popular validation library, but that wouldn’t make a significant difference. The important point is to consider whether your business rules change and how often. Based on that, you can decide whether Data Annotations, VAB, Code Contracts or some other library is more appropriate. In my experience, if you know exactly what you need to achieve, then the “right” validation library is easy to choose.

Wrapping Up

An object model for a realistic business domain can hardly be a plain collection of properties and classes. Moreover, design considerations take precedence over technologies. A well-done object model expresses every necessary aspect of the domain. Most of the time, this means having classes that are easy to initialize and validate, and are rich in properties and logic. DDD practices should not be considered dogmatically, but instead be the guideposts that show the way to go.     


Dino Espositois the author of “Programming Microsoft ASP.NET 4” (Microsoft Press, 2011) and “Programming Microsoft ASP.NET MVC” (Microsoft Press, 2011), and coauthor of “Microsoft .NET: Architecting Applications for the Enterprise” (Microsoft Press, 2008). Based in Italy, Esposito is a frequent speaker at industry events worldwide. Follow him on Twitter at twitter.com/despos.

Thanks to the following technical experts for reviewing this article: *Manuel Fahndrich and *Andrea Saltarello