Pragmatic Domain-Driven Design: supporting JSON Patch in Entity Framework Core

Last Updated: July 31, 2020 | Created: September 20, 2018

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!

0 0 vote
Article Rating
Subscribe
Notify of
guest
10 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Jason Haffey
Jason Haffey
2 months ago

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.

Jon P Smith
2 months ago
Reply to  Jason Haffey

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.

Jason Haffey
Jason Haffey
2 years ago
Reply to  Jon P Smith

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.

Adrian
Adrian
2 months ago

JSON Patch only works if the properties have public setters

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.

Jon P Smith
2 months ago
Reply to  Adrian

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.

Adrian
Adrian
1 year ago
Reply to  Jon P Smith

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.

chris lesage
chris lesage
2 months ago

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!

Jon P Smith
2 months ago
Reply to  chris lesage

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.

Frankely Diaz
Frankely Diaz
2 months ago

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?

Jon P Smith
2 months ago
Reply to  Frankely Diaz

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.