Experimenting with Broadway

Posted on by Matthias Noback

Event sourcing with Broadway

At the Dutch PHP Conference I attended a workshop by Beau Simensen and Willem-Jan Zijderveld. They showed us some examples of how to work with Broadway, a framework for event sourcing, with full Symfony integration, created by the smart people at Qandidate.

During my two weeks of funemployment, before starting my new job at Ibuildings, I decided to recreate one of my previous projects using Broadway. As it turns out, it's a great framework that's quite easy to use and is very powerful at the same time. Even though it's not a stable package (as in, it's still in the 0.x version range), I think it's safe to depend on it.

For me it wasn't hard to find out how to do things with Broadway because I had attended the workshop. I can imagine that it will be harder for people who didn't. Anyway, I'd recommend you to take a look at Beau's slides and the demo project he used in the workshop.

What's great about Broadway is that it takes away many of your infrastructural concerns and lets you focus on developing a proper domain model and write commands and command handlers to interact with it. Of course, this means there will be enough left for you to personally struggle with.

Introducing the broadway-serialization library

Something that I noticed during the workshop is that domain event classes as well as view model classes contain a lot of code that is just there to support serialization/deserialization. Domain events will be stored in the event store as a JSON serialized object. View models will (by default) be stored in an ElasticSearch database (also using JSON). As an example of what it takes to make such objects serializable, take a look at the PostWasPublished event:

use Broadway\Serializer\SerializableInterface;

class PostWasPublished implements SerializableInterface
{
    ...
    public static function deserialize(array $data)
    {
        return new static(
            $data['id'],
            $data['title'],
            $data['content'],
            $data['category']
        );
    }

    public function serialize()
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->content,
            'category' => $this->category,
        ];
    }
}

Writing this type of code is quite repetitive, boring, yet error-prone. After a couple of hours of doing this, it started itching so badly that I decided to create a very simple library that would save me from this. Because I think many Broadway users would be helped by it, I open sourced it.

Using matthiasnoback/broadway-serialization you can remove the serialize() and deserialize() methods from your serializable classes and instead just use a trait:

use Broadway\Serializer\SerializableInterface;
use BroadwaySerialization\Serializable;

class PostWasPublished implements SerializableInterface
{
    use Serializable;

    ...
}

If some of the properties contains (an array) of objects, you need to provide a callable that is able to reconstitute those objects, like this:

class PostWasPublished implements SerializableInterface
{
    /**
     * @var PublishedAt
     */
    private $publishedAt;

    protected static function deserializationCallbacks()
    {
        return [
            'publishedAt' => ['PublishedAt', 'deserialize']
        ];
    }

There's also a Symfony integration for this library. Take a look at the project's README to find out more about it.

Let me know if you have any feedback about this library.

Sagas

Something that's missing in Broadway, and was requested quite often, is an example of how to work with sagas. It turned out that a part of the implementation was still missing. I'm working on a pull request to solve some of the issues. When it's done, it should be possible to use sagas in your application.

It's probably wise to read up on the subject. In summary, it's quite a cool concept. When handling a command, an aggregate produces domain events. A saga subscribes to these events and dispatches new commands based on them. Meanwhile it keeps any information it needs in order to decide if or when to dispatch those commands.

Sagas are useful to coordinate actions across bounded contexts, when a new piece of knowledge about what happened "out there" requires some action to be taken "in here".

I know that the authors of Broadway, based on their experience with sagas, have come to the conclusion that they shouldn't be used that often. I guess that's why it's not documented that well. Personally I think it's a great tool that every Broadway user should have in their toolbox, hence my effort to make it work and document it.

SimpleBus

When I tweeted about the fact that I was using Broadway now, Loïc Chardonnet asked me: "What's going to happen to SimpleBus?". I think it makes sense to answer the question here again, but with some more words.

Broadway itself contains a command bus implementation, which works perfectly fine. It's a lot simpler than the one from SimpleBus though, since some of the problems SimpleBus tries to solve, are automatically being solved if you use a full event sourcing solution like Broadway. That's why I'd say: if you use Broadway, don't bother to use SimpleBus as well.

If you don't use Broadway, then I'd definitely recommend using SimpleBus. It helps you move your application in better directions. With SimpleBus a hexagonal design style, CQRS and proper DDD are just around the corner. Of course it's not going to do all the work for you. Remember that SimpleBus, as well as any other command bus library, is just a very simple, insignificant thing in your application. Its supportive strength is what matters.

PHP Symfony event sourcing Domain-Driven Design Broadway bundle serialization
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
Rasta

haven't you considered this library for de/serialization? https://github.com/netresea... I think it's a better approach comparing to traits.

Matthias Noback

There are many options, but, like the one you pointed out, they all try to cover too many different scenarios. For Broadway (de)serialization you just need to be able to expose the properties of an object, and later copy them back into the object. I didn't want to be forced to have public setters/getters for everything, nor annotations, nor predefined property types, etc. I also wanted it to be as fast as possible, and as easy as possible to use. So, that's why :) By the way, you don't need to use the trait, it's just highly convenient and increases development speed.