My twitter buddy, and a person I quite admire, Alan Stevens has been looking into the latest “fad” of repository hate. He’s been looking at alternatives, and has created a simple sample application, available at https://github.com/alanstevens/CommandQueryExample . He’s asked for feedback, and seeing that these are often common questions, with his permission, I’m giving mine in the form of a few posts. So, let’s get started.
What It’s Not
The sample is not an attempt at CQRS – Alan mentions that in the readme. However, the application does seem similar to many (not so good) CQRS implementations I’ve seen. I won’t talk about CQRS much here, I might do in a future post. I’m (initially) reviewing the code from a non-repository approach. Some elements of DDD and even some CQRS might seep in, as the sample borrows some things from them.
High Level Impression
The implementation is very very technical. There are numerous abstractions in place, while the end result is simply reading and writing to a single table. One of the key benefits of having query objects (vs repositories) is overall simplicity, and moving away from uniform abstraction levels. I often talk about non-uniform abstraction layers, where different parts of an application have different levels of abstraction. Instead of everything going through UI –> Service –> Repository –> Data, some queries may be composed of many sub operations, while others can be straight forward. Functional languages intrinsically support non-uniform abstraction without much effort. In OOP languages, sometimes a little work is needed. One result of this though, is that generic repositories become less useful.
The sample implementation actually does a lot of work to provide a uniform infrastructure. That’s not necessarily a bad (or good) thing, but I do see many teams get so focused on the infrastructure that actual business context loses focus.
Why the Complexity?
The readme specifies that the goal is to not have DbContext acting as a god object, and to experiment with non-repository approaches. Assuming the goal is not DDD / CQRS / etc., the target is to simply do CRUD. And it does seem like a lot of work to do CRUD. And when I say work, I don’t necessarily mean lines of code, rather the sheer number of things a team member would need to know to figure out how to add a query, command, or anything else. The entry barrier to this “architecture” seems quite high.
Person Exposed Everywhere
I did say I wouldn’t get too much into DDD, but the library called “Domain” has a single mutable class called Person, that’s used everywhere. It provides a unified view of a person across all operations. In my experience, such an approach soon degenerates and queries / repositories / command handlers / etc. get more and more burdened with responsibilities they don’t really care about. SRP isn’t necessary about class responsibilities – the single responsibility concept is from an actors perspective. At any time, there should only be one actor who’s needs prompt a change in a unit. Whether that’s a single class, multiple classes composed together – SRP still applies. For the Person class, there could be numerous reasons for it to change. The coupling / cohesion factor is also concerning. I wouldn’t really call it a domain object. In fact, I wouldn’t call it an object at all. It’s simply a data structure. And if we expose the data structure to clients anyway, why bother with all the abstraction?
I don’t really like the dispatcher one bit. I understand why it exists, and what it’s trying to do (I’ve been implementation in memory queuing systems of lots of different types since 2009), but it seems to simply be a wrapper for a context without exposing the context. And the use of statics in the factory is a hacky workaround. There are pros and cons to dispatchers, and I’ll cover them in a future post.
Last Words for Today
There might be other things that I think of later, but for now, my initial impression is that it is implementing patterns used with repositories without explicitly using repositories. This has resulted in quite complex code to do something very simple. It’s missing out on some of the benefits of using query objects.
That’s enough for today. I’ll dig deeper into a few things. But first, I’ll do a simple app that’ll achieve the same thing, but in a way I’d probably start off with. It’ll be far less enterprisey… and least that’s one thing I’ll be gunning for. Stay tuned!!