Command/query objects and beyond

« back

February 21, 2020 | Originally published at https://medium.com/ingeniouslysimple/command-query-objects-and-beyond-fcfefcf3ebee?source=friends_link&sk=35a3633afaae130a07cc33119a1478ea

First, a quick story:

Recently I’ve been reworking a Redgate-internal chatbot I wrote a few years ago. It’s done pretty well over the years, but I wanted to take it off the random VM it was running on and make it cloud-hostable.

The biggest change required was to rewrite the storage layer. I’d been playing rather fast and loose with the original designs, and needed to throw together some storage so that the bot could save data. I only needed key/value pairs at the time, so I threw everything into some JSON, saved it to a file on disk and called it a day.

Some time later, I realized I need to store lists of objects. I briefly considered upgrading the storage tech, before realizing that I could write everything out as a JSON array, then store that array as a string in the key-value store I already had! Yes, this story is partly a confession for my past sins :) Surprisingly, this JSON-in-JSON solution actually worked well for about five years.

Fast-forward to today. The upshot of this process is that I had core logic code which depended on an IListPersistenceApi<T> interface, which could load and save lists of arbitrary objects. This all worked pretty well: the implementing code was simple enough, and mocking the interface for unit tests was easy too.

Photo by Chris Tweten

The real problems started when I wanted to move the bot’s hosting to something a bit more managed. Switching to some cloud storage would make the bot a lot more portable, since the bot itself would become effectively stateless.

Unfortunately, real databases don’t work like my IListPersistenceApi<T> api does. While unit tests could easily swap out the storage layer, the client code still ended up being too coupled to the structure of the API I invented. I’d ended up writing client code like “1: fetch list of games. 2: filter to remove last item. 3: set whole list of games” to implement relatively simple operations. (While being wildly inefficient, this still worked well since the amount of data involved was tiny.)


So what went wrong? Thinking about this in hindsight: the main problem was that I approached the storage API by thinking what I knew I could write. The getList/setList API was super-easy to implement, but ended up making all the code around it worse, since it had subtle mismatches with what I actually needed to do.

I’d ended up just writing some generic repository of objects instead of implementing the operations that actually mattered. Generic repositories are a pattern that usually seem like a good idea at the time, but end up providing APIs which are either too general or too specific. They cause the code around them to contort itself into whatever shape the repository requires, and have the wrong boundary for transactions. They’re even worse if you create one repository per database table, since the client code now has to coordinate between repositories and manage their transactions itself!
Our database is leaking all over the place.


One commonly-proposed solution is “command/query objects”. The basic idea is to give each database operation its own dedicated object with any relevant parameters and an Execute() method. The object owns the transaction boundary and all the code needed to implement one conceptual operation against the database.

This pattern is quite nice. I particularly like how it isolates each database operation from the others, allowing client code to only depend on what it needs. The query object can also serve as its own builder if the query has many parameters to set. The pattern does have a few major downsides, however. Writing a whole class for each operation is a lot of boilerplate, especially for simple operations which don’t benefit from the builder pattern. Also, you may need to insert an extra factory seam for each object if you want to be able to swap out mock objects for testing.

The core idea we want to take from command/query objects is writing our API in terms of “operations against the database” instead of repositories of objects. We can go back to a repository-like interface for most cases, but we want to write our interface methods in terms of what the client code needs rather than what our database provides.

This idea of consumer-driven APIs is ultimately the lesson I think I want to take away from this whole story. Whether we bake it into our process or just keep it in mind as we code, designing interfaces that do only what the client code requires should prevent premature generalization and simplify code on both sides of the abstraction.

There are loads of advantages here, whether we’re splitting our database operations into separate objects or not:

Are there any downsides? I’m not sure, but I think there might be one: you might end up with a bit more logic behind the database layer than you’re comfortable with. If you prefer to keep as much logic as possible in code that can be unit-tested, you may end up with interfaces that are a tighter fit around the database. Ultimately it’s up to you to decide what’s best for your project.


So, to sum up: