Data Points

Coding for Domain-Driven Design: Tips for Data-Focused Devs

Julie Lerman

Download the Code Sample

Read the entire Domain-Driven Design series:
Coding for Domain-Driven Design: Tips for Data-Focused Devs, Part 1
Coding for Domain-Driven Design: Tips for Data-Focused Devs, Part 2
Coding for Domain-Driven Design: Tips for Data-Focused Devs, Part 3

Julie LermanThis year, Eric Evans’ groundbreaking software design book, “Domain-Driven Design: Tackling Complexity in the Heart of Software” (Addison-Wesley Professional, 2003, amzn.to/ffL1k), celebrates its 10th anniversary. Evans brought to this book many years of experience guiding large businesses through the process of building software. He then spent more years thinking about how to encapsulate the patterns that lead these projects to success—interacting with the client, analyzing the business problems being solved, building teams and architecting the software. The focus of these patterns is the business’s domain, and together they comprise Domain-Driven Design (DDD). With DDD, you model the domain in question. The patterns result from this abstraction of your knowledge about the domain. Rereading Martin Fowler’s foreword and Evans’ preface even today, continues to provide a rich overview of the essence of DDD.

In this column and the next two as well, I’ll share some pointers that have helped my data-focused, Entity Framework brain gain clarity as I work on getting my code to benefit from some DDD technical patterns.

Why Do I Care About DDD?

My introduction to DDD came from a short video interview on InfoQ.com with Jimmy Nilsson, a respected architect in the .NET community (and elsewhere), who was talking about LINQ to SQL and the Entity Framework ( bit.ly/11DdZue). At the end, Nilsson is asked to name his favorite tech book. His reply: “My favorite computer book is the book by Eric Evans, “Domain-Driven Design.” It’s like poetry, I think. It’s not just great content, but you can read it many times and it reads like poetry.” Poetry! I was writing my first tech book, “Programming Entity Framework” (O’Reilly Media, 2009), at the time, and I was intrigued by this description. So I went and read a little bit of Evans’ book to see what it was like. Evans is a beautiful, fluid writer. And that, combined with his perceptive, naturalistic view of software development, does make the book a joy to read. But I was also surprised by what I was reading. Not only was the writing wonderful, what he was writing about intrigued me. He talked about building relationships with clients and truly understanding their businesses and their business problems (related to the software in question), not just slogging code. This is something that’s been important to me in my 25 years of software development. I wanted more.

I tiptoed around the edge of DDD for a few more years, then started learning more—meeting Evans at a conference and then attending his four-day immersion workshop. While I’m far from an expert in DDD, I found that the Bounded Context pattern was something I could leverage right away as I worked to shift my own software creation process toward a more organized, manageable structure. You can read about that topic in my January 2013 column, “Shrink EF Models with DDD Bounded Contexts” ( msdn.microsoft.com/magazine/jj883952).

Since then I’ve explored further. I’m intrigued and inspired by DDD, but struggle with my data-driven perspective to comprehend some of the technical patterns that make it successful. It seems likely that many developers go through the same struggle, so I’m going to share some of the lessons I’ve been learning with the help, interest, and generosity of Evans and a number of other DDD practitioners and teachers, including Paul Rayner, Vaughn Vernon, Greg Young, Cesar de la Torre, and Yves Reynhout.

When Modeling the Domain, Forget About Persistence

Modeling the domain is all about focusing on the tasks of the business. When designing types and their properties and behaviors, I’m sorely tempted to think about how a relationship will work out in the database and how my object relational mapping (ORM) framework of choice—Entity Framework—will treat the properties, relationships and inheritance hierarchies that I’m building. Unless you’re building software for a company whose business is data storage and retrieval—something like Dropbox—data persistence only plays a supporting role in your application. It’s much like making a call out to a weather source’s API in order to display the current temperature to a user. Or sending data from your app to an external service, perhaps a registration on Meetup.com. Of course, your data may be more complicated, but with a DDD approach to bounding contexts, focusing on behaviors and following DDD guidance when building types, the persistence can be much less complex than the systems you may be building today.

And if you’ve studied up on your ORM, such as learning how to configure database mappings using the Entity Framework Fluent API, you should be able to make the persistence work as needed. In the worst case, you may need to make some tweaks to your classes. In an extreme case, such as with a legacy database, you could even add in a persistence model designed for database mapping, then use something such as AutoMapper to resolve things between your domain model and your persistence model.

