Skip to content

Commit

Permalink
Merge bdfa843 into cfe630e
Browse files Browse the repository at this point in the history
  • Loading branch information
codeliner committed Nov 16, 2018
2 parents cfe630e + bdfa843 commit f1d4727
Show file tree
Hide file tree
Showing 138 changed files with 4,303 additions and 625 deletions.
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ matrix:
- EXECUTE_CS_CHECK=true
- TEST_COVERAGE=true

- php: 7.2
env:
- DEPENDENCIES=""
- EXECUTE_CS_CHECK=false
- TEST_COVERAGE=false

cache:
directories:
- $HOME/.composer/cache
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"bookdown/bookdown": "1.x-dev",
"webuni/commonmark-table-extension": "^0.6.1",
"webuni/commonmark-attributes-extension": "^0.5.0",
"prooph/php-cs-fixer-config": "^0.1.1",
"prooph/php-cs-fixer-config": "^0.2",
"satooshi/php-coveralls": "^1.0",
"malukenho/docheader": "^0.1.4"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

declare(strict_types=1);

namespace ProophExample\Aggregate;
namespace ProophExample\FunctionalFlavour\Aggregate;

final class Aggregate
{
Expand Down
111 changes: 111 additions & 0 deletions examples/FunctionalFlavour/Aggregate/UserDescription.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php
/**
* This file is part of the proophsoftware/event-machine.
* (c) 2017-2018 prooph software GmbH <contact@prooph.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ProophExample\FunctionalFlavour\Aggregate;

use Prooph\EventMachine\EventMachine;
use Prooph\EventMachine\EventMachineDescription;
use ProophExample\FunctionalFlavour\Api\Command;
use ProophExample\FunctionalFlavour\Api\Event;
use ProophExample\FunctionalFlavour\Command\ChangeUsername;
use ProophExample\FunctionalFlavour\Command\RegisterUser;
use ProophExample\FunctionalFlavour\Event\UsernameChanged;
use ProophExample\FunctionalFlavour\Event\UserRegistered;
use ProophExample\FunctionalFlavour\Event\UserRegistrationFailed;

/**
* Class UserDescription
*
* Tell EventMachine how to handle commands with aggregates, which events are yielded by the handle methods
* and how to apply the yielded events to the aggregate state.
*
* Please note:
* UserDescription uses closures. It is the fastest and most readable way of describing
* aggregate behaviour BUT closures cannot be serialized/cached.
* So the closure style is useful for learning and prototyping but if you want to use Event Machine for
* production, you should consider using a cacheable description like illustrated with CacheableUserDescription.
* Also see EventMachine::cacheableConfig() which throws an exception if it detects usage of closure
* The returned array can be used to call EventMachine::fromCachedConfig(). You can json_encode the config and store it
* in a json file.
*
* @package ProophExample\Aggregate
*/
final class UserDescription implements EventMachineDescription
{
public const IDENTIFIER = 'userId';
public const USERNAME = 'username';
public const EMAIL = 'email';

const STATE_CLASS = UserState::class;

public static function describe(EventMachine $eventMachine): void
{
self::describeRegisterUser($eventMachine);
self::describeChangeUsername($eventMachine);
}

private static function describeRegisterUser(EventMachine $eventMachine): void
{
$eventMachine->process(Command::REGISTER_USER)
->withNew(Aggregate::USER)
->identifiedBy(self::IDENTIFIER)
// Note: Our custom command is passed to the function
->handle(function (RegisterUser $command) {
//We can return a custom event
if ($command->shouldFail) {
yield new UserRegistrationFailed([self::IDENTIFIER => $command->userId]);

return;
}

yield new UserRegistered([
'userId' => $command->userId,
'username' => $command->username,
'email' => $command->email,
]);
})
->recordThat(Event::USER_WAS_REGISTERED)
// The custom event is passed to the apply function
->apply(function (UserRegistered $event) {
return new UserState((array) $event);
})
->orRecordThat(Event::USER_REGISTRATION_FAILED)
->apply(function (UserRegistrationFailed $failed): UserState {
return new UserState([self::IDENTIFIER => $failed->userId, 'failed' => true]);
});
}

private static function describeChangeUsername(EventMachine $eventMachine): void
{
$eventMachine->process(Command::CHANGE_USERNAME)
->withExisting(Aggregate::USER)
// This time we handle command with existing aggregate, hence we get current user state injected
->handle(function (UserState $user, ChangeUsername $changeUsername) {
yield new UsernameChanged([
self::IDENTIFIER => $user->userId,
'oldName' => $user->username,
'newName' => $changeUsername->username,
]);
})
->recordThat(Event::USERNAME_WAS_CHANGED)
// Same here, UsernameChanged is NOT the first event, so current user state is passed
->apply(function (UserState $user, UsernameChanged $event) {
$user->username = $event->newName;

return $user;
});
}

private function __construct()
{
//static class only
}
}
24 changes: 24 additions & 0 deletions examples/FunctionalFlavour/Aggregate/UserState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* This file is part of the proophsoftware/event-machine.
* (c) 2017-2018 prooph software GmbH <contact@prooph.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ProophExample\FunctionalFlavour\Aggregate;

use ProophExample\FunctionalFlavour\Util\ApplyPayload;

class UserState
{
use ApplyPayload;

public $userId;
public $username;
public $email;
public $failed;
}
46 changes: 46 additions & 0 deletions examples/FunctionalFlavour/Api/Command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/**
* This file is part of the proophsoftware/event-machine.
* (c) 2017-2018 prooph software GmbH <contact@prooph.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ProophExample\FunctionalFlavour\Api;

use ProophExample\FunctionalFlavour\Command\ChangeUsername;
use ProophExample\FunctionalFlavour\Command\RegisterUser;

final class Command
{
const REGISTER_USER = 'RegisterUser';
const CHANGE_USERNAME = 'ChangeUsername';
const DO_NOTHING = 'DoNothing';

const CLASS_MAP = [
self::REGISTER_USER => RegisterUser::class,
self::CHANGE_USERNAME => ChangeUsername::class,
];

public static function createFromNameAndPayload(string $commandName, array $payload)
{
$class = self::CLASS_MAP[$commandName];

return new $class($payload);
}

public static function nameOf($command): string
{
$map = \array_flip(self::CLASS_MAP);

return $map[\get_class($command)];
}

private function __construct()
{
//static class only
}
}
48 changes: 48 additions & 0 deletions examples/FunctionalFlavour/Api/Event.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* This file is part of the proophsoftware/event-machine.
* (c) 2017-2018 prooph software GmbH <contact@prooph.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ProophExample\FunctionalFlavour\Api;

use ProophExample\FunctionalFlavour\Event\UsernameChanged;
use ProophExample\FunctionalFlavour\Event\UserRegistered;
use ProophExample\FunctionalFlavour\Event\UserRegistrationFailed;

final class Event
{
const USER_WAS_REGISTERED = 'UserWasRegistered';
const USER_REGISTRATION_FAILED = 'UserRegistrationFailed';
const USERNAME_WAS_CHANGED = 'UsernameWasChanged';

const CLASS_MAP = [
self::USER_WAS_REGISTERED => UserRegistered::class,
self::USER_REGISTRATION_FAILED => UserRegistrationFailed::class,
self::USERNAME_WAS_CHANGED => UsernameChanged::class,
];

public static function createFromNameAndPayload(string $commandName, array $payload)
{
$class = self::CLASS_MAP[$commandName];

return new $class($payload);
}

public static function nameOf($event): string
{
$map = \array_flip(self::CLASS_MAP);

return $map[\get_class($event)];
}

private function __construct()
{
//static class only
}
}
97 changes: 97 additions & 0 deletions examples/FunctionalFlavour/Api/MessageDescription.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* This file is part of the proophsoftware/event-machine.
* (c) 2017-2018 prooph software GmbH <contact@prooph.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ProophExample\FunctionalFlavour\Api;

use Prooph\EventMachine\EventMachine;
use Prooph\EventMachine\EventMachineDescription;
use Prooph\EventMachine\JsonSchema\JsonSchema;
use Prooph\EventMachine\JsonSchema\Type\EmailType;
use Prooph\EventMachine\JsonSchema\Type\StringType;
use Prooph\EventMachine\JsonSchema\Type\UuidType;
use ProophExample\FunctionalFlavour\Resolver\GetUserResolver;
use ProophExample\FunctionalFlavour\Resolver\GetUsersResolver;
use ProophExample\PrototypingFlavour\Aggregate\UserDescription;

/**
* You're free to organize EventMachineDescriptions in the way that best fits your personal preferences
*
* We decided to describe all messages of the bounded context in a centralized MessageDescription.
* Another idea would be to register messages within an aggregate description.
*
* You only need to follow one rule:
* Messages need be registered BEFORE they are referenced by handling or listing descriptions
*
* Class MessageDescription
* @package ProophExample\Messaging
*/
final class MessageDescription implements EventMachineDescription
{
public static function describe(EventMachine $eventMachine): void
{
/* Schema Definitions */
$userId = new UuidType();

$username = (new StringType())->withMinLength(1);

$userDataSchema = JsonSchema::object([
UserDescription::IDENTIFIER => $userId,
UserDescription::USERNAME => $username,
UserDescription::EMAIL => new EmailType(),
], [
//If it is set to true user registration handler will record a UserRegistrationFailed event
'shouldFail' => JsonSchema::boolean(),
]);
$eventMachine->registerCommand(Command::DO_NOTHING, JsonSchema::object([
UserDescription::IDENTIFIER => $userId,
]));

/* Message Registration */
$eventMachine->registerCommand(Command::REGISTER_USER, $userDataSchema);
$eventMachine->registerCommand(Command::CHANGE_USERNAME, JsonSchema::object([
UserDescription::IDENTIFIER => $userId,
UserDescription::USERNAME => $username,
]));

$eventMachine->registerEvent(Event::USER_WAS_REGISTERED, $userDataSchema);
$eventMachine->registerEvent(Event::USERNAME_WAS_CHANGED, JsonSchema::object([
UserDescription::IDENTIFIER => $userId,
'oldName' => $username,
'newName' => $username,
]));

$eventMachine->registerEvent(Event::USER_REGISTRATION_FAILED, JsonSchema::object([
UserDescription::IDENTIFIER => $userId,
]));

//Register user state as a Type so that we can reference it as query return type
$eventMachine->registerType('User', $userDataSchema);
$eventMachine->registerQuery(Query::GET_USER, JsonSchema::object([
UserDescription::IDENTIFIER => $userId,
]))
->resolveWith(GetUserResolver::class)
->setReturnType(JsonSchema::typeRef('User'));

$eventMachine->registerQuery(Query::GET_USERS)
->resolveWith(GetUsersResolver::class)
->setReturnType(JsonSchema::array(JsonSchema::typeRef('User')));

$filterInput = JsonSchema::object([
'username' => JsonSchema::nullOr(JsonSchema::string()),
'email' => JsonSchema::nullOr(JsonSchema::email()),
]);
$eventMachine->registerQuery(Query::GET_FILTERED_USERS, JsonSchema::object([], [
'filter' => $filterInput,
]))
->resolveWith(GetUsersResolver::class)
->setReturnType(JsonSchema::array(JsonSchema::typeRef('User')));
}
}
Loading

0 comments on commit f1d4727

Please sign in to comment.