Protect your ArrayCollections

Here's a little Doctrine ORM tip to safeguard your business logic when dealing with collections.

Do you have some business logic tied up in your adder methods? Something like this maybe.

class Cart
{
    private $items;

    public function __construct()
    {
        $this->items = new ArrayCollection();
    }

    public function addItem(Item $item)
    {
        if ($item->isExpired()) {
            throw new ItemExpiredException(sprintf("The item is expired: %s.", $item->getName()));
        }

        $this->items->add($item);
    }

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

Look how easy it is for another developer to accidentally skirt your nice logic.

$cart = new Cart();
$cart->getItems()->add($expiredItem);

It's easy to prevent though!

class Cart
{
    // ...

    public function getItems()
    {
        return new ArrayCollection($this->items->toArray());
    }
}

If you're concerned with them still calling $cart->getItems()->add($expiredItem) and silently not doing what they think it should be doing, simply return $this->items->toArray() from the getItems() method.

If you want to return an ArrayCollection and also take advantage of Doctrine's extra lazy functionality one solution would be to subclass ArrayCollection and proxy read-only calls to the lazy collection.

class Cart
{
    // ...

    public function getItems()
    {
        return new ReadOnlyCollection($this->items);
    }
}

Tags: PHP, DoctrineORM

Structuring my applications, Cont'd

It really irks me when I see some design/architecture decisions other developers have made but there's no technical explanation. What packages did they use? What challenges did they face? What trade-offs were made?

I'll go over some more specifics in this post.

Recap

In the last post, I described how I structure my Model directory to house framework agnostic code. I don't develop that in isolation though. I write a feature in the Model directory and then I go to my src/Acme/AwesomeProject/Infrastructure/AppBundle directory to implement what I need to in my Symfony application. This includes:

  • Writing Doctrine ORM mapping configurations
  • Implementing repositories
  • Wiring everything up in the IoC container
  • Defining routes and controllers
  • Acceptance testing with Behat.

Bundles

When I started using Symfony, I saw that you develop your code inside "bundles". I also started off using the FOSUserBundle. Seeing that set me on creating bundles for basically each of my entities. All of the routes/controller/config for an entity would be in its very own bundle. I finally came to the realization that that was moronic. They were all so coupled together that it didn't make any sense. It's not like I could use my TaskBundle in another application. It was so ingrained with classes in the other bundles. That is why I now have a single AppBundle.

This is what's inside. It looks very similar to my Model directory for a reason. This is the Symfony implementation of my application.

Model Directory


Let's go over the directories.

Command

This holds my custom console commands, not the same commands in my Model directory. I view these as controllers. They may not be dealing with http, but they're identical in purpose: take input data, create command objects, send them to the application via the command bus.

Controllers

Pretty self-explanatory but here's an example of my most complicated controller method.

namespace Acme\AwesomeProject\Infrastructure\AppBundle\Controller

use Acme\AwesomeProject\Infrastructure\AppBundle\Form\RelocatePersonCommandType;
use Acme\AwesomeProject\Model\Command\RelocatePersonCommand;

class PersonController extends ApiController
{
    /**
     * @param Request $request
     * @param string $personId
     * @return Response
     */
    public function relocatePersonAction(Request $request, $personId)
    {
        $type = new RelocatePersonCommandType($personId);
        $form = $this->createForm($type, null, ['method' => 'PUT']);

        $form->handleRequest($request);

        if ($form->isValid()) {

            /** @var RelocatePersonCommand $command */
            $command = $form->getData();
            $this->getCommandBus()->handle($command);

            return $this
                ->setData(['person' => $command->getPerson()])
                ->respond();
        }

        // creates a response with errors extracted
        // from the form object
        return $this->respondWithForm($form);
    }
}

DependencyInjection

This is for Symfony's bundle architecture. If you're not familiar with Symfony, you don't need to worry about it for this post.

Features

This holds my context and feature files for testing with Behat.

Pro tip! If you're using Symfony, use the extension docs to get up and running. I wasted a lot of time thinking everything was laid out in the Behat docs.

Form

Symfony's form component is really powerful. It maps http requests into php objects. It also triggers validation checks. The docs all show mapping data straight into entities. That simply doesn't work with my architecture because of the way I've written my entity constructors. They require arguments and Symfony's form company attempts to use setters to get the data into the entities.

I need the form component to map to command objects anyway. William Durand has a great post about bending forms to your will. I use this technique to create my command objects from POST and PUT requests.

Provider

This holds my implementation of the CurrentUserProvider interface defined in my Model directory.

namespace Acme\AwesomeProject\Infrastructure\AppBundle\Provider;

use Acme\AwesomeProject\Model\Entity\User;
use Acme\AwesomeProject\Model\Provider\CurrentUserProvider;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class SymfonyCurrentUserProvider implements CurrentUserProvider
{
    /**
     * @var TokenStorageInterface
     */
    private $storage;

    /**
     * @param TokenStorageInterface $storage
     */
    public function __construct(TokenStorageInterface $storage)
    {
        $this->storage = $storage;
    }

    /**
     * @return User
     */
    public function getUser()
    {
        if (null === $token = $this->storage->getToken()) {
            return null;
        }

        if (null === $user = $token->getUser()) {
            return null;
        }

        if (!is_object($user)) {
            return null;
        }

        return $user;
    }
}

Repository

This is where I put my Doctrine implementations of the repositories I've interfaced in the Model directory.

Resources

This is Symfony specific. It contains IoC container configuration, Doctrine ORM mappings, route definitions, serialization configuration, and twig templates.

By default, Symfony bundles come with a single services.yml file for you to wire up your IoC container. I delete that and make a services directory. I then break up the service definitions into multiple files, handlers.yml, infrastructure.yml, listeners.yml, repositories.yml, security.yml, etc. This avoids having a single gigantic services file. It makes finding and tweaking definitions much saner. You'll need to add these new files to the bundle's extension class.

namespace Acme\AwesomeProject\Infrastructure\AppBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

class AcmeAwesomeProjectInfrastructureAppExtension extends Extension
{
    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services/repositories.yml');
        $loader->load('services/infrastructure.yml');
        $loader->load('services/security.yml');
        $loader->load('services/handlers.yml');
        $loader->load('services/listeners.yml');

