October 2017

Volume 32 Number 10

[Data Points]

DDD-Friendlier EF Core 2.0, Part 2

By Julie Lerman

Julie LermanIn my September column (msdn.com/magazine/mt842503), I laid out the many Entity Framework Core (EF Core) 2.0 fea-tures that align nicely with Domain-Driven Design (DDD) principles. In addition to providing great guidance and patterns for software development, DDD principles are also critical if you’re designing microservices. In the examples throughout the article, I used simplistic patterns in order to focus on the particular EF Core feature. Doing this meant that the code didn’t represent well-designed DDD-guided classes, and I promised that in an upcoming column I’d evolve those classes to look more like what you might write for a real-world implementation using DDD. And that’s what I’m going to do in this article. I’ll walk you through these better-architected classes and show you how they continue to work well as I use EF Core 2.0 to map them to my database.

The Original Domain Model

I’ll begin with a quick refresher on my little domain model. Because it’s for a sample, the domain lacks the complex business problems that would generally drive you to lean on DDD, but even without those complicated problems, I can still apply the patterns so you can see them in action, and see how EF Core 2.0 responds to them.

The domain comprises the Samurai characters from the movie “Seven Samurai,” where I keep track of their first appearance in the movie and their secret identities.

In the original article, the Samurai was the root of the aggregate and I had constrained the model to ensure the Samurai was responsible for managing its entrances and its secret identity. I demonstrated some of those constraints as follows:

Samurai and Entrance have a one-to-one relationship. Samurai’s Entrance field is private. Entrance has a foreign key field, SamuraiId. Because Samurai.Entrance is private, I needed to add a Fluent API mapping in the DbContext class to be sure EF Core was able to comprehend the relationship for retrieving and persisting this data. I evolved the Entrance property to be tied to a backing field, and then modified the mappings to let EF Core know about this, as well.

PersonName_ValueObject (named so elaborately for your benefit) is a value object type without its own identity. It can be used as a property in other types. Samurai has a PersonName_ValueObject property called SecretIdentity. I used the new EF Core Owned Entity feature to make SamuraiContext know to treat the SecretIdentity the same as earlier versions of EF would handle a ComplexType, storing the properties of the value object in columns of the same table to which the Samurai type maps.

The Enhanced Domain Model

What follows are the more advanced classes in the aggregate, along with the EF Core 2.0 DbContext I’m using to map to the database, which in my case happens to be SQLite. The diagram in Figure 1 shows the aggregate with its class details. The code listings will start with the non-root entities and finish up with the root, Samurai, which controls the others. Note that I’ve removed namespace references, but you can see them in the download that accompanies this article.

 

Diagram of the Advanced Aggregate

Figure 1 Diagram of the Advanced Aggregate

Figure 2 shows the evolved Entrance class.

Figure 2 The Entrance Class Designed Following DDD Patterns

public class Entrance {
  public Entrance (Guid samuraiGuidId,int movieMinute, string sceneName, string description) {
    MovieMinute = movieMinute;
    SceneName = sceneName;
    ActionDescription = description;
    SamuraiGuidId=samuraiGuidId;
  }
  private Entrance () { } // Needed by ORM
  public int Id { get; private set; }
  public int MovieMinute { get; private set; }
  public string SceneName { get; private set; }
  public string ActionDescription { get; private set; }

  private int SamuraiFk { get;  set; }
  public Guid SamuraiGuidId{get;private set;}
}

So much of DDD code is about protecting your domain from being unintentionally misused or abused. You constrain access to the logic within the classes to ensure they can be used only in the way you intend. My intention for the En-trance class (Figure 1) is that it be immutable. You can define its property values using the overloaded constructor, passing in the values for all of its properties except for SamuraiFk. You’re allowed to read any of the properties—but notice they all have private setters. The constructor is the only way to affect those values. Therefore, if you need to modify it, you’ll need to replace it with a whole new Entrance instance. This class looks like a candidate for a value object, especially because it’s immutable, but I want to use it to demonstrate one-to-one behavior in EF Core.

With EF Core (and earlier iterations of EF), when you query for data, EF is able to materialize results even when properties have private setters because it uses reflection. So EF Core can work with all these properties of Entrance that have private setters.

