-
-
Save muhamed-didovic/b19370004843cf2ae74c to your computer and use it in GitHub Desktop.
This file contains hidden or 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 hidden or 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\CommandId; | |
use Http\Status; | |
use Product\ProductId; | |
class ProductsController | |
{ | |
use ControllerTrait; | |
public function put($id, array $data) | |
{ | |
$categoryId = CategoryId::fromString($id); | |
$productIds = $this->getProductIds($data['productIds']); | |
$commandId = $this->commandCenter->issue( | |
new AddProductsToCategoryCommand($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->issue( | |
new RemoveProductsFromCategoryCommand($categoryId, $productIds) | |
); | |
return Status::202(compact('commandId')); | |
} | |
private function getProductIds(array $dataIds) | |
{ | |
return array_map(function($productId) { | |
return ProductId::fromString($productId); | |
}, $dataIds); | |
} | |
} |
This file contains hidden or 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 hidden or 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