Skip to content

Instantly share code, notes, and snippets.

@latik
Last active January 26, 2019 09:54
Show Gist options
  • Select an option

  • Save latik/ab647c92a63ebe4664c3ef67b1705237 to your computer and use it in GitHub Desktop.

Select an option

Save latik/ab647c92a63ebe4664c3ef67b1705237 to your computer and use it in GitHub Desktop.
CQRS +- ES notes
<?php
// Aggregate method (domain logic)
// Domain event
// Command
// Command Handler
// Projector
// Listener
// There are 4 places validation takes place in CQRS system.
// Client side(js)
// Server side(controller)
// Application service(CommandHandler)
// Domain Objects(AggrigateRoot)
class UserRegisterRequest
{
public function toArray(): array
{
}
}
class UserController
{
public function register(UserRegisterRequest $request)
{
// do some validation here
// use value object for do validation(required field, valid email, etc.)
$params = $request->toArray();
$this->commandBus->dispatch(UserRegisterCommand::fromArray($params));
// The DTO containing the intent and data to execute the command.
}
}
class UserRegisterCommand
{
public function fromArray(array $params): self
{
// store $params as context
}
}
//---------------------------
// CommandBus
//---------------------------
/** A command handler will be registered with the command bus and handle the commands that are dispatched.
* The command handler can be seen as a small layer between your application code and the actual domain code.
*
* In the end a command handler listens for commands and translates commands to
* method calls on the actual aggregate roots.
**/
class UserRegisterCommandHandler
{
public function handle(UserRegisterCommand $command): void // UserRegisterCommandHandler is CommandHandler
{
try {
// do some validation here
$user = User::register($command); // <-- call AggregateRoot method
$this->userRepository->save($user); // Skip this step in ES case.
// Instead need use projectors for ReadModel change
$this->eventBus->dispatch($user->realeseEvents());
//$this->eventBus->dispatch($user->getRecordedEvents());
} catch (\DomainException $exception) {
$this->eventBus->dispatch(new UserUnableRegisterEvent($user, $exception));
}
}
}
class User
{
public function register(UserRegisterCommand $command)
{
// do some domain validation here
$user = new static();
$user->email = $command->email;
///..
$user->raise(new UserRegisteredEvent($user)); // UserWasCreatedEvent is DomainEvent
//$user->recordThat(new UserRegisteredEvent($user));
return $user;
}
}
//---------------------------
// EventBus //All events MUST be stored into EventStorage (use transactions, it's most critical part of the ES app)
//---------------------------
// UpdateExistedUsersFromNewRegistration is one of many others projectors for event UserWasCreatedEvent
// it store user data into DB for use in ReadModels at future
//UserProjector@onRegistered(UserRegisteredEvent $event): void OR
class UpdateExistedUsersListFromNewRegistration
{
public function handle(UserRegisteredEvent $event): void
{
$this->dbConnection->insert('users', [
'id' => $event->user->id,
// ....
]);
}
}
// UserWasCreatedEmail is one of many others listeners for event UserWasCreatedEvent
// It's describe feature, example: send email to new user
// Also it may fire new Command!!
class SendWelcomeMailWhenUserSigned
{
public function handle(UserRegisteredEvent $event): void
{
$this->mailer->send(
$event->user->email
);
}
}
//https://pilsniak.com/cqrs-es-php-prooph/
//https://github.com/nepda/iwsf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment