The Joy of Hex

Drunken Monkey Coding Style with a hint of Carlin humor

Apr 16, 2017 - 3 minute read - php CQRS EventBus DIC

Using Zend Service Manager with Simplebus

We are currently working on a project that is evoliving in such a way that using a messagebus would simplify code a lot, and would help us deliver higher quality in less time. After looking around on packagist for available event/command busses, I decided to try out the SimpleBus. The docs are a bit sparse, they do give you all the necessary information, and it is a quick setup.

We use Zend Service Manager as DIC and it is fairly straight forward to implement it.

Following the example from the docs, first you would declare your event

Event UserRegistered.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php

class UserRegistered
{
    private $userId;

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

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

Then your subscriber

Subscriber SendWelcomeMailWhenUserRegistered.php
1
2
3
4
5
6
7
8
9
<?php

class SendWelcomeMailWhenUserRegistered
{
    public function __invoke($message)
    {
        print "received {$message->userId()}" . PHP_EOL;
    }
}

And now for the important part, you would create a factory for ServiceLocatorAwareCallableResolver in which it is instantiated with a service locator callable that will instantiate the subscriber service when needed with Zend Service Manager as shown on lines 20-22

Factory ServiceLocatorAwareCallableResolverFactory
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

class ServiceLocatorAwareCallableResolverFactory implements FactoryInterface
{
    /**
     * Create a ServiceLocatorAwareCallableResolver object instance
     *
     * @param  ContainerInterface $container
     * @param  string $requestedName
     * @param  null|array $options
     * @return ServiceLocatorAwareCallableResolver
     * @throws ServiceNotFoundException if unable to resolve the service.
     * @throws ServiceNotCreatedException if an exception is raised when
     *     creating a service.
     * @throws ContainerException if any other error occurs
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null) : ServiceLocatorAwareCallableResolver
    {
        return new ServiceLocatorAwareCallableResolver(
            function ($serviceId) use ($container) {
                return $container->get($serviceId);
            }
        );
    }
}

Add the factory and class in the Service Manager configuration

1
2
3
4
5
6
7
<?php

$serviceManager = new ServiceManager([
    'factories' => [
        ServiceLocatorAwareCallableResolver::class => ServiceLocatorAwareCallableResolverFactory::class
    ],
]);

Then put together the example as described in docs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php

$eventBus = new MessageBusSupportingMiddleware();
$eventBus->appendMiddleware(new FinishesHandlingMessageBeforeHandlingNext());

// Provide a map of event names to callables. You can provide actual callables, or lazy-loading ones.
$eventSubscribersByEventName = [
    UserRegistered::class => [
        SendWelcomeMailWhenUserRegistered::class,
        SendWelcomeMailWhenUserRegisteredAndSomething::class
    ]
];

With a slight difference when declaring a subscriber collection, on line 5 Service Manager is used to resolve the ServiceLocatorAwareCallableResolver which will inject it with callable setup to resolve the requested subscribers

1
2
3
4
5
6
<?php

$eventSubscriberCollection = new CallableCollection(
    $eventSubscribersByEventName,
    $serviceManager->get(ServiceLocatorAwareCallableResolver::class)
);

Rest is identical to the example from the docs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php

$eventNameResolver = new ClassBasedNameResolver();

$eventSubscribersResolver = new NameBasedMessageSubscriberResolver(
    $eventNameResolver,
    $eventSubscriberCollection
);

$eventBus->appendMiddleware(
    new NotifiesMessageSubscribersMiddleware(
        $eventSubscribersResolver
    )
);

$event = new UserRegistered('some-user-id');
$eventBus->handle($event);

So when the script is run, you get a nice output of the user id

1
2
➜  application git:(master) ✗ php event-bus.php
received some-user-id