Permalink
Browse files

Lite CQRS library init commit

  • Loading branch information...
0 parents commit f386ad87ec02a0bc8c8292125afcbcf7ffa357da @beberlei committed Aug 9, 2012
@@ -0,0 +1 @@
+vendor
@@ -0,0 +1,11 @@
+# LiteCQRS for PHP
+
+Small convention based CQRS library for PHP (loosly based on [LiteCQRS for C#](https://github.com/danielwertheim/LiteCQRS)).
+
+Conventions are:
+
+* All public methods of a command handler class are mapped to Commands "Command Class Shortname" => "MethodName", for example "MyLib\DoSomethingCommand" => "doSomething($command)"
+* Domain Events are applied on Entities/Aggregate Roots "Event Class Shortname" => "applyEventClassShortname", for example "MyLib\SomethingDoneEvent => "applySomethingDone($event)"
+* Domain Events are applied to Event Handlers "Event Class Shortname" => "onEventClassShortname", for example "MyLib\SomethingDoneEvent" => "onSomethingDone($event)"
+
+You can stick yourself together a simple CQRS application by implementing a ``CommandBus` and an ``EventMessageBus``.
@@ -0,0 +1,8 @@
+{
+ "name": "beberlei/lite-cqrs",
+ "description": "Simple Command-Query-Responsibility-Segregation library.",
+ "license": "MIT",
+ "autoload": {
+ "psr-0": {"LiteCQRS": "src/"}
+ }
+}

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -0,0 +1,18 @@
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ processIsolation="false"
+ stopOnFailure="false"
+ syntaxCheck="false"
+ bootstrap="vendor/autoload.php"
+>
+
+ <testsuites>
+ <testsuite name="LiteCQRS TestSuite">
+ <directory>./tests/</directory>
+ </testsuite>
+ </testsuites>
+</phpunit>
@@ -0,0 +1,10 @@
+<?php
+namespace LiteCQRS;
+
+interface AggregateRoot
+{
+ public function getAppliedEvents();
+ public function loadFromHistory(array $events);
+}
+
+
@@ -0,0 +1,39 @@
+<?php
+
+namespace LiteCQRS;
+
+abstract class BaseAggregateRoot implements AggregateRoot
+{
+ private $appliedEvents;
+
+ public function getAppliedEvents()
+ {
+ return $this->appliedEvents;
+ }
+
+ protected function apply(DomainEvent $event)
+ {
+ $method = sprintf('apply%s', $event->getEventName());
+
+ if (!method_exists($this, $method)) {
+ throw new \BadMethodCallException("There is no event named '$method' that can be applied to '" . get_class($this) . "'");
+ }
+
+ $this->$method($event);
+ $this->appliedEvents[] = $event;
+ }
+
+ public function loadFromHistory(array $events)
+ {
+ foreach ($events as $event) {
+ $this->apply($event);
+ }
+ }
+
+ public function clearEvents()
+ {
+ $this->appliedEvents = array();
+ }
+}
+
+
@@ -0,0 +1,32 @@
+<?php
+
+namespace LiteCQRS\Bus;
+
+use LiteCQRS\Command;
+
+abstract class CommandBus implements MessageBus
+{
+ abstract protected function getService($commandType);
+ abstract protected function wrapHandlerChain($service, $method);
+
+ public function handle($command)
+ {
+ if (!($command instanceof Command)) {
@stof

stof Aug 9, 2012

why not usign a typehint ?

@beberlei

beberlei Aug 9, 2012

Owner

because messagebus handles either events or commands, but maybe i should get rid of that interface.

+ throw new \RuntimeException("Invalid message type, has to be a command!");
+ }
+
+ $type = get_class($command);
+ $service = $this->getService($type);
+ $method = $this->getHandlerMethodName($command);
+
+ $service = $this->wrapHandlerChain($service, $method);
+
+ $service->$method($command);
+ }
+
+ public function getHandlerMethodName($command)
+ {
+ $parts = explode("\\", get_class($command));
+ return str_replace("Command", "", lcfirst(end($parts)));
+ }
+}
@@ -0,0 +1,50 @@
+<?php
+namespace LiteCQRS\Bus;
+
+class DirectCommandBus extends CommandBus
+{
+ private $handlers = array();
+ private $eventBus;
+
+ public function __construct(DirectEventMessageBus $eventBus = null)
+ {
+ $this->eventBus = $eventBus ?: new DirectEventMessageBus();
+ }
+
+ public function register($commandType, $service, $method = null)
+ {
+ if (!is_object($service)) {
+ throw new \RuntimeException("No valid service given for command type '" . $commandType . "'");
+ }
+
+ if ($method === null) {
+ $parts = explode("\\", $commandType);
+ $method = str_replace("Command", "", lcfirst(end($parts)));
+ }
+
+ $this->handlers[strtolower($commandType)] = array(
+ 'service' => $service,
+ 'method' => $method
+ );
+ }
+
+ protected function getService($commandType)
+ {
+ if (!isset($this->handlers[strtolower($commandType)])) {
+ throw new \RuntimeException("No service registered for command type '" . $commandType . "'");
+ }
+
+ return $this->handlers[strtolower($commandType)]['service'];
+ }
+
+ protected function getCommandMethod($commandType)
+ {
+ return $this->handlers[strtolower($commandType)]['method'];
+ }
+
+ protected function wrapHandlerChain($service, $method)
+ {
+ return $service;
+ }
+}
+
@@ -0,0 +1,30 @@
+<?php
+namespace LiteCQRS\Bus;
+
+class DirectEventMessageBus extends EventMessageBus
+{
+ private $handlers = array();
+
+ public function register($handler)
+ {
+ foreach (get_class_methods($handler) as $methodName) {
+ if (strpos($methodName, "on") !== 0) {
+ continue;
+ }
+
+ $eventName = strtolower(substr($methodName, 2));
+
+ if (!isset($this->handlers[$eventName])) {
+ $this->handlers[$eventName] = array();
+ }
+
+ $this->handlers[$eventName][] = $handler;
+ }
+ }
+
+ protected function getHandlers($eventName)
+ {
+ return $this->handlers[strtolower($eventName)];
+ }
+}
+
@@ -0,0 +1,23 @@
+<?php
+namespace LiteCQRS\Bus;
+
+abstract class EventMessageBus implements MessageBus
+{
+ abstract protected function getHandlers($eventName);
+
+ public function handle($event)
+ {
+ if (!($event instanceof DomainEvent)) {
+ throw new \RuntimeException("No valid Domain Event given!");
+ }
+
+ $eventName = $event->getEventName();
+ $handlers = $this->getHandlers($eventName);
+ $methodName = "on" . $eventName;
+
+ foreach ($handlers as $handler) {
+ $handler->$methodName($event);
+ }
+ }
+}
+
@@ -0,0 +1,7 @@
+<?php
+namespace LiteCQRS\Bus;
+
+interface MessageBus
+{
+ public function handle($message);
+}
@@ -0,0 +1,8 @@
+<?php
+
+namespace LiteCQRS;
+
+interface Command
+{
+}
+
@@ -0,0 +1,21 @@
+<?php
+namespace LiteCQRS;
+
+abstract class DefaultDomainEvent implements DomainEvent
+{
+ public function getEventName()
+ {
+ $class = get_class($class);
@stof

stof Aug 9, 2012

undefined variable

+
+ if (substr($class, -6) === "Event") {
+ $class = substr($class, 0, -6);
+ }
+
+ if (strpos($class, "\\") === false) {
+ return $class;
+ }
+
+ return substr($class, 1+strpos(strrev($class), "\\"));
+ }
+}
+
@@ -0,0 +1,8 @@
+<?php
+namespace LiteCQRS;
+
+interface DomainEvent
+{
+ public function getEventName();
+}
+
@@ -0,0 +1,35 @@
+<?php
+namespace LiteCQRS;
+/**
+ * Generic event that can be used when lazy.
+ *
+ * It allows to set the event name dynamically and accepts an array of data as
+ * properties. Access to non existant properties returns null through
+ * {@link __get()}.
+ */
+class DomainObjectChanged implements DomainEvent
+{
+ private $eventName;
+
+ public function __construct($eventName, array $args)
+ {
+ $this->eventName = $eventName;
+ foreach ($args as $property => $value) {
+ $this->$property = $value;
+ }
+ }
+
+ public function getEventName()
+ {
+ return $this->eventName;
+ }
+
+ public function __get($name)
+ {
+ if (isset($this->$name)) {
+ return $this->$name;
+ }
+ return null;
+ }
+}
+
Oops, something went wrong.

0 comments on commit f386ad8

Please sign in to comment.