Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CQRS and Event Sourcing

CQRS and Event Sourcing

Jefersson Nathan (@malukenho)'s talk on #PHPeste about CQRS and Event Sourcing

Jefersson Nathan

October 09, 2016
Tweet

More Decks by Jefersson Nathan

Other Decks in Programming

Transcript

  1. <?php 
 declare(strict_types=1);
 
 final class UserService
 {
 public function

    enable(Uuid $userId) : void;
 public function disable(Uuid $userId) : void;
 public function fetchAll() : UserCollection;
 } Customer Service
  2. <?php declare(strict_types=1);
 
 final class UserWrite
 {
 public function enable(Uuid

    $userId) : void;
 public function disable(Uuid $userId) : void;
 } <?php declare(strict_types=1);
 
 final class UserRead
 {
 public function fetchAll(Uuid $userId) : UserCollection;
 }
  3. Register User Command <?php
 
 declare(strict_types=1);
 
 final class RegisterUser


    {
 private $username;
 
 private function __construct(string $username)
 {
 $this->username = $username;
 }
 
 public static function fromUserName(string $username) : self
 {
 return new self($username);
 }
 
 public function username() : string
 {
 return $this->username;
 }
 }
  4. Simple command bus <?php
 
 declare(strict_types=1);
 
 $commandBus = static

    function ($command) use ($container) : void {
 $commandClassName = get_class($command);
 
 if (! $container->has($commandClassName)) {
 throw CommandBusException::cannotResolveToCommandHandler($command);
 }
 
 $container->get($commandClassName)($command);
 };

  5. <?php
 
 declare(strict_types=1);
 
 final class RegisterUserHandler
 {
 /**
 *

    @var UserRepositoryInterface
 */
 private $users;
 
 public function __construct(UserRepositoryInterface $users)
 {
 $this->users = $users;
 }
 
 /**
 * @param RegisterUser $registerUser
 *
 * @return void
 */
 public function __invoke(RegisterUser $registerUser) : void
 {
 $username = $registerUser->username();
 
 $this->users->store(User::registerNew($username, UserId::newUserId()));
 }
 }
  6. <?php
 
 declare(strict_types=1);
 
 $app = new \Zend\Expressive\Application();
 
 $app->post(


    
 '/register-user',
 
 function ($request, $response) use ($commandBus) : Response {
 
 $commandBus(RegisterUser::fromUserName(
 
 UserName::fromString($request->getQueryParams()['username'])
 
 ));
 
 
 return $response->withAddedHeader('Location', '/home');
 }
 );
 
 $app->run();
 Middleware
  7. NAMED CONSTRUCTORS <?php
 
 declare(strict_types=1);
 
 final class Email
 {


    private $email;
 
 private function __construct(string $email)
 { Assert::isValidEmail($email); 
 $this->email = $email;
 }
 
 public static function fromString(string $email) : self
 {
 return new self($email);
 }
 }
  8. NAMED CONSTRUCTORS <?php
 
 declare(strict_types=1);
 
 final class RegisterUser
 {


    private $username;
 
 private function __construct(string $username)
 {
 $this->username = $username;
 }
 
 public static function fromString(string $username) : self
 {
 return new self($username);
 } public static function fromUser(User $user) : self
 {
 return new self($user->getUsername());
 }
 }
  9. UUID EVENT PAYLOAD AGGREGATE_ID 1 UserWasCreated {name: “John”, …} 1

    2 UserWasActivated {enabled: true} 2 3 UserWasFollowed {name: “Maria”} 1 4 UserWasExcludedBy Administrator {name: “Batman”} 5
  10. Domain Events <?php
 
 declare(strict_types=1);
 
 final class UserWasCreated
 {


    private $userName;
 
 private function __construct(UserName $userName)
 {
 $this->userName = $userName;
 }
 
 public static function fromUserName(UserName $userName) : self
 {
 return new self($userName);
 }
 }
  11. UserName Email USER AGGREGATE CPF Aggregates are Entities that hold

    other Entities and Value Objects that help keep data consistent. Gender Birthday
  12. Disparando Eventos <?php
 
 declare(strict_types=1);
 
 class User
 {
 public

    function enable() : void
 {
 $this->recordThat(UserWasEnabled::fromUuid($this->uuid));
 }
 }

  13. Changing Aggregate State <?php
 
 declare(strict_types=1);
 
 class User
 {


    public function whenUserWasEnable(UserWasEnabled $event) : void
 {
 $this->enabled = true;
 }
 }

  14. Projector Example <?php
 
 declare(strict_types=1);
 
 final class UserWasEnabledProjector
 {


    private $connection;
 
 public function __construct(Connection $connection)
 {
 $this->connection = $connection;
 }
 
 public function __invoke(UserWasEnabled $event) : void
 {
 $this->connection->update(
 'users',
 ['enabled' => true],
 ['user_id' => (string) $event->getUserUuid()]
 );
 }
 }

  15. Event Listeners Example <?php
 
 declare(strict_types=1);
 
 final class UserWasEnabledListener


    {
 private $mailService;
 
 public function __construct(MailService $mailService)
 {
 $this->mailService = $mailService;
 }
 
 public function __invoke(UserWasEnabled $event) : void
 {
 $this->mailService->sendEnabledMail(
 $event->userMail()
 );
 }
 }
  16. <?php
 
 declare(strict_types=1);
 
 $app = new \Zend\Expressive\Application();
 
 $app->post(


    
 '/register-user',
 
 function ($request, $response) use ($commandBus) : Response {
 
 $commandBus(RegisterUser::fromUserName(
 
 UserName::fromString($request->getQueryParams()['username'])
 
 ));
 
 
 return $response->withAddedHeader('Location', '/home');
 }
 );
 
 $app->run();

  17. CREATE TABLE event_stream
 (
 event_id VARCHAR(36) PRIMARY KEY NOT NULL,


    version INTEGER NOT NULL,
 event_name VARCHAR(100) NOT NULL,
 payload TEXT NOT NULL,
 created_at VARCHAR(50) NOT NULL,
 aggregate_id VARCHAR(36) NOT NULL,
 aggregate_type VARCHAR(100) NOT NULL,
 causation_id VARCHAR(36) NOT NULL,
 causation_name VARCHAR(100) NOT NULL
 );