        $env = $container->getParameter('kernel.environment');

        if (in_array($env, ['dev', 'test'])) {
            $loader->load("services/dev.yml");
        }
    }
}

The Symfony's Doctrine ORM bundle expects your entities to be in an Entity direcotry inside bundles. It also expects your mapping configurations to be in a certain place in your Resources directory. Well...my entities are defined in my Model directory, outside of my AppBundle. Here's how I configure Doctrine to know how to map my objects.

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver:   "%database_driver%"
                host:     "%database_host%"
                port:     "%database_port%"
                dbname:   "%database_name%"
                user:     "%database_user%"
                password: "%database_password%"
                charset:  UTF8
                logging:  true

            admin:
                driver:   "%database_driver%"
                host:     "%database_host%"
                port:     "%database_port%"
                dbname:   "%database_name%"$
                user:     "%database_admin_user%"
                password: "%database_admin_password%"
                charset:  UTF8

    orm:
        default_entity_manager: default
        entity_managers:
            default:
                connection: default
                mappings:
                    AcmeAwesomeProjectOAuthBundle: ~
                    FOSOAuthServerBundle: ~
                    models:
                        mapping:   true
                        type:      yml
                        dir:       "%kernel.root_dir%/../src/Acme/AwesomeProject/Infrastructure/AppBundle/Resources/config/entity-mappings"
                        prefix:    Acme\AwesomeProject\Model\Entity
                        is_bundle: false
                    embeddables:
                        mapping:   true
                        type:      yml
                        dir:       "%kernel.root_dir%/../src/Acme/AwesomeProject/Infrastructure/AppBundle/Resources/config/embeddable-mappings"
                        prefix:    Acme\AwesomeProject\Model\ValueObject
                        is_bundle: false

If you're using other bundles that adhere to the expected structure you can configure them with BundleName: ~ as seen above. For my entities and value objects defined in my Model directory, I have to explicitly tell Doctrine where to find the mapping files.

Inside the entity-mappings and embeddable-mappings directories I have files like User.orm.yml, Project.orm.yml, Location.orm.yml, etc.

Serializer

I'm using the JMS Serializer to turn objects into json. Most of the time you can configure how you want objects to be serialized with a configuration file(located in the Resources directory). Sometimes it's just easier to serialize objects yourself in code. This is where I put those serializer classes.

The JMS Serializer has a Symfony bundle available. It expects the classes to be serialized to be in a bundle. You can get around this by configuring it in your app/config/config.yml file.

jms_serializer:
    metadata:
        auto_detection: true
        directories:
            AppBundle:
                namespace_prefix: Acme\AwesomeProject\Model
                path: "%kernel.root_dir%/../src/Acme/AwesomeProject/Infrastructure/AppBundle/Resources/serializer"

Now, everytime the serializer encounters an object with the namespace that starts with Acme\AwesomeProject\Model it will look in my AppBundle's Resources/serializer directory. I can define how to serialize an entity by defining a Entity.Project.yml file inside the Resources/serializer directory. Directories are signifed with dots. I can define configuration for a value object with a ValueObject.CategorySummary.yml file.

Be aware! After you modify your serializer configuration files be sure to clear your app's cache! I've spent way too much time trying to figure out why my latest changes weren't being reflected.

Service

This is where I put implementations defined in my Model/Service directory, like my TwigSwiftUserMailer.

Tests

This is where my functional tests go. Mainly, this is where I use PHPUnit to ensure my entities and value objects are being persisted correctly. I actually interact with a test database in these tests.

Other bundles

There other bundles I typically pull into my projects. One such bundle is the FOSRestBundle. This bundle allows your application to accept form encoded data, json, and xml and will massage that data into the same format regardless of content type. It let's you pass Request objects straight into your forms to create command objects. Although it supports form encoded data, json, and xml, I only configure it to use json.

It also takes over how exceptions are displayed to the client. You can configure it to only show exception messages from certain classes and map exception classes to http status codes.

fos_rest:
    routing_loader:
        default_format: json
    param_fetcher_listener: true
    body_listener: true
    format_listener:
        rules:
            - { path: '^/', priorities: [ 'json'], fallback_format: json, prefer_extension: false }
    view:
        view_response_listener: 'force'
        formats:
            json: true
    service:
        serializer: jms_serializer.serializer
    exception:
        codes:
            Symfony\Component\Security\Core\Exception\AccessDeniedException: 403
            Acme\AwesomeProject\Model\Exception\UserNotFoundException: 404
            Acme\AwesomeProject\Model\Exception\AssertionFailedException: 400
            Acme\AwesomeProject\Model\Exception\AccessDeniedException: 403
            Acme\AwesomeProject\Model\Exception\DomainException: 400
        messages:
            Symfony\Component\Security\Core\Exception\AccessDeniedException: true
            Acme\AwesomeProject\Model\Exception\UserNotFoundException: true
            Acme\AwesomeProject\Model\Exception\AssertionFailedException: true
            Acme\AwesomeProject\Model\Exception\AccessDeniedException: true
            Acme\AwesomeProject\Model\Exception\DomainException: true

I also secure my applications with the FOSOauthServerBundle but you can easily create your own simpler authentication mechanism.

Heads up! If you do decide to implement your own authentication mechanism, the docs are not correct. You can see my struggle in this stackoverflow question.

Because I'm usually developing API's that have javascript frontend clients, I use the NelmioCorsBundle to get around CORS headaches with a simple configuration.

nelmio_cors:
    paths:
        '^/':
            allow_origin: ['*']
            allow_headers: ['*']
            allow_methods: ['POST', 'PUT', 'GET', 'DELETE', 'OPTIONS']
            max_age: 3600

Wrapping up

Welp, that's it. That's my glimpse into how I've started to develop apps. I'm pretty happy with it but always looking for better ways of doing things. Hopefully it gives you some ideas. I encourage you to write up a post about how you go about writing apps. I would love to read it. Stop giving my abstract ideas. I want to see the nitty-gritty details, the trade-offs, the implementation details!

Ask me questions in the comments or give me suggestions. I'm definitely open to those. If you liked what you read, you can subscribe to be notified of my future posts in the sidebar :)

Tags: Symfony, PHP

Structuring my applications

One of the biggest struggles for me, as an app developer, is coming up with an architecture that I'm happy with. It's something I wish other developers talked about more often. I thoroughly enjoyed Kris Wallsmith's SymfonyCon talk. It's very raw and real and doesn't come across as him talking down to anyone at all. Do I agree with everything he says? No, but that's not a bad thing. It's very insightful and I really enjoy taking a peak behind the curtains and seeing how other people do things.

This is my attempt at doing just that. What I'm going to say isn't unique to me but rather me stealing a bunch of good ideas from several developers smarter than me. I want give a big thanks to Mathias Varraes. He's got it figured out and happily helps me sort things out in the DDDinPHP forum. His blog is also chock-full of awesome posts.

Preface

Let me preface this by saying that I use Symfony. The things I will talk about can be applied to any framework though. Please don't let that scare you away!

Packages

A while back I posted a rant on /r/php about the Laravel community going on and on about hexagonal/command-bus architectures. It seemed completely moronic to me, since it's basically a crippled event dispatcher. Oh how wrong I was. I want to take a moment and apologize to Shawn McCool, Jeffrey Way, and Ross Stuck for that. They responded gracefully to my ignorant rant. Needless to say, I'm now a big proponent of using a command bus.

If you're disciplined about using a command bus, it's a fantastic way to only allow a single entry point into your entire application. I'm using Matthias Noback's Simple Bus package for my command bus. It's framework agnostic, but has packages to integrate with Symfony and Doctrine ORM which I make use of.

Next, I pull in Benjamin Eberlei's Assert package. This package is incredibly simple, yet so powerful. It just has a ton of static methods that you can use to check the validity of data. It throws exceptions if the assertion fails. It's such a simple straightforward way to ensure incoming data is valid, keeping your domain valid. After installing that package, I create my own AssertionFailedException and subclass the Assertion class to use my custom exception. I also write my own static assertion methods in this class specific to my domain.

Diggin in

After I install my framework and the packages above, I create a src/Acme/AwesomeProject/Model directory. This is where the meat of my application lives. Code in here is meant to be completely unaware of an http framework.

I also create a complimentary infrastructure directory. Symfony uses bundles for this. Mine would be found in src/Acme/AwesomeProject/Infrastructure/AppBundle. This is where my Symfony implementation lives.

This is what is typically in my Model directory. I'll go over each directory.

Model Directory


Entity

This is where I start. I define my persistable entities in this directory. This is where my first trade-off is. If I were really hardcore about being completely decoupled I would define interfaces for my entities in here instead concrete classes. This would, hypothetically, allow me to be ORM agnostic. That's too much of a hassle for me to be worth it. Because I'm using Doctrine ORM, a data-mapper ORM, there is very little trace of Doctrine in my entites anyway.

Although, I don't like it I'm also implementing Symfony's security UserInterface on my User entity. I don't like it because I want as little to do with any framework in this code as possible. I could get around it if I cared enough but it's just a minor annoyance.

I take special care when defining my entities' constructors. I inject only what is required for that entity to be valid, not all possible fields. I use setters for the optional fields.

namespace Acme\AwesomeProject\Model\Entity;

use Acme\AwesomeProject\Model\Event\AssetWasEntered;
use Acme\AwesomeProject\Model\Validation\Assert;
use DateTime;
use SimpleBus\Message\Recorder\ContainsRecordedMessages;
use SimpleBus\Message\Recorder\PrivateMessageRecorderCapabilities;

class Asset implements ContainsRecordedMessages
{
    use PrivateMessageRecorderCapabilities;

    // fields

    public function __construct($id, $name, Category $category)
    {
        Assert::id($id, "Invalid asset id.");
        Assert::string($name, "Asset name must be a string.");

        $this->id = $id;
        $this->createdAt = new DateTime();
        $this->name = $name;
        $this->category = $category;

        $this->record(new AssetWasEntered($id));
    }

    // methods
}

There are several things to talk about with the code snippet above.

One of the great things about using Simple Bus, the command bus package, is its Doctrine integration. You can configure it to use a middleware so all command handlers get wrapped in a database transaction. Great for keeping the integrity of your data.

