Last active
August 29, 2015 13:56
-
-
Save texdc/8963676 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Category\Command; | |
use Category\CategoryId; | |
use Message\AbstractMessage; | |
use Message\Message; | |
use Message\MessageId; | |
use Message\MessageTrait; | |
use Product\Command\ProductsCommandTrait; | |
interface Command extends Message | |
{ | |
public function categoryId(); | |
} | |
class CommandId extends MessageId | |
{ | |
const ADD_PRODUCTS_TO_CATEGORY = 'AddProductsToCategory'; | |
const REMOVE_PRODUCTS_FROM_CATEGORY = 'RemoveProductsFromCategory'; | |
protected static $validNames = [ | |
self::ADD_PRODUCTS_TO_CATEGORY, | |
self::REMOVE_PRODUCTS_FROM_CATEGORY | |
]; | |
} | |
abstract class AbstractCommand extends AbstractMessage implements Command | |
{ | |
use MessageTrait { | |
setAggregateRootId as private setCategoryId; | |
aggregateRootId as categoryId; | |
} | |
public function __construct(CategoryId $categoryId) | |
{ | |
parent::__construct(new CommandId(static::$messageName)); | |
$this->setCategoryId($categoryId); | |
} | |
public function __toString() | |
{ | |
return sprintf( | |
'%s [categoryId: %s, timestamp: %s]', | |
$this->messageName(), | |
$this->aggregateRootId ?: 'N/A', | |
$this->messageTime()->format('Y-m-d\TH:i:s.u') | |
); | |
} | |
} | |
abstract class AbstractProductsCommand extends AbstractCommand | |
{ | |
use ProductsCommandTrait; | |
protected $productIds; | |
public function __construct(CategoryId $categoryId, array $productIds) | |
{ | |
parent::__construct($categoryId); | |
$this->setProductIds($productIds); | |
} | |
} | |
class AddProductsToCategoryCommand extends AbstractProductsCommand | |
{ | |
final protected static $messageName = CommandId::ADD_PRODUCTS_TO_CATEGORY; | |
} | |
class RemoveProductsFromCategoryCommand extends AbstractProductsCommand | |
{ | |
final protected static $messageName = CommandId::REMOVE_PRODUCTS_FROM_CATEGORY; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Category\Controller; | |
use Category\CategoryId; | |
use Category\Command\CommandCenter; | |
use Http\Status; | |
use Platform\Controller\RestController; | |
use Product\ProductId; | |
class ProductsController extends RestController | |
{ | |
/** | |
* @var CommandCenter | |
*/ | |
protected $commandCenter; | |
public function put($id, array $data) | |
{ | |
$categoryId = CategoryId::fromString($id); | |
$productIds = $this->getProductIds($data['productIds']); | |
$commandId = $this->commandCenter->addProductsToCategory($categoryId, $productIds); | |
return Status::202(compact('commandId')); | |
} | |
public function delete(array $data) | |
{ | |
$categoryId = CategoryId::fromString($data['categoryId']); | |
$productIds = $this->getProductIds($data['productIds']); | |
$commandId = $this->commandCenter->removeProductsFromCategory($categoryId, $productIds); | |
return Status::202(compact('commandId')); | |
} | |
private function getProductIds(array $dataIds) | |
{ | |
$convertIds = function($productId) { | |
return ProductId::fromString($productId); | |
}; | |
return array_map($convertIds, $dataIds); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Message; | |
use DateTime; | |
use DomainException; | |
interface Message | |
{ | |
public function messageId(); | |
public function messageName(); | |
public function messageTime(); | |
} | |
/** | |
* A rich and expressive unique identifier that forms the heart of every message. | |
*/ | |
class MessageId | |
{ | |
protected static $partSeperator = '_'; | |
protected static $hashAlgorithm = 'crc32'; | |
protected static $validNames = []; | |
protected $name; | |
protected $time; | |
protected $hash; | |
public function __construct($name) | |
{ | |
$this->time = microtime(true); | |
$this->name = trim($name); | |
$this->hash = hash(static::$hashAlgorithm, rand(0, $this->time)); | |
if (empty($this->name)) { | |
throw new DomainException('Name must not be empty'); | |
} | |
if (!in_array($this->name, static::$validNames)) { | |
throw new DomainException("Invalid name [$name]"); | |
} | |
} | |
public static function fromString($idString) | |
{ | |
$parts = explode(static::$partSeperator, $idString); | |
if (count($parts) != 3) { | |
throw new DomainException("Unexpected format [$idString]"); | |
} | |
return static::fromParts($parts[0], $parts[1], $parts[2]); | |
} | |
public static function fromParts($name, $time, $hash) | |
{ | |
$id = new static($name); | |
$id->time = floatval($time); | |
$id->hash = strval($hash); | |
return $id; | |
} | |
public function name() | |
{ | |
return $this->name; | |
} | |
public function time() | |
{ | |
return $this->time; | |
} | |
public function equals(self $other) | |
{ | |
return ((string) $this == (string) $other); | |
} | |
public function __toString() | |
{ | |
return implode(static::$partSeperator, (array) $this); | |
} | |
} | |
trait MessageTrait | |
{ | |
private function setAggregateRootId($aggregateRootId) | |
{ | |
$this->aggregateRootId = $aggregateRootId; | |
} | |
public function aggregateRootId() | |
{ | |
return $this->aggregateRootId; | |
} | |
public function messageId() | |
{ | |
return $this->messageId; | |
} | |
public function messageName() | |
{ | |
return $this->messageId->name(); | |
} | |
public function messageTime() | |
{ | |
return DateTime::createFromFormat('U.u', $this->messageId->time()); | |
} | |
} | |
abstract class AbstractMessage implements Message | |
{ | |
protected static $messageName; | |
protected $aggregateRootId; | |
protected $messageId; | |
public function __construct(MessageId $messageId = null) | |
{ | |
$this->messageId = $messageId ?: new MessageId(static::$messageName); | |
} | |
public function equals(self $other) | |
{ | |
return $this->messageId->equals($other->messageId); | |
} | |
} | |
interface MessageBus | |
{ | |
public function append(Message $aMessage); | |
public function clear(); | |
public function deliver(Dispatcher $withDispatcher); | |
} | |
interface Dispatcher | |
{ | |
public function dispatch(Message $aMessage); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Product\Command; | |
use DomainException; | |
use Product\ProductId; | |
trait ProductsCommandTrait | |
{ | |
public function productIds() | |
{ | |
return $this->productIds; | |
} | |
private function setProductIds(array $productIds) | |
{ | |
$this->productIds = []; | |
foreach (array_unique($productIds) as $productId) { | |
$this->assertProductId($productId); | |
$this->productIds[] = $productId; | |
} | |
} | |
private function assertProductId($value) | |
{ | |
if (!($value instanceof ProductId)) { | |
$message = sprintf( | |
'Expected ProductId, not %s', | |
is_object($value) ? get_class($value) : gettype($value) | |
); | |
throw new DomainException($message); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment