Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Considering HTTP Methods PUT And PATCH Indicators Of An Anemic Domain Model And A Leaky Abstraction

By Ben Nadel on
Tags: ColdFusion

A few years ago, I wrote about how I don't like "Update" methods in my API design. In that post, I talked about my "feelings" on the matter because I couldn't really articulate the red flags that were going off in the back of my mind. Over the weekend, however, I was reading through Implementing Domain-Driven Design by Vaughn Vernon when it suddenly occurred to me that everything he was saying about the anemic domain model applied to the way that I think about HTTP API design. Finally, I think I have the right words needed to codify my feelings. I think the PUT and PATCH HTTP methods indicate an anemic domain model and a leaky abstraction. Which, in most cases, is not a good thing.

In Implementing Domain-Driven Design (DDD), Vernon talks about the "Ubiquitous Language", which is the set of terms (aka, Design Patterns) that the entire team agrees to use in order to describe a set of shared definitions:

Thus, the Ubiquitous Language is a team pattern used to capture the concepts and terms of a specific core business domain in the software model itself. The software model incorporates the nouns, adjectives, verbs, and richer expressions formally formulated and spoken by the close-knit team. Both the software and the tests that verify the model's adherence to the tenets of the domain capture and adhere to this Language, the same one spoken by the team. (Vaughn Vernon)

While I am a total noobie when it comes to Domain-Driven Design (DDD), when I read this passage, I translate it in my head to mean that the Ubiquitous Language describes the state of an application and the state transformations that can be applied to it in terms of "business use-cases". Now, in the book, Vernon looks at how the Ubiquitous Language applies to the domain model; but, I think it can also be applied to the HTTP API surface-area that sits above the application core.

For example, Vernon looks at a Customer entity that supports the following public setters:

  • Customer.setStreetAddress1( string )
  • Customer.setStreetAddress2( string )
  • Customer.setCity( string )
  • Customer.setStateOrProvince( string )
  • Customer.setPostalCode( string )
  • Customer.setCountry( string )

This set of methods creates an anemic domain model because it doesn't reveal any intention that relates to the business use-cases. There's no obvious way to tell whether or not these methods all need to be called together; or, under what conditions they are even allowed to be called. All that logic, complexity, and know-how is pushed into the responsibility of the consuming code.

This set of methods also creates a bit of a leaky abstraction because it exposes the fact that the address is stored as a set of properties on the Customer object, each of which can be mutated independently.

Vernon suggests that we can replace these anemic public setters with a method that describes the business-use cases in terms of the Ubiquitous Language. Something like:

  • Customer.relocateTo( Address )

Now, instead of knowing how, when, why, and in which combination to call a series of setter methods, we are calling a single method, .relocateTo(), which describes the state mutation in terms of the business use-cases - "relocation" - and the agreed upon term, Address.

Circling back to the whole point of this post, this refactoring away from an anemic domain modal can then be applied to the HTTP layer that is used to interact with the application core. Instead of allowing the Customer object to be manipulated with a PATCH method, which is essentially an anemic set of public setter methods:

PATCH /customers/{ uuid }

... we can create an API end-point that uses the same ubiquitous language and reflects the set of business uses-cases that the application supports:

POST /customers/{ uuid }/relocate-to

Using this mindset, we can all but get rid of the PUT and PATCH HTTP methods. Instead, we can use POST to create and transition state, GET to read it, and DELETE to remove it.

Heck, I'd even be open to getting rid of the DELETE HTTP method:

DELETE /customers/{ uuid }

... and replacing it with a POST and a term from the Ubiquitous Language:

POST /customers/{ uuid }/deactivate

If you start crafting your HTTP API surface area to reflect the processes of the business and the terms from the Ubiquitous Language, you could probably do everything you need with just GET and POST. I'm not saying you should do this, necessarily (getting rid of DELETE); I'm just having fun extending the thought exercise).

I've never liked the PUT and PATCH HTTP methods. But, I could only ever describe this through "gut feelings" on the matter. As I'm reading up on Domain-Driven Design (DDD), I think I finally have ways to articulate those feelings in more meaningful ways.

Epilogue On Amazon S3 And The PUT HTTP Method

In my post, I talk about using a meaningful API design that captures the richness of the business and reflects the ubiquitous language. However, that needs to be taken in context; Domain-Driven Design (DDD) is all about context. Take, for example, Amazon S3. The S3 API allows users to upload arbitrary data to an arbitrary end-point using the PUT HTTP method. In this context, this is completely fine because S3 isn't trying to reflect the richness of your business - it only tries to provide a storage API with a file-system-like schema. In that context, HTTP methods like PUT and DELETE make total sense since they align with the richness of the S3 business.

Epilogue On Leaky Abstractions And The PUT And PATCH Methods

When I say that PUT and PATCH can indicate a leaky abstraction, what I mean is that I often see PUT and PATCH methods being provided by engineers who view their API as a thin layer over a data-persistence mechanism. The thought process being, "I have a service that manages Customer data; so, I'll need to create end-points, like GET, POST, PATCH, and DELETE that allow that data to be managed." So, with this mindset, HTTP methods like PUT and PATCH can indicate that the service is nothing more than a leaky abstraction around the underlying database.

This becomes especially obvious when a service provides DELETE and PATCH end-points that never get used, indicating that the service was never designed to reflect the richness of the business, and only the implementation of the underlying database.



Reader Comments

It sounds like you might dig Event Sourcing.

Here's a pretty comprehensive look at CQRS + Event Sourcing: https://www.microsoft.com/en-us/download/details.aspx?id=34774

In 2019 I implemented an event sourced system which had me move to only using POST and GET http methods. I'm slowly working on an article about the experience :)

If you've never looked at ES before check out Greg Young videos on YouTube for an introduction.

Reply to this Comment

@Jason,

Thanks for the link, I will take a look. I know "of" Event Sourcing; but, I haven't had any hands-on experience with it. I've definitely watched a few videos from Greg Young. There was also another guy, Juri something? who also had a lot of articles about Event Sourcing. It's definitely a fascinating idea.

Reply to this Comment

@Ben,

Alberto Brandolini is also a great source for Event Storming, he named and codified the approach. In particular the things necessary to make it work speak to human psychology in a way that challenges people.

Cheers,
Michael

Reply to this Comment

@Ben,

Cool, looks like Udi contributed to that CQRS/ES book I mentioned. I've started to go through it over this winter break and it's enjoyable. I like the structure of the book a lot, as a journey of software development.

Reply to this Comment

@Michael,

Alberto is fantastic, as well as event storming. If you haven't seen it yet, check out what Adam Dymitruk is doing with Event Modeling at eventmodeling.org

Reply to this Comment

@Jason,

Ha ha, very cool -- small world with all this stuff. I'll take a look at the Event Modeling link. Sounds interesting.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.