There's also a middleware you can use that will dispatch events you've recorded in your entities. As you can see above, I'm implementing a Simple Bus interface and taking advantage of a trait to implement it. That gives me access to the ::record($event) method. The middleware hooks into Doctrine so after an entity is persisted it dispatches all events recorded in your entites.

Now, anywhere I new up this entity and it get's persisted I can be sure this event will be dispatched. I could also record an event if the asset get's renamed. The event could contain the asset id, the old name, and the new name. Maybe I use it for auditing or sending an email alert.

I also pass in the id. I use Ben Ramsey's uuid package to generate these. Using uuid's is so much nicer than using your database's auto-incremented values. You can dispatch events with it before the entity is actually persisted, importing relational data into your system is much much easier, and if you use these in your urls they don't reveal how big...or small your app is. :p

Events and Event Listeners

I only define event classes and record them when I need them though. I could spend all day thinking up of all of the events that could potentially be useful in the future. It's premature clutter.

When it comes to what gets included as fields in my event objects, I only use scalar data, not objects. You'll notice above I only passed the id into the AssetWasEntered event and not the Asset instance. I want to make it dead simple to audit these events. Converting these events into json and storing that in a database is a very powerful, easy thing to do. It's also cheap to refetch these entities by id. Most likely I will have already gotten and persisted the entity in a command handler. If that's the case, it's already being managed by Doctrine and asking for it again won't require executing sql and hydrating a new object. It will simply give me the same instance it's already managing.

It's important to note that these events get dispatched after the command is handled, which is wrapped in a database transaction. Consider this scenario: You have some crazy business rule where if the asset's name starts with the letter "B", then another field needs to be updated and a related entity needs to be updated. Rather than satisfy this requirement directly in an event listener, I will write another command and command handler and name them something appropriate to that particular requirement. I'll use the event listener to new up that command and the command bus to handle it. The listener is just glue. Now I have the bit of data manipulation named very explicitly in code and could use it elsewhere if needed. Event listeners are very similar to a framework's controllers. They're a thin layer that pass application commands into the bus.

I ran into a tricky issue that I had to deal with: delete events. They're not like creation events, you can't just raise them in the __destruct() method. No one really has control over when that gets executed(unless you call it explicitly).

I have a business requirement that dictates an email needs to be sent when a Task for a Project gets deleted. I had a couple of ideas. The first was to dispatch an event in my command handler after I called $tasks->remove($task). I also thought of creating a public method on the task, $task->markForDeletion(), which would record the event in the entity. Both of these are flawed though. What happens in another command handler when I just delete the tasks' owning project? My ORM will delete the project and also all of its tasks, not giving me a chance to dispatch events for each task.

I finally came to the realization that I only had one way to reliably ensure that delete events for my entities occur: I have to hook directly in my ORM. I have an event listener tied to my ORM that listens for all entity deletions and fires the appropriate event for each entity type.

Commands and Handlers

This is where the business logic lives. A command has a single handler. Commands convey user intent. Events are by-products of those intents. The commands are almost identical to event objects. I don't pass objects into them, strictly scalar data. My http framework code is responsible for mapping http requests into command objects. I do, however, often convert that raw data into value objects.

namespace Acme\AwesomeProject\Model\Command;

use Acme\AwesomeProject\Model\ValueObject\PersonName as Name;
use Acme\AwesomeProject\Model\ValueObject\Location;
use SimpleBus\Message\Message;

class TrackPersonCommand implements Message
{
    // fields

    public function __construct(
        $userId, 
        $firstName,
        $lastName, 
        $address,
        $city,
        $state,
        $zipCode
    ) {
        // assignments
    }

    public function getName()
    {
        return new Name(
            $this->firstName, 
            $this->lastName
        );
    }   

    public function getLocation()
    {
        return new Location(
            $this->address, 
            $this->city, 
            $this->state, 
            $this->zipCode
        );
    }
}

Remember that Assert package? In the constructors of these value objects, PersonName and Location, I'm using assertions to ensure that data is valid. Now it's super easy for my command handlers to work with validated data.

namespace Acme\AwesomeProject\Model\Handler;

