Good ORMs do not call __construct a.k.a. where to fire my Domain Events

tl;dr: Entity constructor should be called once in its entire life. That’s why good ORMs don’t use constructor to reconstitute objects from the database. Good ORMs use reflection, deserialization or proxies. So, fire your Domain Events in the constructor.

Last week, I saw a presentation about going from coupled-to-framework code to a Domain-Driven Design approach in a sample application. It was a good talk. However, in my honest opinion, there were some mistakes that are important to address. In order not to troll, I have checked with the speakers to write this post ;).

One of the mistakes, that I would like to point is where a Domain Event about “a new product was created” should be trigger specifically inside the Product Entity.

Speakers were showing a “Video” Entity with a named constructor called “create”. They were calling new inside the named constructor and then firing the event. I mean, the Domain Event was not fired inside of the real constructor. Let’s see the code we’re talking about (the whole repository can be found here):

final class Video extends AggregateRoot
{
    private $id;
    private $title;
    private $url;
    private $courseId;

    public function __construct(VideoId $id, VideoTitle $title, VideoUrl $url, CourseId $courseId)
    {
        $this->id       = $id;
        $this->title    = $title;
        $this->url      = $url;
        $this->courseId = $courseId;
    }

    public static function create(VideoId $id, VideoTitle $title, VideoUrl $url, CourseId $courseId)
    {
        $video = new self($id, $title, $url, $courseId);

        $video->raise(
            new VideoCreatedDomainEvent(
                $id,
                [
                    'title'    => $title->value(),
                    'url'      => $url->value(),
                    'courseId' => $courseId->value(),
                ]
            )
        );

        return $video;
    }

    // ...
}

The reason about this approach was because when dealing with databases, new can be called multiple times when fetching back the object. I think here’s is the misunderstanding. :)

Consider the following version that fires the event inside the __constructor:

final class Video extends AggregateRoot
{
    private $id;
    private $title;
    private $url;
    private $courseId;

    public function __construct(VideoId $id, VideoTitle $title, VideoUrl $url, CourseId $courseId)
    {
        $this->id       = $id;
        $this->title    = $title;
        $this->url      = $url;
        $this->courseId = $courseId;

        $this->raise(
            new VideoCreatedDomainEvent(
                $id,
                [
                    'title' => $title->value(),
                    'url' => $url->value(),
                    'courseId' => $courseId->value(),
                ]
            )
        );
    }

    public static function create(VideoId $id, VideoTitle $title, VideoUrl $url, CourseId $courseId)
    {
        return new self($id, $title, $url, $courseId);
    }

//...

In terms of DDD, the second approach is better. The Domain Event is fired when the Entity is created. But if you consider the first approach, a part from the mistake of the constructor being public (it’s a good practice to make it private with named constructors), the Domain Event is fired right after the Entity is created.

However, let’s argue about the possible problem with the last approach with ORMs. They were defending that the first approach is better because when you fetch an instance from the Database it doesn’t call the constructor so the Domain Event is not fired again. That’s not right.

Let’s see some Doctrine code.

Doctrine depends on “doctrine/instatiator” package on composer.json

...
"doctrine/instantiator": "~1.0.1",
"doctrine/common": ">=2.5-dev,<2.7-dev",
"doctrine/cache": "~1.4",
...

Doctrine uses Instantiator to build new instances without calling new. Check it on github.

    /**
     * Creates a new instance of the mapped class, without invoking the constructor.
     *
     * @return object
     */
    public function newInstance()
    {
        return $this->instantiator->instantiate($this->name);
    }

This was not the behaviour with Doctrine 1. But it is with Doctrine 2. In fact, six years ago, in 2010, the internal implementation was not using the Instantiator object, but a PHP internal trick. Let’s see it.

public function newInstance()
{
    if ($this->_prototype === null) {
        $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
    }
    return clone $this->_prototype;
}

In my personal experience, in the LastWishes project, I sign up a user once and then sign in multiple times. The Domain Event is fired in the constructor and there is only one creation event fired ever.

How many times new should be called in the life of an Entity?

Beyond this specific topic, I would like to point another even more general idea around this topic. How many times new should be called in the life of an Entity? One. Correct! It doesn’t matter how this Entity is persisted and retrieved, an Entity is created once. Then it remains alive persisted in memory or in a database. No-one should invoke new again.

Hope it helps.

  • This was discussed the other day in the BcnEng codelytv channel I think, and some people say that ORM use the constructor to retrieve information. It’s nice to know a bit more of the intern of the Doctrine and confirm that new is called once :)

  • Pingback: Firing Domain Events in the __constructor | Carlos Buenosvinos()

  • Fernando Arconada

    agree, and another (maybe controversial point). and.. if you use a non deterministic function inside __construct like $created = new DateTime() ??
    If you call new several times you are changing your object state

  • Fernando Arconada
  • snapshotpl

    Interesting post. I have same problem which solution is better. I choose first, why? Because it require from me to use ORMs, or reflection. If I shouldn’t use `new` what about create objects in event sourcing from events?