I’m a big fan of Domain-Driven Design (DDD) and build all my Entity Framework Core (EF Core) entity classes using a DDD approach, i.e. all properties have private setters and all changes are made via method in the entity class. This makes it very clear what, and how, properties and relationships can be updated. (See the article “Creating Domain-Driven Design entity classes with Entity Framework Core” for more details on implementing DDD in EF Core).
But there are times where a fully DDD-styled entity can create more work for yourself. I’m working with a client on a large application that will have hundreds of Web APIs feeding an Angular front-end. In this case using the JSON Patch feature will save the frontend developers a lot of time, because there are libraries that will process a form and create a JSON Patch to update the properties that have changes. (see the article “JSON Patch With ASP.NET Core“ for a fuller description of JSON Patch).
In this article I talk about how I merged the use of JSON Patch approach into my DDD entity class approach. I also describe how I added support for JSON Patch in my EfCore.GenericServices library, while keeping all the good attributes of a DDD-styled entity class.
Introducing the hybrid DDD entity class
JSON Patch, with its update via the property’s setters, seems at odds with the DDD approach to entity classes where properties have private setters. But you can’t ignore the fact that JSON Patch, and associated libraries, can significantly speed up the building of single-page applications, using Angular, React, Vue etc.
DDD is all about making the entity class business-focused, where it deals in terms of the business needs rather than simple properties. So, having methods in the entity class with names like MarkOrderAsDispatched is better than setting the OrderStatus to Dispatched. Also, DDD wants to provide the developer with a clear idea of if/how a property can be updated, especially around relationships.
When building my EfCore.GenericServices library I made it capable of updating “standard” entity classes, i.e. with public setters, and also work with DDD-styled entities, i.e. where updates are done via calling public methods. In all of this I did allow what I call a hybrid DDD entity class, where some properties were updated by method calls, and some were updated via their public setters.
The typical use case for a hybrid DDD entity class is updating an address, with its various street, city, state, zip/postcode properties, inside entity class. I wanted to keep DDD’s tight control over the relationships, but in many cases updating the address properties has no business effects so it simplified things to make the properties’ setters public (the library would notice that and call AutoMapper to update the properties from a DTO).
While some might say that a hybrid DDD entity class breaks the DDD approach, but I believe it follows the spirit of what Eric Evans was getting at in his book, Domain-Driven Design. The ‘intent’ of what can be done with property is clearly communicated.
- If a property has a public setter then there are no business logic implications (other than validation).
- If the property has a private setter then it either has business logic implications or you wanted to provide a business-named access method, like my earlier example of MarkOrderAsDispatched. Either way you must update it via a method call because the setter is private.
Altering a DDD-styled entity to allow JSON Patch to work
By default JSON Patch will fail on a property with non-public setter. That is actually useful, as we can differentiate between the properties that we allow JSON patch to update, and properties that should only be updated by ctors or methods. This is where the hybrid DDD entity class comes in.
Here is an example from my EfCore.GenericServices.AspNetCore library, which contains some simple entity classes and Web API to show how things work (you can clone and run this locally to try this out – it uses an in-memory which is seeded at startup to make it easier to play with).
I created a simple hybrid entity class called TodoItemHybrid, as shown below
public class TodoItemHybrid { public int Id { get; private set; } [Required(AllowEmptyStrings = false)] public string Name { get; private set; } [Range(1, 5)] public int Difficulty { get; set; } //ctor and access methods public TodoItemHybrid(string name, int difficulty) { Name = name; Difficulty = difficulty; } public IStatusGeneric ChangeName(string name) { var status = new StatusGenericHandler(); if (name.EndsWith("!")) status.AddError("Business logic says the name cannot end with !", nameof(Name)); Name = name; return status; } }
You can see that the Name property (line 6) has a private setter and can only be updated via the ChangeName method (lined 18 to 26). That method has some (fake) business logic connected to it (see lines 21 to 22), so that must be updated via the ChangeName method.
On the other hand, the Difficulty property (line 9) is a simple property with no business logic attached (apart from validation), so I have given it a public setter. This means I can update it via the Microsoft.AspNetCore.JsonPatch library.
The way that JSON Patch works is you can provide a list of changes. In this simple case there is only one property, Difficulty, but you can provide an array of updates. Here is simple example containing two updates to the same class:
[ { "op": "replace", "path": "/FirstName", "value": "Jon" } { "op": "replace", "path": "/LastName", "value": "Smith" } ]
Using JSON Patch can cut down the amount of work required in the front-end code. A JavaScript library such as https://www.npmjs.com/package/rfc6902 can automate the creating of the JSON patches to send back to the server. This package has a createPatch feature that compares original data with new data (for instance, from a form) and generate a patch with all the changes to send to the server.
A look at the ASP.NET Core Web APIs
To apply such an update from a Web API controller, then you would need something like this
[HttpPatch("{id}")] public IActionResult HandCodedUpdate(int id, JsonPatchDocument<TodoItemHybrid> patch, [FromServices]ExampleDbContext context) { var entity = context.Find<TodoItemHybrid>(id); if (entity == null) { return NoContent(); } patch.ApplyTo(entity); context.SaveChanges(); return Ok(); }
The call sends the primary key of the entity you want to update (end of line 2), plus a JsonPatchDocument<T> patch (line 3) created by the caller. Then you have to load the entity you want to update (line 6) and if found you apply the patch and save the changes to the database (lines 11 to 12).
To make this more streamlined I have added a JSON Patch feature to my EfCore.GenericServices library. This wraps the ApplyTo with extra checks, optional validation and success/error messages. Here is the same example but using the GenericServices library:
[HttpPatch("{id}")] public ActionResult<WebApiMessageOnly> Update(int id, JsonPatchDocument<TodoItemHybrid> patch, [FromServices]ICrudServices service) { service.UpdateAndSave(patch, id); return service.Response(); }
Conclusion
DDD can provide many benefits, such as access methods which are named in a business-focused way, and these access methods are the definitive way to do something, as there is no other way in. I really like that level of isolation.
But most database tables have plenty of simple read/write columns that don’t need the level of access control. Writing access methods still works, but sometimes a simpler copy of data is sufficient. And when approaches like JSON Patch make the developers life simpler its worth adopting it in the right places.
In the end DDD is more a philosophy than a standard pattern – Eric Evans was saying “make the domain (business) problem the focus, not the tools and technology you use”. My hybrid DDD entity class might not be liked by some, but I think it still conveys the intent of the data, and it makes you a quicker developer.
Happy coding!
I also tried to use very similar approach, but I faced some issues.
1) Protecting api from the arbitrary changes.
For example, we have an api endpoint like this: PATCH /api/v1/profile/certificates.
Our application expects that only [model.ceritifcates] entity will be changed, but client can send any kind of the patches and change everything. We should provide additional logic (smth like schema validation, or regex validation for all paths in the JsonPatchDocument) to defend our system from unexpected changes / behaviors.
2) The relational databases do not guarantee row order.
JsonPatch requests applies changes to the nested collections by the resource index. We should sort data before the our application sends data to the client or update one.
But sort order can be changed if entities are added or removed.
3) If we want to update entity with the private setters, we can write custom ContractResolver
I want to fork the Microsoft implementation (from the official asp net core repo) and “patch” some behavior. For example, I want to provide ability for changes nested resources by some property instead of index (yes, I know that this non-compliant with the RFC)
I see what you are trying to do and might work for you. In my case I am using a DDD approach, so the entity class is in change and contains validation code. Therefore I only use JSON Patch for simple update, and anything complex is handled by DDD methods in the entity class.
Thank you for your reply 🙂
I am also using model-level validation, but the model can’t detect what properties should be changed for a specific case. The DDD approach is great and covers all BL operations very well. But the CRUD code with DDD is large and complex, so I want to change the entities using the JSON patch and validate them later.
With the NoSQL databases this approach works well. I’ve solved the problem with validation of allowed operations using FluentValidation. We should create a validator derived from AllowedForPatchingPathsValidator with the string array that describes allowed properties e.g. /certificates/[*]/*. The validator checks each operation with the regex and returns the error if the request contains unexpected paths.
Very nice. The only thing I would have changed is I would have followed the use of the AutoMapper example in the JSON Patch post you included instead of trying to directly patch the JSON DTO to the DDD Entity. You can control the mappings a bit more with AutoMapper, IMO.
Hi Jason,
You say “You can control the mappings a bit more with AutoMapper”. There are two forms of control, and I’m not sure which you mean. They are:
1. Controlling which properties can be updated
2. Controlling the mapping process, e.g. employing reverse mapping to map to relationships.
In item 1 I am in a DDD hybrid styled entity, so I control what properties JSON Patch can update by the public setters (The ApplyTo(patch) method throws an exception if you try to update a property that hasn’t got a public setter). If you look at the AutoMapper example in the JSON Patch article they only load the entity, so they are using AutoMapper to control which properties can be updated.
On item 2 you could do that, but because I am using a DDD design I only allow access to relationships via access methods. That means that I can’t use the complex reverse mappings that AutoMapper provides.
I hope that helps explain why I built it this way.
Correct me if I am wrong but you would not have your EF Context directly in the API Controller, would you? You would move that up to a Business Layer? I guess what I was getting at was that on the JSON Patch post (https://dotnetcoretutorials.com/2017/11/29/json-patch-asp-net-core/) at the bottom he provides an example of using AutoMapper to map in the request body (JSONPatchDocument) to the DTO. Then he hands the DTO off to the business layer. I prefer this method because then you can leave any existing logic and property access in the Domain Models alone and keep a pure (almost) DDD with Factory Pattern. I have setup AutoMapper Profiles to use the Static Creates on a DDD Model so that I can convert a DTO to a Domain Model. Yes its a bit more work but I find it to be ‘cleaner’ in the long run.
Like all things in software development, there are always multiple ways of doing the same thing and different reasons for doing them. There is not technically wrong with what you have shown.
This is wrong. The JsonPatchDocument.ApplyTo method implementation that comes out of the box uses Newtonsoft’s Json.Net. As long as the property is serializable, then JSON Patch works.
Hi Adrian,
That’s funny, as I have a unit test in my EfCore.GenericServices (see this link) that shows I get an error trying to update a private setter. I do use the ApplyTo method with the logErrorAction parameter, but seem to remember without it the ApplyTo threw an exception (can’t remember what type).
Are you using a different ApplyTo?? Mine came from the library Microsoft.AspNetCore.JsonPatch.
Well, I looked at Book.Title and see there’s neither a DataMember nor a JsonProperty attribute, so what do you expect with a private setter? I think you need to learn about serialization.
Yes, we’re talking about the same ApplyTo method.
No, by default the ApplyTo method does not have support of private setters. But it can if you create your own contract resolver (see my prev. commen)
I also like this approach a lot. In the past I was somewhat of a DDD purist to much, but I now have a customer that also has CRUD-contexts. CRUD (business-logic-anemic, e.g. Configuration) models are very useful in creational bounded contexts I believe. Thanks for sharing such an incredible well written article!
Hi Chris,
I’m glad that was useful to you, and thanks for your kind words.
I’m a working developer, with an interest in patterns and architecture. But my experience is that any patterns and architecture I dream up needs to be tested in a real application with the real pressures of delivery before you can say they a) work, and b) are useful.
This is a great article, thank you for putting this together I have a question for you: Why are two similar validations being done in two different places? I can see how using attributes in TodoItemHybrid can allow you to use a more configurable approach to validate that Entity. However, you are also validating the Name property in the constructor. Also, how can I take this Entity in isolation and test the Required and Range validations?
Hi Frankely,
I will try to answer your questions in the order you asked them.
1. “Why are two similar validations being done in two different places?” – I assume you are asking about the TodoItemHybrid I show in the article. I just wanted to show an example of how a method could return an error, and I added a (silly) test in the ChangeName method. The point I am trying to get across is that any methods in a entity class can optionally contain validation code and return a status.
2. “… also validating the Name property in the constructor”. That’s a mistake, and I have removed it. I use resharper which, by default, adds null checking. But in this case I wouldn’t want that because the error would have been found via the validation.
3. “how can I take this Entity in isolation and test the Required and Range validations?”. If you are using my EfCore.GenericServices then there is a extension method called SaveChangesWithValidation, which runs a validation before it calls SaveChanges. If any of the entities returns errors it doesn’t call SaveChanges and returns the errors.
I hope that clears up a few things.