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!

Evento: PHP Conference Brasil 2020
URL: https://phpconference.com.br/

CQRS (Command Query Responsibility Segregation) e Event Sourcing são assuntos que você já deve ter ouvido por ai. É 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

December 05, 2020
Tweet

More Decks by Junior Grossi

Other Decks in Programming

Transcript

  1. tô ficando véi! brinde durante a ao final da palestra!

    pegue sua cerveja, suco, refri, água ou toddy e vamos brindar!
  2. CQRS & EVENT SOURCING não são um padrão de arquitetura

    de software são design patterns independentes
  3. ONDE ELES SE ENCAIXAM? você não precisa mudar sua arquitetura

    atual podem ser aplicados em um ou mais casos de uso (incluindo aquele código legado bizarro que vc trabalha)
  4. CQRS COMMAND QUERY RESPONSIBILITY SEGREGATION (quebrar a responsabilidade de "leitura"

    e "escrita") Command: operações de escrita Query: operações de leitura
  5. 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 f nd($request get('id')); return new JsonResponse($user, 200); } }
  6. AVISO AOS NAVEGANTES! estamos usando um ORM! (ele é usado

    antes e depois das operações de DB)
  7. POST /v1/users f nal class RegisterUserController { public function construct(

    private RegisterUserHandler $handler ) {} public function invoke( RequestInterface $request ): ResponseInterface { $userId = Uuid uuid4(); $this handler handle( new RegisterUserCommand( new UserId($userId), $request getParsedBody()['name'], $request getParsedBody()['email'] ) ); return new JsonResponse(['id' $userId], 201); } }
  8. COMMAND: PROS DADOS f nal class RegisterUserCommand { public function

    construct( private UserId $id, private string $name, private string $email ) {} getId(), getName(), getEmail() }
  9. HANDLER: PRA LÓGICA f nal class RegisterUserHandler { public function

    construct( private UserRepositoryInterface $userRepository ) {} public function handle(RegisterUserCommand $command): void { $this userRepository add( new User( $command getId(), $command getName(), $command getEmail() ) ); } }
  10. lembre-se: CQRS é baseado em eventos EVENTOS NÃO RETORNAM DADOS

    (vou criar um usuário mas não vou retorná-lo) CQRS: operações de escrita não retornam
  11. BASEADO EM EVENTO posso mandar um "command" pra ser executado

    pela fila CommandBus (retorno rápido para o usuário)
  12. POST /v1/users f nal class RegisterUserController { public function construct(

    private CommandBus $commandBus ) {} public function invoke( RequestInterface $request ): ResponseInterface { $userId = Uuid uuid4(); $this commandBus dispatch( new RegisterUserCommand( new UserId($userId), $request getParsedBody()['name'], $request getParsedBody()['email'] ) ); return new JsonResponse(['id' $userId], 201); } }
  13. E QUANDO FALHAR A BAGAÇA? a fila irá processar a

    mensagem novamente quando possível (o DB pode até cair, mas a fila será processada)
  14. f nal class UserFinder { public function construct( private Connection

    $connection ) {} public function f nd(UserId $id): array { $userData = $this connection f ndOne(['id' (string)$id]); if (!$userData) { throw UserNotFoundException withId($id); } return $userData; } }
  15. mas pq Connection? a gente não precisa de um Repository

    ou ORM! somente dados puros (array) são suficientes (pq no final a gnt retorna é JSON msm né?)
  16. id name email 1 Junior Grossi [email protected] $handler handle( new

    ChangeEmailCommand( userId: new UserId(1), email: '[email protected]' ) );
  17. QUAL O PROBLEMA AQUI? qual foi a mudança? pq mudou?

    (nós não sabemos, infelizmente!) talvez se checar os logs, quem sabe né?
  18. isso aqui é Event Sourcing! [ { event: "UserWasRegistered", createdAt:

    "2020-10-19 07 33 18", payload: { id: 1, name: "Junior Grossi", email: "[email protected]" } }, { event: "UserEmailWasUpdated", createdAt: "2020-10-23 07 33 18", payload: { id: 1, newEmail: "[email protected]" } } ]
  19. PQ USAR EVENT SOURCING? controlar mudanças de estado reescrever a

    história quando der merda algo der errado decisão estratégica (comportamento do usuário)
  20. CASOS DE USO (EXEMPLOS) bugfixes: encontrar o ponto exato da

    falha (ver qual a mudança causou o bug) microservices: centralizar as mudanças de estado entre serviços reescrever o histórico do DB (literalmente voltar no tempo para arrumar algo)
  21. AGGREGATES representam a consistência de um estado garantem que eu

    posso mudar de um estado para o outro (regras de validação a nível de negócio)
  22. REGRAS DE NEGÓCIO "um carrinho de compras sempre começa vazio"

    "um produto somente pode ser adicionado se disponível" "número de itens não pode ser maior que o estoque do produto"
  23. f nal class ShoppingCart extends AggregateRoot { public function construct(

    private ShoppingSession $shoppingSession, private BasketId $basketId, private array $products = [] ) { $this record( new ShoppingSessionStarted($basketId, [ 'shoppingSession' (string)$shoppingSession, ]) ); } public function addProduct( ProductId $productId, int $quantity = 1 ): void { $this record( new ProductAdded($basketId, [ 'productId' (string)$productId, 'quantity' $quantity, ]) ); } }
  24. CQRS responsabilidades diferentes entre leitura e escrita Command + Handler

    = Event CommandBus => Queue (sincroniza o DB baseado nos eventos)
  25. EVENT SOURCING registra cada mudança de estado (usando eventos) Events

    + Aggregates (registra o evento com um "payload" - quando/o quê) (histórico de todas as mudanças de estado)
  26. CONSIDERAÇÕES FINAIS não use CQRS / Event Sourcing no projeto

    todo (somente onde você realmente precisa)
  27. COMO COMEÇAR? use CQRS primeiro! padrão Command + Handler (Event

    Sourcing possui uma curva de aprendizado maior)