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.

7bb3a66a199daec275d5ad339724c754?s=128

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!
  2. Sou Junior Grossi twitter.com/junior_grossi github.com/jgrossi

  3. Paciência!

  4. https://github.com/corcel/corcel

  5. None
  6. None
  7. https://glofox.com/careers

  8. CQRS & EVENT SOURCING Não é um padrão de Arquitetura

    de So ware É um padrão de código independente
  9. 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)
  10. CQRS COMMAND QUERY RESPONSIBILITY SEGREGATION (separar responsabilidade de leitura e

    escrita)
  11. MAS PRA QUÊ? Escalabilidade / Performance (toda informação exibida já

    pode estar obsoleta)
  12. 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); } }
  13. AVISO! Estamos usando um ORM! (ida e vinda passam por

    ele)
  14. E O BANCO DE DADOS?

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

  16. CQRS é baseado em eventos (usar o que cada "storage"

    faz de melhor)
  17. None
  18. ESCRITA LEITURA

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

  20. 1 Command para 1 Handler RegisterUserCommand + RegisterUserHandler (command para

    dados + handler para lógica)
  21. 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); } }
  22. 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() }
  23. 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() ) ); } }
  24. 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
  25. WTF? Como não vou retornar o usuário? (você não precisa,

    acredite!)
  26. BASEADO EM EVENTOS Posso jogar um Handler na fila, por

    exemplo! CommandBus (retorno bem rápido pro usuário)
  27. MAS E SE FALHAR? A fila vai processar quando der

    novamente (pode parar o banco e a fila irá ficar esperando)
  28. Actor RegisterUserHandler Queue ID Data Processing

  29. E A LEITURA? (QUERY?)

  30. 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; } }
  31. Connection? Não precisamos de Repository ou ORM! Dados puros (array)

    são suficientes para o retorno (JSON)
  32. EVENT SOURCING TUDO É EVENTO (mudanças de estado são registradas

    como eventos)
  33. CASO DE USO ChangeEmailHandler (altera o email do usuário)

  34. id name email 1 Junior Grossi junior@grossi.dev $handler->handle( new ChangeEmailCommand(

    'userId' => 1, 'newEmail' => 'junior@darkmira.tour', ) );
  35. id name email 1 Junior Grossi junior@darkmira.tour

  36. PROBLEMA? Qual a mudança foi feita? O que causou a

    mudança? (não temos estes registros, infelizmente!)
  37. Isto é Event Sourcing! ❤ [ { event: "UserWasRegistered", createdAt:

    "2019-06-08", payload: {id: 1, name: "Junior Grossi", email: "junior@grossi.dev"} }, { event: "EmailWasChanged", createdAt: "2019-06-09", payload: {id: 1, newEmail: "junior@darkmira.tour"} } ]
  38. 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)
  39. Todo evento é descrito no passado: OrderPaid (registro do passo

    a passo e dos dados dos eventos)
  40. 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)
  41. composer require prooph/common prooph/event-sourcing https://github.com/prooph

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

  43. 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
  44. 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"
  45. 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. } }
  46. Mais informações sobre Aggregates http://docs.getprooph.org/tutorial/event_sourcing_basics.html

  47. RESUMÃO

  48. CQRS Separar leitura de escrita Command + Handler = Event

    (sincronizar os bancos com base neste evento)
  49. 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)
  50. CONSIDERAÇÕES Não use CQRS + Event Sourcing no projeto todo

    (apenas onde você realmente precisa)
  51. COMO COMEÇAR? Aplique CQRS primeiro! Command + Handler pattern (Event

    Sourcing possui uma curva de aprendizado maior)
  52. OBRIGADO! AVALIE ESTA PALESTRA NO JOIND.IN ⭐ https://joind.in/talk/6352e http://twitter.com/junior_grossi