Domain Events on Creation

2 minute read

When not enjoying my funemployment, I’ve been talking to a lot of folks lately about domain events. One of the questions that’s come up multiple times is how to properly raise events when creating something. Say, a batch of noodles.

I’m a simple man, so I reckon this is fine:

class Noodles
{
    use EventGenerator;

    public function __construct()
    {
        $this->raise(new NoodlesCreatedEvent($this));
    }
}

(EventGenerator trait code as a gist or video explanation)

At first glance, this might seem like it violates “Don’t Do Work In The Constructor”, especially the part about creating new dependencies. That said, if your raise method is only queueing the event internally and not tossing it off to an external dispatcher or static method, are you really doing any work? In my mind, these simple state-setting operations aren’t much different than initializing a scalar value in the property declaration. I’ve never seen anyone argue against:

class Noodles
{
    protected $knots = 0;
}

Also, while we are creating the Events here, I would argue these Events are the business of this entity and it should have all the necessary dependencies to create them at hand. Even Misko’s advice makes allowance for creating value objects (which these Events are akin to). Events do not have behavior, which is often what you need to substitute.

In short, the rule is nuanced and blindly applying it here will likely result in a more complex solution. The constructor example above is delightfully simple, even dumb. When using a rule like this, try to understand intent: One of the main goals in “Don’t Do Work In the Constructor” is making substitution and thus testing easier. However, the events are often the things we test for!

(Please note I am not arguing against this rule in general, I’m a firm believer in it. There is a special hell for 3rd-party libraries that connect to databases on creation).

As a side note, some DDD practitioners like Mathias Verraes recommend using static constructors named after the actual operations in your domain. In other words, noodles are never just “created”, they’re stretched from dough, for example. In these cases, you could create the event(s) in the static method and pass it through the constructor, which then raises it.

class Noodles
{
    use EventGenerator;

    public static function stretchFromDough()
    {
        $noodles = new Noodles(new NoodlesStretchedEvent());
        return $noodles;
    }

    protected function __construct($event) {
        $event->setNoodles($event);
        $this->raise($event);
    }
}

This works well, especially if you have multiple ways of creating noodles, such as stretching or rolling. A downside is that if you want to pass the entity into the event itself (rather than the ID which might be better) then you need to do that in two stages, either by setting the Noodles on the Event or adding the event to the Noodles.

However, the ever-clever Alexander Mols pointed out this can be simplified because PHP is class scoped, not instance scoped (see example 3). In that case, you can just invoke the raise() method inside the factory method.

class Noodles
{
    use EventGenerator;

    protected function __construct() {}

    public static function stretchFromDough()
    {
        $noodles = new Noodles();
        $noodles->raise(new NoodlesStretchedEvent($noodles));
        return $noodles;
    }
}

Updated: