Ayende @ Rahien

It's a girl

Limit your abstractions: You 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.

image

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:

  1. Controllers
  2. Views
  3. Entities
  4. Commands
  5. Tasks
  6. Events
  7. 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.

Comments

Omer Katz
02/13/2012 10:20 AM by
Omer Katz

"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 Mor
02/13/2012 11:35 AM by
Omer Mor

You're absolutely right. IBookingService is a terrible interface. It should have been called IBookingManager ! :-)

Khalid Abuhakmeh
02/13/2012 01:09 PM by
Khalid Abuhakmeh

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?

cbp
02/13/2012 01:53 PM by
cbp

Nice one. I think this post will be a game changer for a lot of people.

Ayende Rahien
02/13/2012 02:33 PM by
Ayende Rahien

Khalid, No, those aren't your own things, they belong to the infrastructure.

Dan Vanderboom
02/13/2012 02:36 PM by
Dan Vanderboom

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.

Chris Wright
02/13/2012 10:23 PM by
Chris Wright

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".

afif
02/13/2012 11:46 PM by
afif

I second Chris Wright. Can you please clarify Oren!

Betty
02/14/2012 03:45 AM by
Betty

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?

Ayende Rahien
02/14/2012 07:35 AM by
Ayende Rahien

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.

Daniel Lang
02/14/2012 01:18 PM by
Daniel Lang

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.

Ayende Rahien
02/14/2012 01:34 PM by
Ayende Rahien

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

Andrey Shchekin
02/14/2012 10:35 PM by
Andrey Shchekin

How would you actually create commands though (in a system that uses dependency injection)?

Ayende Rahien
02/15/2012 09:40 AM by
Ayende Rahien

Andrey, I wouldn't :-) I am discussing this in detail in a future post.

hammerip
02/17/2012 11:54 AM by
hammerip

I presume you are happy to allow additional abstractions for strategies identified within the system?

Ayende Rahien
02/17/2012 12:08 PM by
Ayende Rahien

hammerip , I am not sure that I am following the question

hammerip
02/17/2012 12:21 PM by
hammerip

Sorry, badly worded. I mean if you need to use strategy pattern, e.g. ICargoRouteCalculator

Comments have been closed on this topic.