The journeylism of @yreynhout

On CQRS, DDD(D), ES, …

Guards and Queries in the domain model

Once you embrace that getters/setters are an anti-pattern in the domain model, there are other things that start becoming more difficult, at least at first glance. How are you going to enforce business rules in the domain model when you can’t query state using property getters? All too often people revert to enforcing a rule by querying an entity/aggregate for its state (using fields or properties), breaking encapsulation, causing yet another dependency. When multiple aggregates are used to establish a collaboration (e.g. an event or a transaction) this kind of behavior is probably reenforced. Yet the solution is very straightforward.

You introduce what I call Guard methods in your domain model. They should communicate their intent, i.e. the functionality they guard. In SOM these are called test methods, but given the multiple connotations of the word test, I chose guard as its replacement.

public class Order : Aggregate {
  public Order Place(Customer customer, ...) {
    customer.GuardPlaceOrder();
    //...
  }
}

public class Customer : Aggregate {
  private bool _hasMoreThanFiveOutstandingInvoices;

  internal void GuardPlaceOrder() {
    //The kind of rule(s) that go in here might change over time.
    Guard.Against(_hasMoreThanFiveOutstandingInvoices,
PlaceOrderErrorCode.CustomerHasMoreThanFiveOutstandingInvoices);
  }
}

public static class Guard {
  public static void Against<TErrorCode>(bool assertion, TErrorCode errorCode) where TErrorCode : IConvertible {
    if(assertion) throw new OperationException(errorCode);
  }
}

A very handy side-effect of guards is that the rules governing one functionality do no get mixed with other functionality. It also becomes obvious where to code the rule: in the guard member of the aggregate that has the state required to make the decision. Another advantage is the contextual error throwing. Even though you might visit two or more aggregates to establish/dissolve a collaboration, when you throw, you only have one thing to catch and you know what rule was violated (by giving it a name). This makes for a very powerful domain to commandhandler exception communication mechanism.

Sometimes you just need to compare state that lives in different aggregates. That’s when queries come in handy.

public class Trash : Aggregate {
  private string _countryOfOrigin;
  public void DumpAt(Place dumpster, ...) {
    Guard.Against(!dumpster.IsInSameCountryAs(_countryOfOrigin), DumpTrashAtError.TrashCannotBeDumpedOutsideTheCountryOfOrigin);
    //...
  }
}

public class Place : Aggregate {
  private string _country;

  //Your query method will be more sophisticated.
  internal bool IsInSameCountryAs(string otherCountry) {
    return _country.Equals(otherCountry);
  }
}

One might say that these query methods are glorified property getters. That may be so, still I think query methods (determine mine services) communicate intent better, transcend the “one property” notion and encapsulate the “how” better.  Additionally they shouldn’t alter state!

I haven’t found any rule I couldn’t implement using these two constructs, my domain model has become much more readable, and the place where “rules” go in code has become clearer. As an added bonus, command handler exception handling has become a matter of mapping a domain error code onto a command error code.

Advertisements

8 responses to “Guards and Queries in the domain model

  1. Bhoomi November 12, 2010 at 07:08

    I understand this is normal DDD, can you guide me as to how you will handle this scenario in CQRS strategy? In particular if you choose to handle one aggregate per transaction. Your guidance will be appreciated. Thanks..

    • seagile November 12, 2010 at 11:55

      Well, the answer is that there’s nothing to change. If you strive to only change a single aggregate per command/transaction, then just make sure you don’t change more than one aggregate. Guard members are there to enforce invariants and will possibly abort the command if any of those are violated. In case of collaborations, they are just cross aggregate rules for establishing/dissolving collaborations (represented as yet another aggregate). Query members are there to ask information of an aggregate in a meaningful way (i.e. using the DSL of the domain). Both are side-effect free: they do not change state of the aggregates they are executed upon. Only when all invariants are met, will the state change take place (using an event or traditional state change management (i.e. setting private fields or properties)) on the aggregate the command is for.

  2. Chris Nicola February 12, 2011 at 20:53

    Why not just tell the place you want to dump trash there:

    var place = repo.Get(command.PlaceId);
    var trash = repo.Get(command.TrashId);
    place.DumpTrash(trash)
    public void Place.DumpTrash(Trash trash) {
    trash.DumpAt(_country, ...);
    }
    public void Trash.DumpAt(Country country) {
    if (country != _countryOfOrigin)
    throw new DomainException(...);
    ApplyEvent(new TrashDumpedAt(...));
    }

    • seagile February 12, 2011 at 21:13

      Sure, that’s a viable solution in this contrived example as well. The only thing to be wary of is putting too much logic/responsibility in the Trash object, but in this case I don’t see any harm in your way of modelling the problem. I doubt any real waste-tracking system will ever contain the above method. After all, with most business software we are merely registering that something happened or ordering something to happen. It’s still people or machines doing the actual “behavior”.

      Yet, I think this blog post is important, because it offers advice on how to stay out of the mess that are properties and state based “testing/guarding”.

    • seagile February 13, 2011 at 16:00

      Upon reflection, I think the point where guard members start to break down is when you need to pass in lots of state to enforce one or more rules. Pretty soon, it’ll become unreadable or incomprehensible as to why you need to pass in that state. That’s a tipping point where I’d either go for query members or dig deeper into the model together with the domain expert.

  3. Pingback: Clarification behind the message that properties are bad. What I learned was: Tell don’t ask

  4. Jeff Turner July 17, 2011 at 18:44

    Isn’t it a bad idea to use exceptions to manage domain rules. They are very expensive to throw and catch. Has that changed?

    • yreynhout July 17, 2011 at 21:41

      This is domain code, probably executing asynchronous. So, this does not need to be the most performant code with regard to exceptions. Moreover, haven’t you heard, CQRS is about making the chances of that command fail really tiny/succeed really big. I wouldn’t optimise this code in the area of exception communication performance, rather in the area why this exception is occuring in the first place (especially if it’s occuring frequently). There’s also the opportunity (in case of event-sourcing I might add) to communicate failure as an event. Others have blogged about that strategy.

      I have seen both others and myself try treat domain code as yet another piece of .NET library/framework code. Suffice it to say it takes a different approach (of course without throwing common sense out the window). You’re free to disagree, but I haven’t come to this conclusion lightly. Readability is valued very highly, especially in DDD code. I don’t see myself sacrificing that.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: