Skip to content

Instantly share code, notes, and snippets.

@bobthemighty
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bobthemighty/b241a4fccadbd7591024 to your computer and use it in GitHub Desktop.
Save bobthemighty/b241a4fccadbd7591024 to your computer and use it in GitHub Desktop.
DDD Glossary

Command

Commands encapsulate requests to DO something in the system. Each command uses the imperative tense ("CreateWidget", "MakeCustomerHappy", "UseTheForce"). A command is an immutable value object with no behaviour of its own. Commands are processed by Command Handlers. Each command must have exactly one command handler.

class CreateWidgetCommand
{
   function __construct($widgetColour, $widgetHeight, $widgetWidth){ ...}
   
   function height()
   {
      return $this->widgetHeight;
   }
}

Events

Events are used to tell other parts of the system that a thing has happened. Events are named in the past tense (CustomerBecameHappyEvent, UsedTheForceEvent, WidgetCreatedEvent). Like commands, events are immutable and have no behaviour of their own. Events are handled by Event Handlers. An event may have any number of handlers, including zero.

Command/Event Handlers

Handlers coordinate the processing of a request in the system. Each handler has the same basic structure:

  1. Fetch current state from the database
  2. Call methods on domain objects to mutate state
  3. Commit state.
  4. Raise any events.

Commands are found and executed by a message processor. I suggest we use SimpleBus.

Unit tests are usually written against command handlers.

Repository

A data store. Repositories encapsulate the idea of data access. They should, ideally, have collection semantics. Repositories work within a Unit Of Work. Doctrine has the concept of an EntityRepository. I would suggest we create a simple interface, and implement using that.

interface Repository
{
   public function add($entity);
   public function get($entityId);
   public function delete($entity);
}

class DoctrineRepository implements Repository
{
 // wrap an EntityRepository 
}

Unit Of Work

A unit of work encapsulates a set of operations against a datastore. A Unit of Work is committed or rolled back as a single atomic operation. Usually, a unit of work spans a single database transaction. Units of work track objects that are loaded within their scope, and automatically persist changes made to them.

Again, Doctine supports this concept with the EntityManager and UnitOfWork classes, so it's easy for us to implement our interface based on that.

// When inserting a new item, we need to call add.

class CreateWidgetHandler
{
   function __construct($unitOfWorkManager){ ... }
   
   function handle($cmd)
   {
      try
      {
         $uow = $this->unitOfWorkManager->start();
         // $widgets is a repository exposed by the unit of work
         $uow->widgets->add( $this->makeWidget($cmd) );
         
         $uow->commit();
      }
      catch(...) { $uow->rollback(); }
   }
}

// When updating an item, the unit of work will track changes automatically.

class SetWidgetSizeHandler
{
   function __construct($unitOfWorkManager){ ... }
   
   function handle($cmd)
   {
      try
      {
         $uow = $this->unitOfWorkManager->start();
         $widget = $uow->widgets->get($cmd->widgetId);
         $widget->setSize($cmd->height, $cmd->width);
         $uow->commit();
      }
      catch(...) { $uow->rollback(); }
   }
}

Adapters

An adapter is responsible for talking to the outside world, and translating into our domain language. For this project the most important adapter is Symfony. Symfony handlers can be registered as services in the dependency injection container, which gives us a single composition root. Because all the logic is in the domain model, and orchestrated in unit-testable command handlers, we don't generally bother testing Adapters. They're very thin, very simple. We would usually write a single Acceptance Test that makes sure our controller is wired up correctly, and use unit tests for verifying all our behaviour.

class WidgetController
{

    function __construct($bus)
    {}

    public function create($colour, $width, $height)
    {
        // validation goes here.
        $cmd = new CreateWidgetCommand($colour, $width, $height);
        $this->bus->send($cmd);
        return new Response('<html><body>Created!</body></html>');
    }
} 
<?php
// This is how I would normally write unit tests.
// I'm writing a unit test library for PHP that makes this style of testing possible.
// PHPUnit is fine, you just have to be more disciplined about Arrange-Act-Assert.
class When_creating_a_new_widget
{
function given_a_command_handler()
{
$this->repository = new FakeRepository();
$this->uow = new FakeUnitOfWorkManager();
$this->uow->widgets = $this->repository;
$this->handler = new CreateWidgetCommandHandler();
}
function because_we_handle_a_create_widget_command()
{
$cmd = new CreateWidgetCommand("RED", 10, 10);
$this->handler->handle($cmd);
}
function it_should_add_a_widget_to_the_repository()
{
expect($this->repository, hasCount(1));
}
function it_should_have_the_correct_colour()
{
expect($this->repository[0]->colour, is(equalTo("RED")));
}
function it_should_have_committed_the_uow()
{
expect($this->uow->wasCommitted, is(equalTo(true)));l
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment