Skip to content

Commit

Permalink
Merge branch 'domain-message'
Browse files Browse the repository at this point in the history
* domain-message:
  changed parameter order
  ignore .phpunit.result.cache
  updated Travis CI build status badge
  changed parameter order
  applied coding standard
  permission
  Add changelog
  Implement skipped testcase
  Refactor saga handling to work with DomainMessage instead of event
  • Loading branch information
othillo committed Apr 8, 2020
2 parents 2fa467f + 07c03da commit 326b386
Show file tree
Hide file tree
Showing 13 changed files with 105 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/vendor/
composer.lock
.php_cs.cache
.phpunit.result.cache
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

## 0.9.x

The major change in this version, is that `sagas` are now handled with
the `DomainMessage` instead of the `event`. This makes it more consistent with the
rest of the broadway framework, where all event listeners are handled with the
`DomainMessage`.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ can be used to wake up and start sagas.

> Note: this component is highly experimental.
[![Build Status](https://travis-ci.org/broadway/broadway-saga.svg?branch=master)](https://travis-ci.org/broadway/broadway-saga)
[![Build Status](https://travis-ci.com/broadway/broadway-saga.svg?branch=master)](https://travis-ci.com/broadway/broadway-saga)

# Installation

Expand Down
6 changes: 3 additions & 3 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ Sagas in Broadway

In Broadway, a saga is a class that extends `Broadway\Saga\Saga`. It uses the
same conventions for listening to events (i.e. it has `handle*()` methods for
each event it's interested in). Instead of a `DomainMessage` instance, each
of the `handle*()` methods receives a `Broadway\Saga\State` object as the
second argument.
each event it's interested in). The major difference is that the first argument
of the `handle*()` receive the `Broadway\Saga\State` object, followed by
`event` and `DomainMessage`.

A `handle*()` method should always return a `State` object, which will be
persisted afterwards. The `State` object can be used to store anything that's
Expand Down
17 changes: 9 additions & 8 deletions src/Metadata/Metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Broadway\Saga\Metadata;

use Broadway\Domain\DomainMessage;
use Broadway\Saga\MetadataInterface;
use RuntimeException;

Expand All @@ -31,29 +32,29 @@ public function __construct($criteria)
/**
* {@inheritdoc}
*/
public function handles($event)
public function handles(DomainMessage $domainMessage)
{
$eventName = $this->getClassName($event);
$eventName = $this->getClassName($domainMessage);

return isset($this->criteria[$eventName]);
}

/**
* {@inheritdoc}
*/
public function criteria($event)
public function criteria(DomainMessage $domainMessage)
{
$eventName = $this->getClassName($event);

if (!isset($this->criteria[$eventName])) {
$eventName = $this->getClassName($domainMessage);
if (!$this->handles($domainMessage)) {
throw new RuntimeException(sprintf("No criteria for event '%s'.", $eventName));
}

return $this->criteria[$eventName]($event);
return $this->criteria[$eventName]($domainMessage->getPayload(), $domainMessage);
}

private function getClassName($event)
private function getClassName(DomainMessage $domainMessage)
{
$event = $domainMessage->getPayload();
$classParts = explode('\\', get_class($event));

return end($classParts);
Expand Down
7 changes: 3 additions & 4 deletions src/MetadataInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@

namespace Broadway\Saga;

use Broadway\Domain\DomainMessage;
use Broadway\Saga\State\Criteria;

interface MetadataInterface
{
/**
* @param mixed $event
*
* @return bool True, if the saga can handle the event
*/
public function handles($event);
public function handles(DomainMessage $domainMessage);

/**
* @return Criteria Criteria for the given event
*/
public function criteria($event);
public function criteria(DomainMessage $domainMessage);
}
16 changes: 6 additions & 10 deletions src/MultipleSagaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,16 @@
class MultipleSagaManager implements SagaManagerInterface
{
private $repository;
private $stateManager;
private $eventDispatcher;

/**
* @var SagaInterface[]
*/
private $sagas;

private $sagas = [];
private $stateManager;
/**
* @var MetadataFactoryInterface
*/
private $metadataFactory;
private $eventDispatcher;

public function __construct(
RepositoryInterface $repository,
Expand All @@ -57,16 +55,14 @@ public function __construct(
*/
public function handle(DomainMessage $domainMessage): void
{
$event = $domainMessage->getPayload();

foreach ($this->sagas as $sagaType => $saga) {
$metadata = $this->metadataFactory->create($saga);

if (!$metadata->handles($event)) {
if (!$metadata->handles($domainMessage)) {
continue;
}

$state = $this->stateManager->findOneBy($metadata->criteria($event), $sagaType);
$state = $this->stateManager->findOneBy($metadata->criteria($domainMessage), $sagaType);

if (null === $state) {
continue;
Expand All @@ -76,7 +72,7 @@ public function handle(DomainMessage $domainMessage): void
[$sagaType, $state->getId()]
);

$newState = $saga->handle($event, $state);
$newState = $saga->handle($domainMessage, $state);

$this->eventDispatcher->dispatch(
SagaManagerInterface::EVENT_POST_HANDLE,
Expand Down
12 changes: 7 additions & 5 deletions src/Saga.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,27 @@

namespace Broadway\Saga;

use Broadway\Domain\DomainMessage;

abstract class Saga implements SagaInterface
{
/**
* {@inheritdoc}
*/
public function handle($event, State $state)
public function handle(DomainMessage $domainMessage, State $state)
{
$method = $this->getHandleMethod($event);
$method = $this->getHandleMethod($domainMessage);

if (!method_exists($this, $method)) {
throw new \BadMethodCallException(sprintf("No handle method '%s' for event '%s'.", $method, get_class($event)));
}

return $this->$method($event, $state);
return $this->$method($domainMessage->getPayload(), $state);
}

private function getHandleMethod($event)
private function getHandleMethod(DomainMessage $domainMessage)
{
$classParts = explode('\\', get_class($event));
$classParts = explode('\\', get_class($domainMessage->getPayload()));

return 'handle'.end($classParts);
}
Expand Down
6 changes: 3 additions & 3 deletions src/SagaInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@

namespace Broadway\Saga;

use Broadway\Domain\DomainMessage;

interface SagaInterface
{
/**
* @param mixed $event
*
* @return State
*/
public function handle($event, State $state);
public function handle(DomainMessage $domainMessage, State $state);
}
16 changes: 15 additions & 1 deletion src/Testing/Scenario.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Scenario
private $testCase;
private $sagaManager;
private $traceableCommandBus;
private $aggregateId;
private $playhead;

public function __construct(
Expand All @@ -34,9 +35,22 @@ public function __construct(
$this->testCase = $testCase;
$this->sagaManager = $sagaManager;
$this->traceableCommandBus = $traceableCommandBus;
$this->aggregateId = 1;
$this->playhead = -1;
}

/**
* @param string $aggregateId
*
* @return Scenario
*/
public function withAggregateId($aggregateId)
{
$this->aggregateId = $aggregateId;

return $this;
}

/**
* @return Scenario
*/
Expand Down Expand Up @@ -77,6 +91,6 @@ private function createDomainMessageForEvent($event)
{
++$this->playhead;

return DomainMessage::recordNow(1, $this->playhead, new Metadata([]), $event);
return DomainMessage::recordNow($this->aggregateId, $this->playhead, new Metadata([]), $event);
}
}
37 changes: 27 additions & 10 deletions test/Metadata/StaticallyConfiguredSagaMetadataFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

namespace Broadway\Saga\Metadata;

use Broadway\Domain\DomainMessage;
use Broadway\Saga\State;
use Broadway\Saga\State\Criteria;
use PHPUnit\Framework\TestCase;

Expand All @@ -23,25 +25,40 @@ class StaticallyConfiguredSagaMetadataFactoryTest extends TestCase
*/
public function it_creates_metadata_using_the_saga_configuration()
{
$this->markTestSkipped('Yay phpunit');
$metadataFactory = new StaticallyConfiguredSagaMetadataFactory();
$criteria = new Criteria(['id' => 'YoLo']);

$saga = $this->getMockBuilder('Broadway\Saga\Metadata\StaticallyConfiguredSagaInterface')->getMock();
$saga->staticExpects($this->any())
->method('configuration')
->will($this->returnValue(['StaticallyConfiguredSagaMetadataFactoryTestEvent' => function ($event) use ($criteria) { return $criteria; }]));

$saga = new StaticallyConfiguredSaga();
$metadata = $metadataFactory->create($saga);

$this->assertInstanceOf('Broadway\Saga\MetadataInterface', $metadata);

$event = new StaticallyConfiguredSagaMetadataFactoryTestEvent();
$this->assertTrue($metadata->handles($event));
$this->assertEquals($criteria, $metadata->criteria($event));
$domainMessage = DomainMessage::recordNow('id', 0, new \Broadway\Domain\Metadata([]), $event);

$this->assertTrue($metadata->handles($domainMessage));
$this->assertEquals(
new Criteria(['id' => $domainMessage->getId()]),
$metadata->criteria($domainMessage)
);
}
}

class StaticallyConfiguredSagaMetadataFactoryTestEvent
{
}

class StaticallyConfiguredSaga implements StaticallyConfiguredSagaInterface
{
public function handle(DomainMessage $domainMessage, State $state)
{
return $state;
}

public static function configuration()
{
return [
'StaticallyConfiguredSagaMetadataFactoryTestEvent' => function ($event, DomainMessage $domainMessage) {
return new Criteria(['id' => $domainMessage->getId()]);
},
];
}
}
25 changes: 20 additions & 5 deletions test/Metadata/StaticallyConfiguredSagaMetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,22 @@

namespace Broadway\Saga\Metadata;

use Broadway\Domain\DomainMessage;
use PHPUnit\Framework\TestCase;

class StaticallyConfiguredSagaMetadataTest extends TestCase
{
/**
* @var Metadata
*/
private $metadata;

public function setUp(): void
{
$this->metadata = new Metadata([
'StaticallyConfiguredSagaMetadataTestSagaTestEvent1' => function () { return 'criteria'; },
'StaticallyConfiguredSagaMetadataTestSagaTestEvent1' => function () {
return 'criteria';
},
]);
}

Expand All @@ -32,8 +38,9 @@ public function setUp(): void
public function it_handles_an_event_if_its_specified_by_the_saga()
{
$event = new StaticallyConfiguredSagaMetadataTestSagaTestEvent1();
$domainMessage = $this->createDomainMessage($event);

$this->assertTrue($this->metadata->handles($event));
$this->assertTrue($this->metadata->handles($domainMessage));
}

/**
Expand All @@ -42,8 +49,9 @@ public function it_handles_an_event_if_its_specified_by_the_saga()
public function it_does_not_handle_an_event_if_its_not_specified_by_the_saga()
{
$event = new StaticallyConfiguredSagaMetadataTestSagaTestEvent2();
$domainMessage = $this->createDomainMessage($event);

$this->assertFalse($this->metadata->handles($event));
$this->assertFalse($this->metadata->handles($domainMessage));
}

/**
Expand All @@ -52,8 +60,9 @@ public function it_does_not_handle_an_event_if_its_not_specified_by_the_saga()
public function it_returns_the_criteria_for_a_configured_event()
{
$event = new StaticallyConfiguredSagaMetadataTestSagaTestEvent1();
$domainMessage = $this->createDomainMessage($event);

$this->assertEquals('criteria', $this->metadata->criteria($event));
$this->assertEquals('criteria', $this->metadata->criteria($domainMessage));
}

/**
Expand All @@ -63,8 +72,14 @@ public function it_throws_an_exception_if_there_is_no_criteria_for_a_given_event
{
$this->expectException('RuntimeException');
$event = new StaticallyConfiguredSagaMetadataTestSagaTestEvent2();
$domainMessage = $this->createDomainMessage($event);

$this->metadata->criteria($event);
$this->metadata->criteria($domainMessage);
}

private function createDomainMessage($event): DomainMessage
{
return DomainMessage::recordNow('id', 0, new \Broadway\Domain\Metadata([]), $event);
}
}

Expand Down
3 changes: 2 additions & 1 deletion test/MultipleSagaManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,10 @@ class SagaManagerTestSaga implements StaticallyConfiguredSagaInterface
{
public $isCalled = false;

public function handle($event, State $state = null)
public function handle(DomainMessage $domainMessage, State $state)
{
$this->isCalled = true;
$event = $domainMessage->getPayload();

if ($event instanceof TestEvent1) {
$state->set('event', 'testevent1');
Expand Down

0 comments on commit 326b386

Please sign in to comment.