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

CQRS e Event Sourcing: Arquitetura de Software em alto nível, para todos os níveis! 🇧🇷

CQRS e Event Sourcing: Arquitetura de Software em alto nível, para todos os níveis! 🇧🇷

CQRS (Command Query Responsibility Segregation) e Event Sourcing são assuntos que você já deve ter ouvido por aí. É uma nova forma de pensar em aplicações web, visando performance e visão do domínio do projeto como um todo. O objetivo dessa palestra é dar uma visão geral sobre os temas, com alguns exemplos práticos em PHP de como as coisas iriam funcionar.

Junior Grossi

June 09, 2019
Tweet

More Decks by Junior Grossi

Other Decks in Programming

Transcript

  1. CQRS & EVENT SOURCING
    Arquitetura em alto nível, para todos os níveis!

    View full-size slide

  2. Sou Junior Grossi
    twitter.com/junior_grossi
    github.com/jgrossi

    View full-size slide

  3. https://github.com/corcel/corcel

    View full-size slide

  4. https://glofox.com/careers

    View full-size slide

  5. CQRS & EVENT SOURCING
    Não é um padrão de Arquitetura de So ware
    É um padrão de código independente

    View full-size slide

  6. ONDE APLICAR?
    Não precisa mudar sua arquitetura
    Pode ser aplicado à um (ou mais) casos de uso
    (inclusive naquele código legado bizarro que você trabalha)

    View full-size slide

  7. CQRS
    COMMAND QUERY RESPONSIBILITY SEGREGATION
    (separar responsabilidade de leitura e escrita)

    View full-size slide

  8. MAS PRA QUÊ?
    Escalabilidade / Performance
    (toda informação exibida já pode estar obsoleta)

    View full-size slide

  9. class UserController
    {
    public function create(Request $request): ResponseInterface
    {
    $user = User::create($request->all());
    return new JsonResponse($user, 201);
    }
    public function show(Request $request): ResponseInterface
    {
    $user = User::find($request->get('id'));
    return new JsonResponse($user, 200);
    }
    }

    View full-size slide

  10. AVISO!
    Estamos usando um ORM!
    (ida e vinda passam por ele)

    View full-size slide

  11. E O BANCO DE DADOS?

    View full-size slide

  12. Actor
    RegisterUser
    ORM
    DB
    ORM
    User::create()
    JSON HTTP
    User instance

    View full-size slide

  13. CQRS é baseado em eventos
    (usar o que cada "storage" faz de melhor)

    View full-size slide

  14. ESCRITA
    LEITURA

    View full-size slide

  15. CONCEITO IMPORTANTE
    Command Handler
    (usado para a parte de escrita)

    View full-size slide

  16. 1 Command para 1 Handler
    RegisterUserCommand + RegisterUserHandler
    (command para dados + handler para lógica)

    View full-size slide

  17. POST /users
    final class RegisterUserAction
    {
    private RegisterUserHandler $handler;
    public function __construct(RegisterUserHandler $handler)
    {
    $this->handler = $handler;
    }
    public function __invoke(RequestInterface $request): ResponseInterface
    {
    $userId = \Ramsey\Uuid\Uuid::uuid4();
    $this->handler->handle(
    new RegisterUserCommand(
    $userId,
    $request->getAttribute('name'),
    $request->getAttribute('email')
    )
    )
    return new JsonResponse(['id' => $userId], 201);
    }
    }

    View full-size slide

  18. COMMAND: PARA OS DADOS
    final class RegisterUserCommand
    {
    private string $id;
    private string $name;
    private string $email;
    public function __construct(
    string $id,
    string $name,
    string $email
    ) {
    $this->id = $id;
    $this->name = $name;
    $this->email = $email;
    }
    // getId(), getName(), getEmail()
    }

    View full-size slide

  19. HANDLER: PARA A LÓGICA
    final class RegisterUserHandler
    {
    private UserRepositoryInterface $userRepository;
    public function __construct(UserRepositoryInterface $userRepository)
    {
    $this->userRepository = $userRepository;
    }
    public function handle(RegisterUserCommand $command): void
    {
    $this->userRepository->persist(
    new User(
    $command->getId(),
    $command->getName(),
    $command->getEmail()
    )
    );
    }
    }

    View full-size slide

  20. EVENTOS NÃO TEM RETORNO
    (vou criar o usuário mas não vou retornar ele)
    CQRS: operações de escrita não possuem retorno

    View full-size slide

  21. WTF? Como não vou retornar o usuário?
    (você não precisa, acredite!)

    View full-size slide

  22. BASEADO EM EVENTOS
    Posso jogar um Handler na fila, por exemplo!
    CommandBus
    (retorno bem rápido pro usuário)

    View full-size slide

  23. MAS E SE FALHAR?
    A fila vai processar quando der novamente
    (pode parar o banco e a fila irá ficar esperando)

    View full-size slide

  24. Actor
    RegisterUserHandler
    Queue
    ID Data
    Processing

    View full-size slide

  25. E A LEITURA? (QUERY?)

    View full-size slide

  26. final class UserFinder
    {
    private Connection $connection;
    public function __construct(Connection $connection)
    {
    $this->connection = $connection;
    }
    public function find(int $id): array
    {
    $userData = $this->connection->findOne(['id' => $id]);
    if (!$userData) {
    throw UserNotFoundException::withId($id);
    }
    return $userData;
    }
    }

    View full-size slide

  27. Connection?
    Não precisamos de Repository ou ORM!
    Dados puros (array) são suficientes para o retorno (JSON)

    View full-size slide

  28. EVENT SOURCING
    TUDO É EVENTO
    (mudanças de estado são registradas como eventos)

    View full-size slide

  29. CASO DE USO
    ChangeEmailHandler
    (altera o email do usuário)

    View full-size slide

  30. id name email
    1 Junior Grossi [email protected]
    $handler->handle(
    new ChangeEmailCommand(
    'userId' => 1,
    'newEmail' => '[email protected]',
    )
    );

    View full-size slide

  31. id name email
    1 Junior Grossi [email protected]

    View full-size slide

  32. PROBLEMA?
    Qual a mudança foi feita? O que causou a mudança?
    (não temos estes registros, infelizmente!)

    View full-size slide

  33. Isto é Event Sourcing!

    [
    {
    event: "UserWasRegistered",
    createdAt: "2019-06-08",
    payload: {id: 1, name: "Junior Grossi", email: "[email protected]"}
    },
    {
    event: "EmailWasChanged",
    createdAt: "2019-06-09",
    payload: {id: 1, newEmail: "[email protected]"}
    }
    ]

    View full-size slide

  34. POR QUE EVENT SOURCING?
    Controle das mudanças de estado
    Reescrever a história quando algo der errado
    (tomada de decisão como estratégia)

    View full-size slide

  35. Todo evento é descrito no passado: OrderPaid
    (registro do passo a passo e dos dados dos eventos)

    View full-size slide

  36. CASOS DE USO (EXEMPLOS)
    BUGFIXES: achar o ponto exato que houve o problema
    (e ver qual foi a mudança que causou o bug)
    Reescrever história do Banco de Dados
    (literalmente voltar no tempo para resolver algum problema)

    View full-size slide

  37. composer require
    prooph/common
    prooph/event-sourcing
    https://github.com/prooph

    View full-size slide

  38. CONCEITO IMPORTANTE
    Aggregates
    (usado para a parte de escrita)

    View full-size slide

  39. AGGREGATES
    Representam estado de consistência
    Garantem que eu possa mudar de um estado para outro
    (validação a nível de regras de negócio)
    ShoppingCartAggregate

    View full-size slide

  40. REGRAS DE NEGÓCIO
    "Um carrinho começa sempre vazio"
    "Um produto só pode ser adicionado ao carrinho se
    houver estoque"
    "A quantidade de um produto no carrinho não pode ser
    maior do que o estoque"

    View full-size slide

  41. use Prooph\EventSourcing\AggregateRoot;
    final class ShoppingCartAggregate extends AggregateRoot
    {
    public static function startShoppingSession(
    ShoppingSession $shoppingSession,
    BasketId $basketId)
    {
    $self = new self();
    $self->recordThat(ShoppingSessionStarted::occur($basketId->toString(), [
    'shopping_session' => $shoppingSession->toString()
    ]));
    return $self;
    }
    protected function aggregateId(): string
    {
    // TODO: Implement aggregateId() method.
    }
    protected function apply(AggregateChanged $event): void
    {
    // TODO: Implement apply() method.
    }
    }

    View full-size slide

  42. Mais informações sobre Aggregates
    http://docs.getprooph.org/tutorial/event_sourcing_basics.html

    View full-size slide

  43. CQRS
    Separar leitura de escrita
    Command + Handler = Event
    (sincronizar os bancos com base neste evento)

    View full-size slide

  44. EVENT SOURCING
    Registrar cada mudança de estado (usando eventos)
    Events + Aggregates
    (registrar o evento no banco com o payload)
    (histórico de todas mudança de estado)

    View full-size slide

  45. CONSIDERAÇÕES
    Não use CQRS + Event Sourcing no projeto todo
    (apenas onde você realmente precisa)

    View full-size slide

  46. COMO COMEÇAR?
    Aplique CQRS primeiro!
    Command + Handler pattern
    (Event Sourcing possui uma curva de aprendizado maior)

    View full-size slide

  47. OBRIGADO!
    AVALIE ESTA PALESTRA NO JOIND.IN

    https://joind.in/talk/6352e
    http://twitter.com/junior_grossi

    View full-size slide