Skip to content

DDD entity patterns

Jon P Smith edited this page Apr 4, 2020 · 4 revisions

This page explains how you can create Domain-Driven Design (DDD) styled EF Core entity classes. It also tells you the signature of that GenericServices looks for methods that it can use to create or update the entity and its aggregates.

If you are not clear about what DDD styled entity classes are, or you want a longer explanation then please read the article on my blog called Creating Domain-Driven Design entity classes with Entity Framework Core.

The basics of a DDD-styled entity class

The idea is that any creation or update of an entity (and its aggregates) has to be done by methods with a function-revealing name, e.g. AddReview. At the same time you must stop the ability to create/update the data in any other way. This requires you to "lock-down" the entity class by:

  • All properties should have a private setter.
  • All collections should be of type IEnumerable<T>, so that nothing can be added/removed from the collection. The actual collection is handled by a backing field.
  • The parameterless constructor should should be private (EF Core uses this for creating instance of read) Then creates should be done by either/both:
  • A public constructors with all the parameters needed to create the entity correctly.
  • A public static method with all the parameters needed to create the entity correctly. *The benefit of static methods is that they can return a status, so errors found during the construction can be fed back. The updates should be done by:
  • Public methods with function-revealing names and parameters.

Example DDD-styled entity classes

  1. The Order entity class.
  2. The Book entity class. Note: I have made one property. PublishedOn, have a public setter so that I can compare the performance of an AutoMapper update and a DDD-Styled update.

How GenericServices matches a DTO to a DDD method

The process that GenericServices goes through to match a DTO to a DDD constructor, static factory or update method is listed below. Note that the CreateAndSave method have an optional property called ctorOrStaticMethodName, and the UdateAndSave method has a property called methodName that can force the selection of the ctor/factory/method to call. This is referred to in the list below as "selectorString"

  1. If the entity has any appropriate ctor/factory/method than can be called (and the update methodName isn't "AutoMapper") then it will first look there for matches.
  2. If the "selectorString" is set, then I will use this to pick the ctor/factory/method. The possible matches are:
  • "AutoMapper" - use AutoMapper to update the properties
  • "ctor" - use a ctor
  • "ctor(2)" - use a ctor with 2 parameters
  • "methodName" - use methodName (static method if create, method in entity class if update).
  • "methodName(2)" - same as last item, but with given number of parameters
  1. If still not defined that it takes the DTO class name, removes "Dto", "VM", or "ViewModel" (case insensitive) and tries to find a entity method (update) or entity static factory (create). If it does it assumes this the the match.
  2. If none of these match then it looks at all the ctors/static factor (Create) or methods (update) and matches them by parameters.
  3. If there are no matches, or there are more than one match it will give you an error message.

NOTE: You will also get an error message if steps 2 or 3 selects a ctor/method and then the parameters don't match.

What does GenericServices expect in a DDD-styled entity?

When GenericServices calls a method to DDD-styled entity class it will have only loaded that entity class. This means if your method needs some other part of the relationship then it must load itself. You are allowed to have the DbContext as one of the parameters in your method call (either your actual DbContext or the generic DbContext class).

You can standard constructors and update methods that return void, but if you want to return a IStatusGeneric, then you can. This allows you to return errors, or a success Message. The rules are:

  • You need to add the NuGet library GenericServices.StatusGeneric in the assembly(s) that your entity classes are in.
  • For update methods they can return IStatusGeneric. This status result is combined into the CrudServices' status.
  • For static methods used to create an entity instance it must return the type IStatusGeneric<TEntity>. Again, the status result is combined into the CrudServices' status, and the primary key(s) are copied back into the DTOs primary key(s) properties (in case you need them).

Finally

You can find more about the pattern I use by looked in at the Summarising the DDD-style in my article and studying the entity classes in the DataLayer/EfClasses directory.

Note that some of the entities are Read-Only, i.e. they can only be created/updated via the root entity class.