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.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in BDD, Behavior-Driven Development. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://colinjack.blogspot.com Colin Jack

    Agree on language, as you say “When_completing_a_quote_from_an_order_place” does what it says on the tin (and in business language).

    I can also definitely see BDD helping us see where a responsibility and in this case it makes sense to place the responsibility on Order. I’d go a step further and say that the default for domain related behavior should be that its in a method in the domain (unless its workflow/process logic that sometimes needs to sit outside the domain). If you go with this mindset then you’re really thinking what Entity/Value Object (or as a fallback Service) should this behavior go on, which I think can be very useful.

    Still doesn’t turn me against setters though, I can see the sense in an almost complete ban on setters but even if you do that people without a good design mindset will just find another way of writing procedural code. I guess my thinking is still that good developers will make good use of setters in the domain and will know when to use a method.

  • http://scottic.us Scott Bellware

    Jimmy,

    Saying that the spec language is business-centric still betrays a technology-centric bias.

    This language is as business-centric as the code is. Everything in a solution is business-centric – even the lambda expressions.

    The point I’ve been making for a year or so is to “describe the experience, not the implementation”.

    Another way to say this is that specifications are written in user goal language.

    To say something is “business-centric” is to speak from a programmer’s biases and perspective with a lack of consciousness of the bias and perspective.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Colin

    It’s not that setters are completely bad, just that they often don’t capture the “why”. Why did the value change, what was the business motivation behind that behavior. That’s often missing when the responsibility for maintaining correct Entity state is spread throughout the application.

  • http://vladan.strigo.net Vladan Strigo

    Hi Jimmy,

    I am afraid that I am having some similar misconceptions as you’ve had. Your post helped me put a finger on them. Only, this sounds great for applications which are geared towards the business user (=client application).

    But what about specs for parts of frameworks? those are still geared towards users – but technical ones?

    Example… UnitOfWorkGateway:

    Context:
    When_UnitOfWorkGateway_is_used_to_start_an_non_nested_Unit_repetedly_2_times

    Specs:
    - Then_the_first_Unit_should_be_created
    - And_Gateway_IsUnitStarted_for_first_Unit_should_be_true
    - And_second_Unit_should_be_created
    - And_first _Unit_and_second_Unit_should_be_the_equal

    How do you see these being expressed in the context of your post (which is I think on the correct track)?

    Thanks,
    Vladan