There’s a public constructor with four parameters to populate properties of Entrance. (In the previous sample, I used a factory method that added no value to this class, so I’ve removed it in this iteration.) In this domain, an Entrance with any of those properties missing makes no sense, so I’m constraining its design to avoid that. Following that constructor is a private parameterless constructor. Because EF Core and EF use reflection to materialize results, like other APIs that instantiate objects for you such as JSON.NET it requires that a parameterless constructor be available. The first constructor overrides the parameterless constructor that’s provided by the base class (object) that all classes derive from. Therefore, you must explicitly add that back in. This is not new behavior to EF Core; it’s something you’ve had to do with EF for a long time. In the context of this article, however, it bears repeating. If you’re new to EF with this version, it’s also notable that when an Entrance is created as a result of a query, EF Core will only use that parameterless constructor to create the object. The public constructor is available for creating new Entrance objects.

What about that Guid and int pointing back to Samurai? The Guid is used by the domain to connect the samurai and entrance so that the domain logic has no reliance on the data store for its Ids. The SamuraiFk will only be used for per-sistence. SamuraiFk is private, but EF Core is able to infer a backing field for it. If it were named SamuraiId, EF Core would recognize it as the foreign key, but because it doesn’t follow convention, there’s a special mapping in the context to let EF Core know that it is, indeed, the foreign key. The reason it’s private is that it’s not relevant to the domain but needed for EF Core to comprehend the relationship in order to store and retrieve the data correctly. This is a concession to avoiding persistence logic in my domain class but, in my opinion, a minor one that doesn’t justify the extra effort of introducing and maintaining a completely separate data model.

There’s a new entity in my aggregate: Quote, shown in Figure 3. In the movie this sample domain honors, the various characters have some notable quotes that I want to keep track of in this domain. It also gives me a chance to demonstrate a one-to-many relationship.

Figure 3 The Quote Type Designed Following DDD Patterns

public class Quote {
  public Quote (Guid samuraiGuidId,string text) {
    Text = text;
    SamuraiGuidId=samuraiGuidId;
  }
  private Quote () { } //ORM requires parameterless ctor
  public int Id { get; private set; }
  public string Text { get; private set; }
  private int SamuraiId { get; set; }
  public Guid SamuraiGuidId{get;private set;}
}

Note that the patterns are the same as those I’ve explained for the Entrance entity: the overloaded public constructor and the private parameterless constructor, the private setters, the private foreign key property for persistence, and the Guid. The only difference is that the SamuraiId, used as the persistence FK, follows EF Core convention. When it’s time to look at the DbContext class, there won’t be a special mapping for this property. The reason I’ve named these two properties inconsistently is so you can see the difference in the mappings for the conventional vs. unconventional nam-ing.

Next is the PersonFullName type (renamed from PersonName), shown in Figure 4, which is a value object. I explained in the previous article that EF Core 2.0 now allows you to persist a value object by mapping it as an Owned Entity of any entity that owns it, such as the Samurai class. As a value object, PersonFullName is used as a property in other types and entities. A value object has no identity of its own, is immutable and isn’t an entity. In addition to the previous article, I have also explained value objects in more depth in other articles, as well as in the Pluralsight course, Domain-Driven Design Fundamentals, which I created with Steve Smith (bit.ly/PS-DDD). There are other important facets to a value object and I use a ValueObject base class created by Jimmy Bogard (bit.ly/13SWd9h) to implement them.

Figure 4 The PersonFullName Value Object

public class PersonFullName : ValueObject<PersonFullName> {
  public static PersonFullName Create (string first, string last) {
    return new PersonFullName (first, last);
  }
  public static PersonFullName Empty () {
    return new PersonFullName (null, null);
  }
  private PersonFullName () { }

  public bool IsEmpty () {
    if (string.IsNullOrEmpty (First) && string.IsNullOrEmpty (Last)) {
      return true;
    } else {
      return false;
    }
  }
  private PersonFullName (string first, string last) {
    First = first;
    Last = last;
  }
  public string First { get; private set; }
  public string Last { get; private set; }
  public string FullName () => First + " " + Last;
}
}

PersonFullName is used to encapsulate common rules in my domain for using a person’s name in any other entity or type. There are a number of notable features of this class. Alt-hough it hasn’t changed from the earlier version, I didn’t provide the full listing in the previous article. Therefore, there are a few things to explain here, in particular the Empty factory method and the IsEmpty method. Because of the way Owned Entity is implemented in EF Core, it can’t be null in the owning class. In my domain, PersonFullName is used to store a samurai’s secret identity, but there’s no rule that it must be populated. This creates a conflict between my business rules and the EF Core rules. Again, I have a simple enough solution that I don’t feel the need to create and maintain a separate data model, and it doesn’t impact how Samurai is used. I don’t want anyone using my domain API to have to remember the EF Core rule, so I built two factory methods: You use Create if you have the values and Empty if you don’t. And the IsEmpty method can quickly determine the state of a PersonFullName. The entities that use PersonFullName as a prop-erty will need to leverage this logic and then anyone using those entities won’t have to know anything about the EF Core rule.

Tying It All Together with the Aggregate Root

Finally, the Samurai class is listed in Figure 5. Samurai is the root of the aggregate. An aggregate root is a guardian for the entire aggregate, ensuring the validity of its internal objects and keeping them consistent. As the root of this aggre-gate, the Samurai type is responsible for how its Entrance, Quotes and SecretIdentity properties are created and man-aged.

Figure 5 The Samurai Entity, Which Is the Root of the Aggregate

public class Samurai {   
  public Samurai (string name): this() {
    Name = name;
    GuidId=Guid.NewGuid();
    IsDirty=true;
  }
  private Samurai () {
    _quotes = new List<Quote> ();
    SecretIdentity = PersonFullName.Empty ();
  }
  public int Id { get; private set; }
  public Guid GuidId{get;private set;}
  public string Name { get; private set; }
  public bool IsDirty { get; private set; }
 
  private readonly List<Quote> _quotes = new List<Quote> ();
  public IEnumerable<Quote> Quotes => _quotes.ToList ();
  public void AddQuote (string quoteText) {
     // TODO: Ensure this isn't a duplicate of an item already in Quotes collection
    _quotes.Add (Quote.Create(GuidId,quoteText));
     IsDirty=true;
  }

  private Entrance _entrance;
  private Entrance Entrance { get { return _entrance; } }
  public void CreateEntrance (int minute, string sceneName, string description) {
    _entrance = Entrance.Create (GuidId, minute, sceneName, description);
     IsDirty=true;
  }
  public string EntranceScene => _entrance?.SceneName;

  private PersonFullName SecretIdentity { get; set; }
  public string RevealSecretIdentity () {
    if (SecretIdentity.IsEmpty ()) {
      return "It's a secret";
    } else {
      return SecretIdentity.FullName ();
    }
  }
  public void Identify (string first, string last) {
    SecretIdentity = PersonFullName.Create (first, last);
    IsDirty=true;
  }
}

Like the other classes, Samurai has an overloaded constructor, which is the only way to instantiate a new Samurai. The only data expected when creating a new samurai is the samurai’s known name. The constructor sets the Name property and also generates a value for the GuidId property. The SamuraiId property will get populated by the database. The GuidId property ensures that my domain isn’t dependent on the data layer to have a unique identity and that’s what’s used to connect the non-root entities (Entrance and Quote) to the Samurai, even if the Samurai hasn’t yet been persisted and honored with a value in the Sam-uraiId field. The constructor appends “: this()” to call the parameterless constructor in the constructor chain. The parame-terless constructor (reminder: it’s also used by EF Core when creating objects from query results) will ensure that the Quotes collection is instantiated and that SecretIdentity is created. This is where I use that Empty factory method. Even if someone writing code with the Samurai never provides values for the SecretIdentity property, EF Core will be satisfied because the property isn’t null.

The full encapsulation of Quotes in Samurai isn’t new. I’m taking advantage of the support for IEnumerable that I discussed in an earlier column on EF Core 1.1 (msdn.com/magazine/mt745093).

The fully encapsulated Entrance property has changed from the previous sample in only two minor ways. First, be-cause I removed the factory method from Entrance, I’m now instantiating it directly. Second, the Entrance constructor now takes additional values so I’m passing those in even though at this time the Samurai class isn’t currently doing any-thing with these extra values.

There are some enhancements to the SecretIdentity property since the earlier sample. First, the property originally was public, with a public getter and a private setter. This allowed EF Core to persist it in the same way as in earlier versions of EF. Now, however, SecretIdentity is declared as a private property yet I’ve defined no backing property. When it comes time to persist, EF Core is able to infer a backing property so it can store and retrieve this data without any additional mapping on my part. The Identify method, where you can specify a first and last name for the secret identity, was in the earlier sample. But in that case, if you wanted to read that value, you could access it through the public property. Now that it’s hidden, I’ve added a new method, RevealSecretIdentity, which will use the PersonFullName.IsEmpty method to determine if the prop-erty is populated or not. If so, then it returns the FullName of the SecretIdentity. But if the person’s true identity wasn’t identified, the method returns the string: “It’s a secret.”

There’s a new property in Samurai, a bool called IsDirty. Any time I modify the Samurai properties, I set IsDirty to true. I’ll use that value elsewhere to determine if I need to call SaveChanges on the Samurai.

