Command Objects or Interfaces?
Sebastian Good

Every distributed system eventually requires messages to be written on the wire to be transmitted from one machine to another. In many cases these messages are hidden magic. Using WCF web services, or Thrift RPC, code-generated proxies make remote calls look like function calls.

// someone publishes thisinterface IPersonServer { AddEligibility(    string personId,     DateTime startDate, DateTime endDate);} // and through code-generation, you getvar IPersonServer magicProxy =  impressiveFactory("with://an-endpoint"); // and finally callmagicProxy.AddEligibility(personId, startDate, endDate);

That command travels over the wire as a message made of simple scalars like strings and numbers. There is value in exposing the command itself. When you work extensively with queuing, you have no choice but to make the message explicit. Some communication frameworks, like the lovely ServiceStack, embrace this message wholeheartedly. (That last link is actually worth a read, if a bit zealous.) Now instead you have:

// a POCO/DTO/PODclass EligibilityRequest{  publicstring PersonId { get; }  publiclong StartDate { get; }  publiclong EndDate { get; }} // this usually doesn't require code genvar endPoint = impressiveFactory("with://an-endpoint"); // now you just send the messagevar request = new EligibilityRequest(  personId, startDate.Ticks, endDate.Ticks);endPoint.Invoke(request);

Voila! An explicit message object you can put in lists, send through generic pipelines (say, to add authentication or logging concerns) and generally have fun with. This is all well and good and obvious at the application service level.

Hang on. If these are nice, why not embrace this pattern for our in memory domain objects? Should our aggregate root interfaces take these commands? Should we prefer option (1) or (2) below?

// 1. typical interfaceinterface IPerson {  AddEligibility(DateTime startDate, DateTime endDate);} // 2. explicit command interfaceinterface IPerson {  void AddEligibility(AddEligibilityCommand command);}

I don’t think there’s an obvious correct answer here. At a project in 2012 we tried both. And we ended up preferring option (1) for the actual domain objects. Here’s why.

What happens when you go implement AddEligibility on your Person domain object?

void AddEligibility(AddEligibilityCommand c) {  // how long is the period  long duration = c.endDate - c.StartTime;  // is it ridiculous?  if (duration < 0) { throw something; }  // how many days is that?  double durationInSeconds = duration / 1000000.;  double days = duration / 86400;  // it's valid if it's an exact number of days  if (days != Math.Round(days)) {    throw somethingElse; }   // and so on and so forth until  // we do something useful}

Did you notice the difference between the command object and the interface? The interface used DateTime, and the command object just used long because it’s more safely serializable.

DateTime is a useful value object with interesting operations like asking how many days are between two times. And it knows about daylight savings time. And leap days. And all the useful things that value objects do. You know, encoding important domain behavior.

We found when programmers had access to the message object inside our domain object, they’d take cruel advantage of it. As silly as it looks to not take advantage of the DateTime in the above example, that’s exactly what would happen. Domain logic that should been safely ensconced in value objects, shared cleanly within a bounded context, was instead re-implemented all over our domain objects. Really, who divides by 86,400 in a method meant to calculate eligibility windows?

The temptation to do that rarely arises inside a raw MVC controller, or ServiceStack service handler, since you’re so clearly at the boundaries of the system, and so clearly unpacking network messages. Inside those application services you better bet people quickly just did this

var startDate = new DateTime(c.startDate);var endDate = new DateTime(c.endDate);var person = personRepo.LoadPerson(c.personId);person.AddEligibility(endDate - startDate);personRepo.SavePerson(person);

And the person object’s implementation was more clearly free of translation concerns and leaked logic (in this artificial case, the logic of date manipulation):

void AddEligibility(TimeSpan duration) {  if (duration < TimeSpan.Zero) { throw something; }  if (duration.Days != duration.TotalDays) {    throw somethingElse;  }   // now do something useful}

Now the domain object can fluently use a nice value object from its domain (say, TimeSpan), and the application service can worry about message unpacking.

For some reason, this separation of concerns led to clearer code in the domain, and clearer flexibility in the application service layer. There was less temptation to treat these command objects as anemic domain objects.

So at least based on an actual programming team in the wild, my instinct is to convert these command objects to domain objects as quickly as possible in the well-known boundaries of the system. Your mileage may vary.

RECENT POSTS FROM
THIS AUTHOR