A friend and colleague, Steve Morris and I, have been attempting to propagate the naming of this pattern for quite some time and it’s about time I wrote about it. In the vein of the AAA pattern of unit testing, we’ve been promulgating the AAA of code responsibility: Assert. Act. Announce.

  1. Assert. Check your preconditions.
  2. Act. Do the thing. And just the thing. No more.
  3. Announce. Shout that the thing was done.

Consider the common case of a customer’s changing their subscription level to an online service. Let’s say that the simplified sequence looks something like:

  1. The customer changes their subscription level.
  2. The customer is sent a notification email.
  3. The customer’s billing schedule is adjusted.

We could model it something like this:

public class CustomerService : IAntiPattern
{
    IRepository<Customer> _customerRepository;
    IEmailSender _emailSender;
    IBillingService _billing;

    // ...

    public void ChangeSubscriptionLevel(Guid customerId, SubscriptionLevel subscriptionLevel)
    {
        var customer = _customerRepository.Get(customerId);
        customer.SubscriptionLevel = subscriptionLevel;
        _emailSender.SendSubscriptionChangedEmailTo(customer);
        _billing.ReschedulePaymentsFor(customer);
    }
}

There are several problems with this approach:

  • It’s slow. The customer has to wait until an email is generated and sent before they can be signed up.
  • It’s fragile. If the email server is down, the operation will fail.
  • To add a step to the process, we need to change existing code.
  • The code is resistant to change.
  • The CustomerService becomes a dumping ground for all things customer-related.
  • When we add behaviours to the CustomerService, our existing tests break.
  • Anything can put our Customer instance into an invalid state just by modifying its properties.

If we were to add to the sequence so that it now looks like this:

  1. The customer changes their subscription level.
  2. The customer is sent a notification email.
  3. The customer is sent a notification text message. (New!)
  4. The customer’s billing schedule is adjusted.

then a couple of things happen. The first is that we need to change our existing CustomerService to add behaviour. We’re going to need to change the ChangeSubscriptionLevel method, and we’re also going to need to change the constructor to include some kind of ITextMessageSender. That means that all of our existing unit tests will immediately fail to compile.

Think about what we’ve just done. We’ve not changed the correctness of our existing code yet our unit test are failing. In other words, we’re teaching ourselves the lesson that whenever we add functionality, existing tests break. This is exactly the opposite of what we want, which is that tests only break when we’ve broken the functionality that they specify.

Enter the AAA pattern: Assert. Act. Announce.

If, instead of our CustomerService, we just modelled a customer with their behaviours, what might be different?

public class Customer
{

    // ...

    public void ChangeSubscriptionLevel(SubscriptionLevel subscriptionLevel)
    {
        // Assert. Check preconditions.
        if (subscriptionLevel == SubscriptionLevel) throw new DomainException("The new subscription level must be different to the old one");

        // Act. Do the thing.
        SubscriptionLevel = subscriptionLevel;

        // Announce. Shout that the thing was done.
        DomainEvents.Raise(new CustomerChangedSubscriptionLevelEvent(this));
    }
}

By publishing the CustomerChangedSubscriptionLevelEvent we allow downstream behaviours to be decoupled from the original action. We get several benefits from this:

  • We can make the assumption that any time we are passed a domain entity, it is in a valid state. In other words, the customer entity can say no, and refuse a change that would put it into an invalid state.
  • The Customer class doesn’t have to change at all when we add a downstream behaviour.
  • The existing tests continue to compile without change.
  • The existing tests also continue to pass without change.

It’s worth noting that the Customer class in this case is not responsible for sending an email, sending a text or informing the billing department - and nor should they be. The Customer entity’s job is to represent a customer, not orchestrate a business process.

To bolt additional downstream behaviours onto the customer’s action, we use event handlers, e.g.

public namespace WhenACustomerChangesTheirSubscriptionLevel
{
    public class SendThemAConfirmationEmail : IHandleEvent<CustomerChangedSubscriptionLevelEvent>
    {
        IEmailSender _emailSender;

        // ...

        public void Handle(CustomerChangedSubscriptionLevelEvent e)
        {
            _emailSender.SendSubscriptionChangedEmailTo(e.Customer);
        }
    }
}

This isn’t a new pattern. Udi Dahan wrote about it in 2009 in his Domain Events – Salvation post, we’ve been using it for over a decade and the Observer pattern is an original GoF pattern. At the ThoughtWorks Event-Driven Architecture Summit in 2017, a number of us started to thrash out a bit more of an opinion about what “event-driven” actually means, and the various flavours of it. Martin Fowler’s article on What do you mean by “Event-Driven”? is one of the outputs of that summit. We’re generally calling pattern that we’re using in this case event notification or event choreography.

If we want multiple behaviours, we just add mulitple handler classes; the principle being that in order to add behaviours, we just add code. We don’t want to change existing code unless the existing behaviour of the system needs to change. We do need to understand which events should be handled synchronously versus which can be handled asynchronously but there are straightforward solutions to that problem.

The pattern to remember, though, is AAA: Assert. Act. Announce.