[Updated] DDD with Broadway and the Design Pattern State

Note : This article is the translate of this article. I use Google Translate for help me to write in english. Please, if you read wrong phrase send me the correct by one comment.

In a refactoring sprint, I found that my main aggregate class took much overweight.

I had exceeded 750 lines of code with, in many actions, a « switch » or a dozen « IF ». This did not please me very much because, if a change was required with this level, the amendment would be difficult.

Taking a step back on my code (it is always a good idea to step back and re-read his code to someone else) I realized that my class had defined constants 5 and a property « state « used in all the » switch « or » if « . A little research and rereading the book « Design Pattern – Head First », I fell on the design pattern « Status ».

On reflection and looking at the code that implements this pattern, I decided to implement my aggregate.

Thus begins an intense reflection!

Since the methods related to the state are redirected directly to the object « state » current, how to make the events from the object « state »? How to change my state in aggregate? How not to expose the `setState () method to prevent an` external element does not change the state of the aggregate?

All these issues have a simple answer. But for that, let’s take them one by one.
How to send events from the « state » object?
By storing it in an instance variable of type « table. » The aggregate will recover after the execution of the action to issue them.

How to change the state in my remote?
It does not change location … It’s always in the protected methods apply … `()`. They are the only empowered to modify the internal state of the aggregate.

How not to expose the `setState () method to prevent an` external element does not change the state of the aggregate?
The previous answer there also answers! There is no `setState ()` function. The « state » objects are instantiated and assigned directly to the events of the application methods.

In the end, the two design patterns are perfectly compatible and produce a very effective composition.

Adding a condition is now possible by the application of the « State pattern. » Now my class aggregate has lost more than 250 lines of code and all « if » and « switch » disappeared.

The implementation with Broadway :

<?php

interface State
{
    public function change();

    public function getEvents();
}

abstract class BaseState implements State
{
    private $events;

    private $aggregate;

    /**
     * The aggregate link is for acces to property of aggegate.
     */
    public function __construct($aggregate)
    {
        $this->aggregate = $aggregate;
    }

    /**
     * return all events not sent
     */
    public function getEvents()
    {
        $events = $this->events;

        $this->events = [];

        return $events;
    }

    private function addEvent($event)
    {
        $this->events[] = $event;
    }
}

class FirstState extends BaseState
{
    public function change()
    {
        // Store event into state object
        $this->addEvent(new ChangeEvent($this->aggregate->getAggregateRootId()));
    }
}

class SecondState extends BaseState
{
    public function change()
    {
        //No change
        throw new \Exception("Unable to change the state !");
    }
}

Agregate and Event :

<?php

use Broadway\EventSourcing\EventSourcedAggregateRoot;
use FirstState;
use SecondState;

class Agregat extends EventSourcedAggregateRoot
{
    private $id;

    private $state;

    public function getAggregateRootId()
    {
        return $this->id;
    }


    public static function make($id)
    {
        //...
        $this->apply(new MakeEvent($this->getAggregateRootId()));
    }

    protected function applyMakeEvent(MakeEvent $event)
    {
        $this->id = $event->getId();
        $this->state = new FirstState($this);
    }

    public function change()
    {
        $this->state->change();
        $this->applyMany($this->state->getEvents());
    }

    protected function applyChange(ChangeEvent $event)
    {
        $this->state = new SecondState($this);
    }

    private function applyMany(array $events)
    {
        foreach ($events as $event) {
            $this->apply($event);
        }
    }

}

class ChangeEvent {
    private $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

    public function getId()
    {
        return $this->id;
    }
}

class MakeEvent {
    private $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

    public function getId()
    {
        return $this->id;
    }
}

This branch on my test-broadway repository apply this pattern. Write a comment if you need help or if you want speak…

1 réflexion sur « [Updated] DDD with Broadway and the Design Pattern State »

Les commentaires sont fermés.