Limit your abstractionsYou only get six to a dozen in the entire app
Let us take a look at another part of the DDD sample application. This time, the booking service.
One thing that is really glaring at me is that we have a mix of both commands and queries in this interface. Also, just consider the name. It is a Booking service, but it doesn’t actually seem to have an actual meaning in the application itself. There is no entity named Booking, and except for the BookNewCargo, there is no mention of booking anywhere in the application.
You know what, maybe there is logic in the RequestPossibleRoutesForCargo that is actually meaningful beyond a simple query. Such as charging the customer for the calculation, or reserving space on the route that we selected, etc.
At any rate, I don’t like this service at all. In fact, any time that you have something that is called XyzService, you ought to suspect it. Let me take this one step further. I don’t like that it is an interface, and I don’t like how it is composed. I don’t see it as an independent thing. Going further than that, I don’t really see a reason why we would need an interface here. You might have noticed what the title of this series is. I want to limit abstraction, and IBookingService is an abstraction, one that I don’t see any value in.
In most applications, I like to have a very small number of abstractions. Usually in the order of half a dozen to a dozen (top!). I usually think about them like this:
- Controllers
- Views
- Entities
- Commands
- Tasks
- Events
- Queries
Sometimes you have a few more, but those are the major ones. Controllers,Views and Entities are fairly obvious, I would imagine. But what about the rest?
Command is a package “thing” that happen immediately. Tasks are very like commands, except that they don’t have an explicit execution date and Events allow you to build smarts into the system. We have already seen how I handle events, in the previous posts in this series. And Queries should be fairly obvious as well.
Let us tackle Commands now, and then explain why they are bad later on.
When I said that I don’t want abstractions, I meant that I don’t want an interface and an implementation, and some way to connect the two, etc. I don’t really see a lot of value in that for the common case.
Let us break it apart into commands, which would give us this:
public class AssignCargoToRoute : Command { public Itinerary Itinerary { get;set; } TrackingId TrackingId { get;set; } public override void Execute() { } } public class BookNewCargo : Command { public UnLocode Origin {get;set;} public UnLocode Destination { get;set; } public DateTime ArrivalDeadline {get;set;} public TrackingId Result {get;set;}public override void Execute() { } }
I am very fond in having a base class for those types of things. The base class provide me with the infrastructure support for the command in question.
In my next post, I’ll go over why I don’t like this approach, and discuss other ways to structure things so it is more suitable for an actual application.
More posts in "Limit your abstractions" series:
- (22 Feb 2012) And how do you handle testing?
- (21 Feb 2012) The key is in the infrastructure…
- (20 Feb 2012) Refactoring toward reduced abstractions
- (16 Feb 2012) So what is the whole big deal about?
- (15 Feb 2012) All cookies looks the same to the cookie cutter
- (14 Feb 2012) Commands vs. Tasks, did you forget the workflow?
- (13 Feb 2012) You only get six to a dozen in the entire app
- (10 Feb 2012) Application Events–event processing and RX
- (09 Feb 2012) Application Events–Proposed Solution #2–Cohesion
- (07 Feb 2012) Application Events–Proposed Solution #1
- (06 Feb 2012) Application Events–what about change?
- (03 Feb 2012) Application Events–the wrong way
- (02 Feb 2012) Analyzing a DDD application
Comments
"In most applications, I like to have a very small number of abstractions. Usually in the order of half a dozen to a dozen (top!). I usually thing about them like this:" You meant think and not thing.
Omer, yes, fixed
You're absolutely right. IBookingService is a terrible interface. It should have been called IBookingManager ! :-)
Would you qualify any use of an interface as an abstraction that goes against your six to twelve count?
For example, if I use IDocumentSession or IQueryable instead of the concrete implementation in my code, did I just use one up? Or is it more about creating wrapper classes for encapsulating logic?
Nice one. I think this post will be a game changer for a lot of people.
Khalid, No, those aren't your own things, they belong to the infrastructure.
I agree with the general premise. Having a small number of the right kind of abstractions is important for maintaining the coherence of code organization.
Events provide endpoints for reactive code to attach Tasks or Commands as responses.
You refer to SL/WPF-style Commands, but what do you mean by Tasks? TPL Tasks?
Commands packaged as separate classes are often too granular, but delegate commands are possible as well. I also occasionally invoke remote commands on a server in the same way I invoke queries.
What drives me nuts is the inconsistency of seeing XyzService alongside AbcManager, etc.
It's a tad ambiguous whether you mean to say "I don't like this approach that I just outlined with the Command base class and various implementations; and I'll discuss why in my next post", or "I don't like the approach that the sample application took; and I'll discuss in further detail why I dislike this approach in my next post".
I second Chris Wright. Can you please clarify Oren!
Given a standard ASP.NET MVC website would you consider Custom ActionFilters, ActionResults, HtmlHelpers, ModelBinders, Validators, Route Constraints etc in your list of abstractions or are they just infrastructure? If they are just infrastructure how are they different from most the things you listed?
Betty, No, they are all infrastructure. If you have many of them, that indicates a problem with either bad infrastructure or wrong headed use of it.
Oren, I totally agree with except for the testing thing. I often find myself introducing abstractions and interfaces just for testing purposes, so that I can stub and mock them out in my tests.
What does that mean? Do I structure my code the wrong way? I'm absolutely sure that I don't generally test too much, so I guess I probably test the wrong things or have my code layed out wrong.
Daniel, Yes, you are doing it wrong :-) You don't need all of that for testing, I am actually going to touch testing specifically in a later post
How would you actually create commands though (in a system that uses dependency injection)?
Andrey, I wouldn't :-) I am discussing this in detail in a future post.
I presume you are happy to allow additional abstractions for strategies identified within the system?
hammerip , I am not sure that I am following the question
Sorry, badly worded. I mean if you need to use strategy pattern, e.g. ICargoRouteCalculator
Comment preview