Encapsulation and thinking behaviorally


I’ve been reading quite a bit lately about setters and encapsulation, but for whatever reason, it never really “clicked” for me.  I thought I was developing behaviorally, as I’ve been using the Context/Specification style of BDD specifications for a while.  I even go outside-in, starting from the outermost layer of the application, writing specs towards the inner parts of the application.  The outer clients dictate the inner behavior.

Aaron summed it up as well, as BDD specs should consider the audience of who is reading the specs.  Looking back at specs I have written over the past year, far too many focus on the technical motivations behind the behavior, rather than the business/domain motivations.  Ubiquitous language belongs not only in the Entity and Service names, but in the names of the tasks/operations in the model.  But if all I’m doing is setting a bunch of properties on my model, in many cases I’ve completely lost the business motivation on why those properties should be set the way they are.

In one domain I’ve worked on, quotes always need to be created before an order can be placed.  Once an order is created, the original quote needs to be updated.  Here’s one spec we had to verify this behavior:

public class When_executing_the_quote_completion_step : SpecBase
{
    private Quote _quote;
    private Order _order;

    protected override void Before_each_spec()
    {
        var quoteCompletionStep = new QuoteCompletionStep();
        
        _quote = new Quote {Status = QuoteStatus.InProgress};
        _order = new Order();

        quoteCompletionStep.Execute(_quote, _order);
    }

    [Test]
    public void Should_set_the_status_to_completed()
    {
        _quote.Status.ShouldEqual(QuoteStatus.Completed);
    }

    [Test]
    public void Should_set_the_order_property()
    {
        _quote.Order.ShouldNotBeNull();
        _quote.Order.ShouldEqual(_order);
    }

    [Test]
    public void Should_set_the_order_date()
    {
        _quote.DateOrderPlaced.ShouldBeLessThan(DateTime.Now);
    }
}

We have a quote completion step as part of a larger order submission pipeline.  Unfortunately, nothing in the context description nor the behavior descriptions provide any clue to what the business motivation behind “completing a quote” might be.  Or what “completing” a quote means other than setting a bunch of properties.  Because our specifications focused on the technical implementation, it’s no surprise that the implementation is just a bunch of property setters:

public class QuoteCompletionStep
{
    public void Execute(Quote quote, Order order)
    {
        quote.Status = QuoteStatus.Completed;
        quote.DateOrderPlaced = DateTime.Now;
        quote.Order = order;
    }
}

There are a few problems with the lack of state encapsulation here.  We’ve set properties on the Quote, but we’ve lost all context on why those values changed, or when they should change.  At Udi Dahan’s class this week, one of the concepts we focused on were the business tasks, operations and events.  Customer added, product deleted, contract expired.  Looking at our domain model, we’ve encapsulated the implementation of state, but not the reasons why that state should change.  We’re treating our Entities as components, which aren’t too interesting.

Suppose instead, we wrote our specs with the business behavior in mind:

public class When_completing_a_quote_from_an_order_placed : SpecBase
{
    private Quote _quote;
    private Order _order;

    protected override void Before_each_spec()
    {
        var quoteCompletionStep = new QuoteCompletionStep();
        
        _quote = new Quote {Status = QuoteStatus.InProgress};
        _order = new Order();

        quoteCompletionStep.Execute(_quote, _order);
    }

    [Test]
    public void Should_mark_the_quote_as_completed()
    {
        _quote.Status.ShouldEqual(QuoteStatus.Completed);
    }

    [Test]
    public void Should_correlate_the_corresponding_order_with_the_quote()
    {
        _quote.Order.ShouldNotBeNull();
        _quote.Order.ShouldEqual(_order);
    }

    [Test]
    public void Should_note_the_time_the_order_was_placed()
    {
        _quote.DateOrderPlaced.ShouldBeLessThan(DateTime.Now);
    }
}

Looking at the context and behavioral descriptions now, it wouldn’t be that much different than a person describing the quote and order process without computers.  This description could match the quote completion process of 100 years ago.  Once our specs become more business-centric, we’re shifting our mind away from technical implementations.  This shift can lead to more task or operation-based domain models:

public class Quote
{
    public Quote()
    {
        Status = QuoteStatus.New;
    }

    public QuoteStatus Status { get; private set; }
    public Order Order { get; private set; }
    public DateTime DateOrderPlaced { get; private set; }

    public void CorrelateOrderPlaced(Order order)
    {
        Order = order;
        Status = QuoteStatus.Completed;
        DateOrderPlaced = DateTime.Now;
    }
}

Instead of users of Quote needing to worry about what the valid state of a Completed quote is, the operation or task of correlating an Order placed will ensure that the Quote’s state is valid.  Which is where this type of logic belongs, I believe.  Once we start thinking in a business-oriented, behavioral mindset, we can start seeing the business tasks, operations and events that work on our entities.  As properties encapsulate merely the implementation of state, it’s still up to the client of the entity to figure out what is valid.  DateOrderPlaced should only have a value when a correlated Order is submitted.  With our new implementation of our domain model and specification descriptions, this motivation is captured explicitly.

Fluent hierarchical construction