Article

Your Service Layer benefits from a Dispatcher

One benefit of recent applications over old 90s, early 2000 style PHP is central dispatching of requests through one Application or Dispatcher object. This is true for dispatching objects such as PHPs SoapServer or Zend\XmlRpc\Server as well. Old style applications use the webserver (Apache, Nginx) for the dispatching of routes to executed PHP scripts.

Central dispatching allows developers to hook into central events during the request processing to implement security, routing, caching, debugging, logging, localization and many things more transparently.

If you check this list of features, these are all cross-cutting concerns to the real purpose of your application and nicely abstracted from the core of your application using central dispatching.

When building a future proof system focussing on the domain and starting test-driven without dependency on a framework and UI, we often use the Service Layer Pattern to decouple our business logic from technical implementation details. The service layer however is affected by cross-cutting concerns again and this blog-post is about finding a maintainable solution to handle them.

As a practical example I will pick the classical user registration example, starting with a service-layer that implements the cross-cutting concerns inside the service method:

<?php

class UserService
{
    public function create($email, $password)
    {
        $this->security->assertNotLoggedInAlready();

        $user = new User();
        $user->setEmail($email);
        $user->encodePassword($password);

        $violations = $this->validator->validate($user);

        if (count($violations) > 0) {
            throw new \IllegalArgumentException(((string)$violations);
        }

        $this->entityManager->persist($user);
        $this->entityManager->flush();

        $message = "Thanks for registering! Please click here: ...";
        $this->mailer->sendMessage($email, $message);

        $this->logger->info("New user registration: " . $email);
    }
}

This is quickly getting out of hands with the number of services needed and you will not have fun testing this for example. This approach violates the Single-Responsibility-Princple (SRP) doing way too much non-related operations. Think about having to do this in every method of your service layer and you can already see this leads to lots of spagetti-code.

We could use the decorator pattern to extract some of the cross-cutting concerns into their own services. This requires to introduce an interface for UserService and then building decorators for the different concerns. Then the service can be built as a decorator chain:

<?php

$userService =
    new LogUserService(
        new SecurityUserService(
            new DoctrineTransactionUserService(
                new UserService(), ...
            ), ...
        ), ...
    );

The real UserService will slimmer and more closely handling just the business logic related information. Using decorators requires us to write the same delegation code for every method on every service, introducing many decorators and interfaces that duplicate lots of code. Something we want to avoid to keep our own sanity.

Lets refactor from decorator towards a dispatching approach instead, to keep more reusable. The CommandDispatcher object accepts a service name and method and calls it. We can wrap every call with cross-cutting concerns.

Starting with a very simple dispatcher possible that just delegates the calls to registered services, we add all the cross-cutting concerns our application needs. Note: This is deliberatly written without further abstraction, just to show the concept. The real thing would propably seperate the responsibilities from each other.

<?php

class CommandDispatcher
{
    private $services;

    public function registerService($serviceName, $service)
    {
        $this->services[$serviceName] = $service;
    }

    public function execute($serviceName, $method, array $params)
    {
        $service = $this->services[$serviceName]; // make lazy
        $callback = array($service, $method);

        if ($serviceName === "user" && $method === "create") {
            $this->assertNotLoggedInAlready();
        }

        $this->entityManager->beginTransaction();

        try {
            $result = call_user_func_array($callback, $params);
            $this->entityManager->commit();

            $this->mailer->sendQueuedMails(); // "deferred commit" of mails
            $this->logger->info("Called $serviceName.$method");

        } catch (\Exception $e) {
            $this->entityManager->rollBack();
            throw $e;
        }

        return $result;
    }
}

The dispatcher handles transactions around all the commands and also makes sure that when they send emails, those only get send when the transaction was successful. It checks if the user has the correct access controls/authentication and performs some generic logging.

And using the dispatcher in your code looks like this:

<?php
$dispatcher = new CommandDispatcher();
$dispatcher->registerService('user', new UserService());

$dispatcher->execute('user', 'create', array($email, $password));

Like the front controller in MVC or PHPs SOAPServer you register services/functions with the dispatcher. Registration of services can be done by convention, via some DependencyInjection Container Service name or any other way you prefer. The dispatcher then handles ALL commands by wrapping them inside some generic logic.

Compared to the Decorator approach, you can now easily reuse this code with many commands. Except registering new services, no new code is necessary when adding a new method or service.

So far the API of the dispatcher is tedious, so lets work a little bit on how you actually call methods on the service-layer.

There are two ways to make this call nicer. The first is use magic __call and some clever duck-typing to create an API similar to this:

<?php

$dispatcher = new CommandDispatcher();
$dispatcher->registerService('user', new UserService());

$dispatcher->user()->create($email, $password);

The second approach does not require magic __call, but requires you to write a class for each command. We map the command class name to a callback:

<?php

$userService = new UserService();

$dispatcher = new CommandDispatcher();
$dispatcher->registerCommand('CreateUserCommand', array($userService, 'create'));

$dispatcher->handle(new CreateUserCommand($email, $password));

The naming is very techincal here, but since the dispatcher also acts as a facade to the application, we could give it better names like PayrollApplication, Shop, TrackingSystem, any name the application has inside your organization.

Now that I have shown the implementation of a dispatcher a small discussion is necessary to evaluate it. The cross-cutting concerns could be nicely wrapped in the dispatcher, so we achieved a considerable improvement over the first example with all the concerns nicely seperated from each other.

The benefits are:

  • Services themself don't need access to the cross-cutting concerns anymore, reducing the number of dependencies and increasing maintainability and testability.
  • Handling cross-cutting concerns, that can make the service layer code very complex otherwise, in a clean way
  • All the concerns are easily composable and the result is a SOLID approach towards them.
  • The dispatcher also allows us to add or remove concerns later at one central location without having to change all the service layer code.
  • The framework we use can be very simple as long it fullfils the major requirement to be easily compatible to the dispatcher approach.
  • Compared to an MVC frameworks dispatcher, our service dispatcher can tailored to the application itself and end up being very small and easily understandable.

How do we use this dispatcher in our MVC framework though? Instead of using controllers/actions a REST or SOAP API could just use the dispatching and services directly and map the HTTP request to it based on convention. This would be a real win and simplify the framework-glue code considerably.

In a web-application however this is not so simple. We need to send redirects, manage session state and handle request and response data, which often requires one specific controller-action for each command. With some experimentation it might be possible to achieve a much higher re-use here, but it might fail as well.

That brings us to the downside of the dispatcher approach:

  • We need some additional code and extra classes, which might be too much for small applications and the indirection of handling cross-cutting concerns might confuse teammates.
  • Having the dispatcher object inside controllers feels strange from the MVC point of view, it doesn't really fit. It also still may require implementing one action for every command, not simplifying this part of the development.
  • While other languages don't need this because of their support for AOP and annotations (Spring for Java for example) this is necessary in PHP only, because we don't have this features.
  • Unless we use the explicit command object approach, there is no auto-completion for commands on the dispatcher in the IDEs.

My conclusion from working with both kind of service layers: If you decided for such a service layer, then my experience shows it is a mistake not to use a dispatcher, because the benefits outweigh the downsides.

Published: 2013-08-09 Tags: #ApplicationDesign