But these concerns are unrelated to the business problem your software is aimed at solving, so persistence should not interfere with the domain design. This is a challenge for me because as I’m designing my entities, I can’t help but consider how EF will infer their database mappings. And so I try to block out that noise.

Private Setters and Public Methods

Another rule of thumb is to make property setters private. Instead of allowing calling code to randomly set various properties, you should control interaction with DDD objects and their related data using methods that modify the properties. And, no, I don’t mean methods like SetFirstName and SetLastName. For example, instead of instantiating a new Customer type and then setting each of its properties, you might have some rules to consider when creating a new customer. You can build those rules into the Customer’s constructor, use a Factory Pattern method or even have a Create method in the Customer type. Figure 1 shows a Customer type that’s defined following the DDD pattern of an aggregate root (that is, the “parent” of a graph of objects, also referred to as a “root entity” in DDD). Customer properties have private setters so that only other members of the Customer class can directly affect those properties. The class exposes a constructor to control how it’s instantiated and hides the parameter-less constructor (required by Entity Framework) as internal.

Figure 1 Properties and Methods of a Type That Acts As an Aggregate Root

public class Customer : Contact
{
  public Customer(string firstName,string lastName, string email)
  { ... }
  internal Customer(){ ... }
  public void CopyBillingAddressToShippingAddress(){ ... }    
  public void CreateNewShippingAddress(
   string street, string city, string zip) { ... }
  public void CreateBillingInformation(
   string street, string city, string zip,
   string creditcardNumber, string bankName){ ... }    
  public void SetCustomerContactDetails(
   string email, string phone, string companyName){ ... }
  public string SalesPersonId { get; private set; }
  public CustomerStatus Status{get;private set;}
  public Address ShippingAddress { get; private set; }
  public Address BillingAddress { get;private set; }
  public CustomerCreditCard CreditCard { get; private set; }
}

The Customer type controls and protects the other entities in the aggregate—some addresses and a credit-card type—by exposing specific methods (such as CopyBillingAddressToShippingAddress) with which those objects will be created and manipulated. The aggregate root must make sure the rules that define each entity within the aggregate are applied using domain logic and behavior implemented in these methods. Most important, the aggregate root is in charge of invariant logic and consistency throughout the aggregate. I’ll talk more about invariants in my next column, but for the meantime, I recommend reading Jimmy Bogard’s blog post, “Strengthening Your Domain: Aggregate Construction,” at bit.ly/ewNZ52, which provides an excellent explanation of invariants in aggregates.

In the end, what’s exposed by Customer is behavior rather than properties: CopyBillingAddressToShippingAddress, CreateNewShipping­Address, CreateBillingInformation and SetCustomerContactDetails.

Note that the Contact type, from which Customer derives, lives in a different assembly named “Common” because it may be needed by other classes. I need to hide the properties of Contact, but they can’t be private or Customer wouldn’t be able to access them. Instead, they’re scoped as Protected:

public class Contact: Identity
{
  public string CompanyName { get; protected set; }
  public string EmailAddress { get; protected set; }
  public string Phone { get; protected set; }
}

A side note about Identities: Customer and Contact may look like DDD value objects because they have no key value. However, in my solution, the key value is provided by the Identity class from which Contact derives. And neither of these types are immutable, so they can’t be considered value objects anyway.

Because Customer inherits from Contact, it will have access to those protected properties and is able to set them, as in this SetCustomerContactDetails method:

public void SetCustomerContactDetails  (string email, string phone, string companyName)

{

  EmailAddress = email;

  Phone = phone;

  CompanyName = companyName;

}

Sometimes All You Need Is CRUD

Not everything in your app needs to be created using DDD. DDD is there to help handle complex behaviors. If you just need to do some raw, random editing or querying, then a simple class (or set of classes), defined just as you’d typically do with EF Code First (using properties and relationships) and combined with insert, update and delete methods (via a repository or just DbContext), is all you need. So, to accomplish something like creating an order and its line items, you might want DDD to help work through special business rules and behaviors. For example, is this a Gold Star customer placing the order? In that case, you need to get some customer details to determine if the answer is yes, and, if so, apply a 10 percent discount to each item being added to the order. Has the user provided their credit-card information? Then you might need to call out to a verification service to ensure it’s a valid card.

