Exceptional cases should be exceptional

It's a rather depressing fact of development that a significant amount of effort and code has to be devoted to non-standard situations, generally well in excess of the proportion with which they're actually encountered in use. Failure to account for these issues means that our code will be unreliable, wrong, insecure or unusable (or some combination thereof) in production. This puts us as a profession in the habit of thinking and talking about the exceptional cases. Generally this is a good thing but sometimes the influence is inappropriate.

I was having a discussion with a colleague recently in which I was explaining one of my heretical views on software architecture. My general belief is that query layers in systems should be very, very thin. In general a controller in the platform should perform a query directly, map straight into a wire format and return the data directly without going through services, repositories, ORMs and other unnecessary abstractions.

The question I got in response to this was around handling exceptional cases. If you don't have services how do you handle complex cases where you need to query multiple services and compose a response. You could argue that there are alternate ways to support this but for the sake of argument let's assume that this is actually necessary in this case, which is not at all unreasonable.

The answer to this is really quite simple. I believe in making query layers as simple as possible, but no simpler. In the general case this means direct querying into the wire format inside a controller. It's simple, direct and pretty efficient. However something that needs to compose multiple sources is not the general case (or at least it shouldn't be for most systems). In this case it's entirely appropriate to use some of the more complex elements we eschew for the simpler scenarios.

The more complex architecture is only required for a subset of cases. It's exceptional to need those elements. Where then is the justification for applying them to all cases? They don't deliver value sufficient to justify their existence for most cases. If they are only needed in exceptional cases the architecture that includes them should also be exceptional.

The key point here is that it's OK to have a set of approaches within a system, provided those approaches are well designed to meet the specific problem sets to which they are applied. You can then choose between them as appropriate, paying only those costs that necessary. This can apply at a small scale, such as choosing whether to call out to a separate service class or not, or at a larger scale such as determining whether a component will use simple CRUD or more complex DDD and/or CQRS/ES approaches. Picking the right tool for the job is a matter of appropriateness not complexity.

Colin Scott

Read more posts by this author.