use Acme\AwesomeProject\Model\Command\TrackPersonCommand;
use Acme\AwesomeProject\Model\Repository\PersonRepository;
use SimpleBus\Message\Handler\MessageHandler;
use SimpleBus\Message\Message;

class TrackPersonHandler implements MessageHandler
{
    // fields

    public function __construct(PersonRepository $people)
    {
        // assignment
    }

    /**
     * @param TrackPersonCommand|Message $command
     */
    public function handle(Message $command)
    {
        $person = new Person(
            $command->getPersonId(),
            $command->getName(),
            $command->getLocation()
        );

        $this->people->track($person);
    }
}

Look how easy to read that handler is. It's amazingly clear what's going on and we can be sure the data is valid because of our assertions.

What about validation that requires the database? Maybe the people we're tacking also require a unique nickname. We have a couple of options.

We could define a middleware class that wraps our command bus, checks if the command is an instance of TrackPersonCommand, and if it is, attempt to get a user by that nickname, ie. $people->findByNickName($command->getNickName()). If the result is not null, throw a validation exception.

Or, we could simply put that check directly in the handler. I go with the latter.

I also use commands as DTO's for retrieval of entities. For example, I would have a GetPersonCommand and handler. I was tempted to just interact with my person repository directly in my http framework's controller. It seemed like overkill to create a command and handler for that...until I realized I would be losing out on the middleware. I wouldn't have a command to serialize for auditing, or caching, or authorization. Even though I stated that I strictly keep scalar data inside of my commands, I lied. I put setters and getters on commands for entity retrieval. That way when I create the command object in my controller, I can pass it to the command bus to be handled. If no exception's are thrown I can just use the command's getter to get the entity for display. My controllers don't need to be aware of repositories.

namespace Acme\AwesomeProject\Infrastructure\AppBundle\Controller;

use Acme\AwesomeProject\Model\Command\GetPersonCommand;
use Symfony\Component\HttpFoundation\Response;

class PersonController extends ApiController
{
    public function getPersonAction($assetId)
    {
        $command = new GetPersonCommand($assetId);
        $this->getCommandBus()->handle($command);

        $person = $command->getPerson();

        return $this
            ->setStatusCode(Response::HTTP_OK)
            ->setData(['person' => $person])
            ->respond();
    }
}

Exceptions

These are exceptions that are unique to my app's domain. Some examples are: EntityNotFoundException, ValidationException, AccessDeniedException. All of these exceptions extend my own DomainException. In your framework's exception handler code you can check if the exception is an instance of DomainException. If it is, show the exception's message and set the response code to the exception's code. Of course this means your exception's code must map to valid http status codes. If the exception isn't one of your own, return a 500 with a generic "Internal server error" message.

namespace Acme\AwesomeProject\Model\Exception;

class AccessDeniedException extends DomainException
{
    public function __construct($message = 'Access Denied', \Exception $previous = null)
    {
        parent::__construct($message, 403, $previous);
    }
}

Provider

This directory currently only holds a single interface, CurrentUserProvider. It has a single method, $provider->getUser(). My framework's implementation of this interface lives outside of the Model directory.

Repository

This directory holds interfaces for my repositories. It contains no implementations. My Doctrine implementations live outside of the Model directory.

namespace Acme\AwesomeProject\Model\Repository;

interface UserRepository
{
    /**
     * @param string $id
     * @return User
     * @throws UserNotFoundException
     */
    public function find($id);

    /**
     * @param string $email
     * @return User
     * @throws UserNotFoundException
     */
    public function findByEmail($email);

    /**
     * @param string $token
     * @return User
     * @throws UserNotFoundException
     */
    public function findByConfirmationToken($token);

    /**
     * @param User $user
     */
    public function add(User $user);

    /**
     * @param User $user
     */
    public function remove(User $user);
}

Security

It took me a while to figure out a clean way to incorporate authorization into my application without referencing an outside security component. Symfony provides the concept of security voters into its security component but this coupled my authorization directly to an outside security component. That bugged me.

Because I'm using a command bus architecture and have a single point of entry, middleware made perfect sense. I created a Middleware directory inside the Security directory. This is where I'm enforcing authorization.

I've defined a base abstract AuthMiddleware class which all of my auth middlewares will extend.