The key in DDD is to include the domain logic as methods within the domain’s entity classes, taking advantage of OOP instead of implementing “transactional scripts” within stateless business objects, which is what a typical demo-ware Code First class looks like. 

But sometimes all you’re doing is something very standard, like creating a contact record: name, address, referred by, and so forth, and saving it.  That’s just create, read, update and delete (CRUD). You don’t need to create aggregates and roots and behaviors to satisfy that.

Most likely your application will contain a combination of complex behaviors and simple CRUD. Take the time to clarify the behaviors and don’t waste time, energy and money over-architecting the pieces of your app that are really just simple. In these cases, it’s important to identify boundaries between different subsystems or bounded contexts. One bounded context could be very much data-driven (just CRUD), while a critical core-domain-bounded context should, on the other hand, be designed following DDD approaches.

Shared Data Can Be a Curse in Complex Systems

Another issue I banged my head on, then ranted and whined about as people kindly tried to explain further, concerned sharing types and data across subsystems. It became clear that I couldn’t “have my cake and eat it too,” so I was forced to think again about my assumption that I absolutely positively must share types across systems and have those types all interact with the same table in the same database.

I’m learning to really consider where I need to share data, and then pick my battles. Some things just may not be worth trying, like mapping from different contexts to a single table or even a single database. The most common example is sharing a Contact that’s trying to satisfy everyone’s needs across systems. How do you reconcile and leverage source control for a Contact type that might be needed in numerous systems? What if one system needs to modify the definition of that Contact type? With respect to an ORM, how do you map a Contact that’s used across systems to a single table in a single database?

DDD guides you away from sharing domain models and data by explaining that you don’t always need to point to the same person table in a single database.

My biggest push back with this is based on 25 years of focusing on the benefits of reuse—reusing code and reusing data. So, I have a hard time with the following idea but I’m warming up to it: It isn’t a crime to duplicate data. Not all data will fit into this new (to me) paradigm, of course. But what about something lightweight like a person’s name? So what if you duplicate a person’s first and last name in multiple tables or even multiple databases that are dedicated to different sub-systems of your software solution? In the long run, by letting go of the complexity of sharing data, you make the job of building your system much simpler. In any case, you must always minimize data and attribute duplication in different bounded contexts. Sometimes you just need the customer’s ID and status in order to calculate discounts in a Pricing-bounded context. That same customer’s first and last name might be needed only in the Contact Management-bounded context.

But there’s still so much information that needs to be shared between systems. You can leverage what DDD refers to as an “anti-­corruption layer” (which can be something as simple as a service or a message queue) to ensure that, for example, if someone creates a new contact in one system, you either recognize that the person already exists elsewhere, or ensure that the person, along with a common identity key, is created in another subsystem.

Plenty to Chew on Until Next Month

As I make my way through learning and comprehending the technical side of Domain-Driven Design, struggling to reconcile old habits with new ideas, and arriving at innumerable “aha!” moments, the  pointers I discussed here are truly ones that helped me see more light than darkness. Sometimes it’s just a matter of perspective, and the way I have expressed them here reflects the perspective that helped make things clearer to me.

I’ll share some more of my “aha!” moments in my next column, where I’ll talk about that condescending term you may have heard: “anemic domain model,” along with its DDD cousin, the “rich domain model.” I’ll also discuss unidirectional relationships and what to expect when it’s time to add in data persistence if you’re using Entity Framework. I’ll also touch on some more DDD topics that caused me plenty of grief in an effort to shorten your own learning curve.

Until then, why not take a closer look at your own classes and see how to be more of a control freak, hiding those property setters and exposing more descriptive and explicit methods. And, remember: no “SetLastName” methods allowed. That’s cheating!


Julie Lerman is a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other Microsoft .NET topics at user groups and conferences around the world. She blogs at thedatafarm.com/blog and is the author of “Programming Entity Framework” (2010) as well as a Code First edition (2011) and a DbContext edition (2012), all from O’Reilly Media. Follow her on Twitter at twitter.com/julielerman and see her Pluralsight courses at juliel.me/PS-Videos

Thanks to the following technical expert for reviewing this article: Cesar de la Torre (Microsoft)

 

Rate: