Page 13 - MSDN Magazine, October 2017
P. 13
namespace references, but you can see them in the download that accompanies this article.
Figure 2 shows the evolved Entrance class.
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 Entrance 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 construc- tor 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, espe- cially 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
Figure 2 The Entrance Class Designed Following DDD Patterns
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 param- eterless constructor to create the object. The public constructor is available for creating new Entrance objects.
The first constructor overrides the parameterless constructor that’s provided by the base class (object) from which all classes derive.
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 persistence. 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 or- der 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 char- acters 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.
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 naming.
Next is the PersonFullName type (renamed from PersonName), shown in Figure 4, which is a value object. I explained in the pre- vious 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,
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;} }
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;} }
msdnmagazine.com
October 2017 9