Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/beberlei/litecqrs-php

Small convention based CQRS library for PHP
https://github.com/beberlei/litecqrs-php

Last synced: 18 days ago
JSON representation

Small convention based CQRS library for PHP

Lists

README

        

# LiteCQRS for PHP

Small naming-convention based CQRS library for PHP (loosely based on [LiteCQRS for
C#](https://github.com/danielwertheim/LiteCQRS)) that relies on the MessageBus,
Command, EventSourcing and Domain Event patterns.

[![Build Status (Master)](https://travis-ci.org/beberlei/litecqrs-php.png?branch=master)](https://travis-ci.org/beberlei/litecqrs-php)

**NOTE** Use the 1.1 branch, as the dev-master is currently in heavy refactoring.

## Terminology

CQS is Command-Query-Separation: A paradigm where read methods never change
state and write methods never return data. Build on top, CQRS suggests the
separation of read- from write-model and uses the [DomainEvent
pattern](http://martinfowler.com/eaaDev/DomainEvent.html) to notify the read
model about changes in the write model.

LiteCQRS uses the command pattern and a central message bus service that
finds the corresponding handler to execute a command. A command is just a class
with some properties describing it, it can optionally implement ``LiteCQRS\Command``.

During the execution of a command, domain events can be triggered. These are
again just simple classes with some properties and they can optionally implement
``LiteCQRS\DomainEvent``.

An event queue knows what domain events have been triggered during a command
and then publishes them to an event message bus, where many listeners can
listen to them.

## Changes

### From 1.0 to 1.1

* Extending ``LiteCQRS\Command`` and ``LiteCQRS\DomainEvent`` is NOT required anymore.
In fact you can use any class as command or event. The naming conventions alone
make sure command handlers and event listeners are detected.

* JMS Serializer Plugin cannot "detach" aggregate root properties that are part
of an event that is serialized anymore. Putting related aggregate roots into
an Event is therefore not supported anymore (and not a good idea even with
JMS Serializer 0.9 anyways).

## Conventions

* All public methods of a command handler class are mapped to Commands "Command
Class Shortname" => "MethodName" when the method and command class shortname match.
Implementing an interface for the commands is NOT required (since 1.1)
* Domain Events are applied to Event Handlers "Event Class Shortname" =>
"onEventClassShortname". Only if this matches is an event listener registered.
* Domain Events are applied on Entities/Aggregate Roots "Event Class Shortname"
=> "applyEventClassShortname"
* You can optionally extend the ``DefaultDomainEvent`` which has a constructor
that maps its array input to properties and throws an exception if an unknown
property is passed.
* There is also a ``DefaultCommand`` with the same semantics as
``DefaultDomainEvent``. Extending this is not required.

Examples:

* ``HelloWorld\GreetingCommand`` maps to the ``greeting(GreetingCommand $command)`` method on the registered handler.
* ``HelloWorld\Commands\Greeting`` maps to the ``greeting(Greeting $command)`` method on the registered handler.
* ``HelloWorld\GreetedEvent`` is passed to all event handlers that have a method ``onGreeted(GreetedEvent $event)``.
* ``HelloWorld\Events\Greeted`` is passed to all event handlers that have a method ``onGreeted(Greeted $event)``.
* ``HelloWorld\GreetedEvent`` is delegated to ``applyGreeted($event)`` when created on the aggregate root

## Installation & Requirements

Use the 1.1 branch, as the dev-master is currently in heavy refactoring.

The core library has no dependencies on other libraries. Plugins have dependencies on their specific libraries.

Install with [Composer](http://getcomposer.org):

{
"require": {
"beberlei/lite-cqrs": "1.1"
}
}

## Workflow

These are the steps that a command regularly takes through the LiteCQRS stack during execution:

1. You push commands into a ``CommandBus``. Commands are simple objects
extending ``Command`` created by you.
2. The ``CommandBus`` checks for a handler that can execute your command. Every
command has exactly one handler.
3. The command handler changes state of the domain model. It does that by
creating events (that represent state change) and passing them to the
``AggregateRoot::apply()`` or ``DomainEventProvider::raise()`` method of your domain objects.
4. When the command is completed, the command bus will check all objects in the
identity map for events.
5. All found events will be passed to the ``EventMessageBus#publish()`` method.
6. The EventMessageBus dispatches all events to observing event handlers.
8. Event Handlers can create new commands again using the ``CommandBus``.

Command and Event handler execution can be wrapped in handlers that manage
transactions. Event handling is always triggered outside of any command
transaction. If the command fails with any exception all events created by the
command are forgotten/ignored. No event handlers will be triggered in this case.

In the case of InMemory CommandBus and EventMessageBus LiteCQRS makes sure that
the execution of command and event handlers is never nested, but in sequential
linearized order. This prevents independent transactions for each command
from affecting each other.

## Examples

See [examples/](https://github.com/beberlei/litecqrs-php/tree/master/example) for
some examples:

1. ``example1.php`` shows usage of the Command- and EventMessageBus with one domain object
2. ``example2_event.php`` shows direct usage of the EventMessageBus inside a command
3. ``example3_sequential_commands.php`` demonstrates how commands are processed sequentially.
4. ``tictactoe.php`` implements a tic tac toe game with CQRS.
5. ``SymfonyExample.md`` shows ``example1.php`` implemented within the scope of a Symfony2 project.

## Setup

1. In Memory Command Handlers, no event publishing/observing

```php
register('MyApp\ChangeEmailCommand', $userService);
```

2. In Memory Commands and Events Handlers

This uses ``LiteCQRS\EventProviderInterface`` instances to trigger domain events.

```php
register('MyApp\ChangeEmailCommand', $userService);

$someEventHandler = new MyEventHandler();
$messageBus->register($someEventHandler);
```

3. In Memory Commands + Custom Event Queue

LiteCQRS knows about triggered events by asking ``LiteCQRS\Bus\EventQueue``.
Provide your own implementation to be independent of
your domain objects having to implement ``EventProviderInterface``.

```php