Skip to content

Commit 40ed442

Browse files
committed
add code
1 parent 73c6c3c commit 40ed442

33 files changed

+1701
-39
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
/composer.phar
66
/composer.lock
77
/metrics
8+
/bin

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"name": "goodby/cqrs",
2+
"name": "goodby/CQRS",
33
"description": "",
44
"keywords": [],
5-
"homepage": "https://github.com/goodby/cqrs",
5+
"homepage": "https://github.com/goodby/php-CQRS",
66
"license": "MIT",
77
"require": {
88
"php": ">=5.3.2"
@@ -18,7 +18,7 @@
1818
},
1919
"autoload": {
2020
"psr-0": {
21-
"Goodby\\Cqrs": "src/"
21+
"Goodby\\CQRS": ["src/", "tests/"]
2222
}
2323
}
2424
}

phpunit.xml.dist

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
colors="true">
88
<testsuites>
99
<testsuite name="PHPUnit">
10-
<directory>src/*/*/Tests</directory>
10+
<directory>tests</directory>
1111
</testsuite>
1212
</testsuites>
1313

@@ -37,8 +37,6 @@
3737
<!-- <file>/path/to/file</file> -->
3838
<exclude>
3939
<!-- <file>/path/to/file</file> -->
40-
<directory suffix="Interface.php">src</directory>
41-
<directory>src/*/*/Tests</directory>
4240
</exclude>
4341
</whitelist>
4442
</filter>

src/Goodby/CQRS/Assertion/Assert.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace Goodby\CQRS\Assertion;
4+
5+
use InvalidArgumentException;
6+
7+
class Assert
8+
{
9+
/**
10+
* @param mixed $argument
11+
* @param string $message
12+
* @throws InvalidArgumentException
13+
*/
14+
public static function argumentNotEmpty($argument, $message)
15+
{
16+
if (empty($argument)) {
17+
throw new InvalidArgumentException($message);
18+
}
19+
}
20+
21+
/**
22+
* @param string $className
23+
* @param string $message
24+
* @throws InvalidArgumentException
25+
*/
26+
public static function argumentIsClass($className, $message)
27+
{
28+
if (class_exists($className) === false) {
29+
throw new InvalidArgumentException($message);
30+
}
31+
}
32+
33+
/**
34+
* @param string $className
35+
* @param string $message
36+
* @param string $superClassName
37+
* @throws InvalidArgumentException
38+
*/
39+
public static function argumentSubclass($className, $message, $superClassName)
40+
{
41+
if (is_subclass_of($className, $superClassName) === false) {
42+
throw new InvalidArgumentException($message . ':' . $superClassName);
43+
}
44+
}
45+
46+
/**
47+
* @param int $number
48+
* @param int $minimum
49+
* @param string $message
50+
* @throws InvalidArgumentException
51+
*/
52+
public static function argumentAtLeast($number, $minimum, $message)
53+
{
54+
if ($number < $minimum) {
55+
throw new InvalidArgumentException($message);
56+
}
57+
}
58+
59+
/**
60+
* @param array $array
61+
* @param int $minimum
62+
* @param string $message
63+
* @throws InvalidArgumentException
64+
*/
65+
public static function argumentAtLeastArray(array $array, $minimum, $message)
66+
{
67+
if (count($array) < $minimum) {
68+
throw new InvalidArgumentException($message);
69+
}
70+
}
71+
72+
/**
73+
* @param $object
74+
* @param $message
75+
* @throws InvalidArgumentException
76+
*/
77+
public static function argumentIsObject($object, $message)
78+
{
79+
if (is_object($object) === false) {
80+
throw new InvalidArgumentException($message);
81+
}
82+
}
83+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Goodby\CQRS\DDDSupport;
4+
5+
use DateTime;
6+
7+
interface DomainEvent
8+
{
9+
/**
10+
* @return int
11+
*/
12+
public function eventVersion();
13+
14+
/**
15+
* @return DateTime
16+
*/
17+
public function occurredOn();
18+
19+
/**
20+
* @return array
21+
*/
22+
public function toContractualData();
23+
24+
/**
25+
* @param array $data
26+
* @return DomainEvent
27+
*/
28+
public static function fromContractualData(array $data);
29+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Goodby\CQRS\DDDSupport\EventTracking;
4+
5+
use ReflectionException;
6+
use RuntimeException;
7+
8+
class AggregateMutateException extends RuntimeException
9+
{
10+
/**
11+
* @param string $aggregateClassName
12+
* @param string $mutatorMethodName
13+
* @param string $eventClassName
14+
* @return AggregateMutateException
15+
*/
16+
public static function mutatorMethodNotExist($aggregateClassName, $mutatorMethodName, $eventClassName)
17+
{
18+
return new self(
19+
sprintf(
20+
'Mutator method not found: %s::%s(%s $event)',
21+
$aggregateClassName,
22+
$mutatorMethodName,
23+
$eventClassName
24+
)
25+
);
26+
}
27+
28+
/**
29+
* @param ReflectionException $because
30+
* @return AggregateMutateException
31+
*/
32+
public static function because(ReflectionException $because)
33+
{
34+
return new self(
35+
sprintf('Aggregate mutation failed, because: %s', $because->getMessage()),
36+
null,
37+
$because
38+
);
39+
}
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Goodby\CQRS\DDDSupport\EventTracking;
4+
5+
use ReflectionClass;
6+
use ReflectionException;
7+
use ReflectionMethod;
8+
use Goodby\CQRS\Assertion\Assert;
9+
use Goodby\CQRS\DDDSupport\DomainEvent;
10+
11+
class AggregateMutator
12+
{
13+
/**
14+
* @param object $aggregate
15+
* @param DomainEvent $domainEvent
16+
* @param string $mutatorMethodFormat
17+
* @throws AggregateMutateException
18+
* @throws AggregateMutateException
19+
*/
20+
public static function mutateWhen($aggregate, DomainEvent $domainEvent, $mutatorMethodFormat = 'when{EventClassName}')
21+
{
22+
Assert::argumentIsObject($aggregate, 'Aggregate must be an object');
23+
24+
$eventClassName = (new ReflectionClass($domainEvent))->getShortName();
25+
$mutatorMethodName = str_replace('{EventClassName}', $eventClassName, $mutatorMethodFormat);
26+
27+
if (method_exists($aggregate, $mutatorMethodName) === false) {
28+
throw AggregateMutateException::mutatorMethodNotExist(get_class($aggregate), $mutatorMethodName, $eventClassName);
29+
}
30+
31+
try {
32+
$mutatorMethod = new ReflectionMethod($aggregate, $mutatorMethodName);
33+
$mutatorMethod->setAccessible(true);
34+
$mutatorMethod->invoke($aggregate, $domainEvent);
35+
} catch (ReflectionException $because) {
36+
throw AggregateMutateException::because($because);
37+
}
38+
}
39+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
namespace Goodby\CQRS\DDDSupport\EventTracking\CompositionStyle;
4+
5+
use ReflectionClass;
6+
use Goodby\CQRS\Assertion\Assert;
7+
use Goodby\CQRS\DDDSupport\DomainEvent;
8+
use Goodby\CQRS\DDDSupport\EventTracking\AggregateMutator;
9+
10+
class AggregateMutation
11+
{
12+
/**
13+
* @var object
14+
*/
15+
private $aggregate;
16+
17+
/**
18+
* @var DomainEvent[]
19+
*/
20+
private $mutatingEvents = [];
21+
22+
/**
23+
* @var int
24+
*/
25+
private $unmutatedVersion = 0;
26+
27+
/**
28+
* @param string $aggregateClassName
29+
* @param DomainEvent[] $domainEvents
30+
* @param int $eventStreamVersion
31+
* @return AggregateMutation
32+
*/
33+
public static function constructWithEventStream($aggregateClassName, array $domainEvents, $eventStreamVersion)
34+
{
35+
Assert::argumentIsClass($aggregateClassName, 'Aggregate class name must be existing class name');
36+
Assert::argumentAtLeastArray($domainEvents, 1, 'Domain events must contain at least one event');
37+
38+
$aggregate = (new ReflectionClass($aggregateClassName))->newInstanceWithoutConstructor();
39+
40+
$aggregateMutation = new self($aggregate);
41+
42+
foreach ($domainEvents as $domainEvent) {
43+
$aggregateMutation->mutateWhen($domainEvent);
44+
}
45+
46+
$aggregateMutation->unmutatedVersion = $eventStreamVersion;
47+
48+
return $aggregateMutation;
49+
}
50+
51+
/**
52+
* @param $aggregate
53+
*/
54+
public function __construct($aggregate)
55+
{
56+
Assert::argumentIsObject($aggregate, 'Aggregate must be an object');
57+
58+
$this->aggregate = $aggregate;
59+
}
60+
61+
/**
62+
* @return object
63+
*/
64+
public function aggregate()
65+
{
66+
return $this->aggregate;
67+
}
68+
69+
/**
70+
* @return int
71+
*/
72+
public function mutatedVersion()
73+
{
74+
return $this->unmutatedVersion + 1;
75+
}
76+
77+
/**
78+
* @param object $aggregate
79+
* @return bool
80+
*/
81+
public function aggregateEquals($aggregate)
82+
{
83+
return ($this->aggregate === $aggregate);
84+
}
85+
86+
/**
87+
* @return DomainEvent[]
88+
*/
89+
public function mutatingEvents()
90+
{
91+
return $this->mutatingEvents;
92+
}
93+
94+
/**
95+
* @return int
96+
*/
97+
public function unmutatedVersion()
98+
{
99+
return $this->unmutatedVersion;
100+
}
101+
102+
/**
103+
* @param DomainEvent $event
104+
*/
105+
public function apply(DomainEvent $event)
106+
{
107+
$this->mutatingEvents[] = $event;
108+
$this->mutateWhen($event);
109+
}
110+
111+
/**
112+
* @param DomainEvent $domainEvent
113+
*/
114+
private function mutateWhen(DomainEvent $domainEvent)
115+
{
116+
AggregateMutator::mutateWhen($this->aggregate, $domainEvent);
117+
}
118+
}
119+

0 commit comments

Comments
 (0)