namespace Acme\AwesomeProject\Model\Security\Middleware;

use Acme\AwesomeProject\Model\Entity\User;
use Acme\AwesomeProject\Model\Exception\AccessDeniedException;
use Acme\AwesomeProject\Model\Provider\CurrentUserProvider;
use SimpleBus\Message\Bus\Middleware\MessageBusMiddleware;
use SimpleBus\Message\Message;

abstract class AuthMiddleware implements MessageBusMiddleware
{
    /**
     * @var CurrentUserProvider
     */
    protected $userProvider;

    /**
     * @param CurrentUserProvider $userProvider
     */
    public function __construct(CurrentUserProvider $userProvider = null)
    {
        $this->userProvider = $userProvider;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(Message $command, callable $next)
    {
        if ($this->applies($command)) {
            $this->beforeHandle($command);
            $next($command);
            $this->afterHandle(($command));
        } else {
            $next($command);
        }
    }

    /**
     * Do you auth check before the command is handled.
     *
     * @param Message $command
     * @throws \Exception
     */
    protected function beforeHandle(Message $command)
    {
        // no-op
    }

    /**
     * Do you auth check after the command is handled.
     *
     * @param Message $command
     * @throws \Exception
     */
    protected function afterHandle(Message $command)
    {
        // no-op
    }

    /**
     * @param string $msg
     * @throws AccessDeniedException
     */
    protected function denyAccess($msg = null)
    {
        throw new AccessDeniedException($msg ?: "Access denied.");
    }

    /**
     * @return User
     */
    protected function getUser()
    {
        return $this->userProvider->getUser();
    }

    /**
     * @param Message $command
     * @return bool
     */
    abstract protected function applies(Message $command);
}

This gives me a nice little framework for implementing auth middleware.

namespace Acme\AwesomeProject\Model\Security\Middleware;

use Acme\AwesomeProject\Model\Command\RelocatePersonCommand;
use SimpleBus\Message\Message;

class RelocatePersonCommandMiddleware extends AuthMiddleware
{
    //fields

    public function __construct(CurrentUserProvider $userProvider, PersonRepository $people)
    {
        // assignment
    }

    /**
     * @param RelocatePersonCommand|Message $command
     */
    protected function beforeHandle(Message $command)
    {
        $person = $this->people->find($command->getPersonId());

        if ($person->getCreatedBy() !== $this->getUser()) {
            $this->denyAccess();
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function applies(Message $command)
    {
        return get_class($command) === RelocatePersonCommand::CLASS;
    }
}

Service

This is where I put general services; interfaces or utility services. For example, I have a UserMailer interface defined. In my framework code, outside of the Model directory, I have a TwigSwiftMailerUserMailer implementation.

Tests

This is where I put my unit tests. These are tests that do not require a database connection, web server, etc. They are strictly for unit tests, not acceptance, functional, etc.

Validation

This is where my subclass of the Assertion class lives.

ValueObject

This is where I put objects that don't require id's and are strictly for transferring data. Some examples are PersonName, Location, and UserCategorySummary. That last one is interesting. Sometimes I have endpoints that show dashboard views. They contain counts of certain entities.

namespace Acme\AwesomeProject\Model\ValueObject;

class CategorizedInvoiceSummary
{
    //fields

    public function __construct(
        $userId, 
        $categoryId,
        $totalInvoices,
        $overdueInvoices,
        $paidInvoices
    ) {
        // assignment
    }

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

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

    // etc...
}

Maybe my application is some sort of ecommerce platform. The business requires to be able to view a listing view of invoices by category. I would have a StatRepository and something like, $stats->getCategorizedInvoiceSummaries($user), which would return a collection of the above objects.

If you're using Doctrine ORM, your entities can have value objects as fields and you can map those to appropriate columns. This cleans up your entity code quite a bit. It's a great feature.

In summary

Other developers should be able to peruse my Model directly and it be abundantly clear what the application can do. They won't be bogged down mentally with the implementation details. That is a beautiful thing.

I don't work on this Model directory in isolation. I'm bouncing from it to framework implementation code as I go. In part 2 of this series I will go over some framework specific code and issues I ran into and how I dealt with those. Read on!

Tags: Symfony, PHP