So throughout this aggregate, there’s no way to get around the rules I built into the entities and the root, Samurai. The only way to create, modify or read Entrance, Quotes and SecretIdentity is through the constrained logic built into Samurai, which, as the aggregate root, is guarding the entire aggregate.

Mapping to the Data Store with EF Core 2.0

The focus of the previous article was on how EF Core 2.0 is able to persist and retrieve data mapped to these con-strained classes. With this enhanced domain model, EF Core is still able to work out most of the mappings even with things so tightly encapsulated in the Samurai class. In a few cases I do have to provide a little help to the DbContext to make sure it comprehends how these classes map to the database, as shown in Figure 6.

Figure 6 The SamuraiContext DbContext Class

public class SamuraiContext : DbContext {
  public DbSet<Samurai> Samurais { get; set; }
  protected override void OnConfiguring (DbContextOptionsBuilder optionsBuilder) {
    optionsBuilder.UseSqlite ("Filename=DP0917Samurai.db");
  }
  protected override void OnModelCreating (ModelBuilder modelBuilder) {
    modelBuilder.Entity<Samurai> ()
      .HasOne (typeof (Entrance), "Entrance")
      .WithOne ().HasForeignKey(typeof (Entrance), "SamuraiFk");

    foreach (var entityType in modelBuilder.Model.GetEntityTypes ()) {
      modelBuilder.Entity (entityType.Name).Property<DateTime> 
        ("LastModified");
      modelBuilder.Entity (entityType.Name).Ignore ("IsDirty");
    }
    modelBuilder.Entity<Samurai> ().OwnsOne (typeof (PersonFullName), 
      "SecretIdentity");
  }

  public override int SaveChanges () {
    foreach (var entry in ChangeTracker.Entries ()
      .Where (e => e.State == EntityState.Added ||
        e.State == EntityState.Modified)) {
      if (!(entry.Entity is PersonFullName))
        entry.Property ("LastModified").CurrentValue = DateTime.Now;
    }
    return base.SaveChanges ();
  }
}

Not a lot has changed in the SamuraiContext since the first sample from my first article, but there are a few things to point out as reminders. For example, the OwnsOne mapping lets EF Core know that SecretIdentity is an Owned Entity and that its properties should be persisted *as though they were individual properties of Samurai. For the sake of this sample, I’m hardcoding the provider in the OnConfiguring method as opposed to leveraging dependency injection and inversion of control (IoC) services. As mentioned in the first article, EF Core can figure out the one-to-one relationship between Samurai and Entrance, but I have to express the relationship in order to access the HasForeignKey method to inform the context about the non-conventional foreign key property, SamuraiFk. In doing so, because Entrance is private in Samurai, I can’t use a lambda expression and am using an alternate syntax for the HasForeignKey parameters.

LastModifed is a shadow property—new to EF Core—and will get persisted into the database even though it’s not a property in the entities. The Ignore mapping is to ensure that the IsDirty property in Samurai isn’t persisted as it’s only for domain-relevant logic.

And that’s it. Given how much of the DDD patterns I’ve applied in my domain classes, there’s very little in the way of special mappings that I have to add to the SamuraiContext class to inform EF Core 2.0 what the database looks like or how to store and retrieve data from that database. And I’m pretty impressed by that.

There’s No Such Thing as a Perfect DDD Sample

This is still a simple example because other than outputting “It’s a secret” when a SecretIdentity hasn’t been given a value, I’m not solving any complex problems in the logic. The subtitle of Eric Evan’s DDD book is “Tackling Complexity in the Heart of Software.” So much of the guidance regarding DDD is about breaking down overwhelming complex problems in to smaller solvable problems. The code design patterns are only a piece of that. Everyone has different problems to solve in their domains and, often, readers ask for a sample that can be used as a template for their own software. But all that those of us who share our code and ideas can do is provide ex-amples as learning tools. You can then extrapolate those lessons and apply some of the thinking and decision making to your own problems. I could spend even more time on this tiny bit of code and apply additional logic and patterns from the DDD arsenal, but this sample does go pretty far in leveraging DDD ideas to create a deeper focus on behavior rather than on properties, and further encapsulate and protect the aggregate.

My goal in these two columns was to show how EF Core 2.0 is so much friendlier for mapping your DDD-focused domain model to your database. While I demonstrated that, I hope you were also inspired by the DDD patterns I’ve in-cluded in these classes.


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

Thanks to the following Microsoft technical expert for reviewing this article: Cesar de La Torre


Discuss this article in the MSDN Magazine forum