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.

7bb3a66a199daec275d5ad339724c754?s=128

Junior Grossi

December 05, 2020
Tweet

More Decks by Junior Grossi

Other Decks in Programming

Transcript

  1. CQRS & EVENT SOURCING arquitetura de software em alto nível,

    para todos os níveis!
  2. tô ficando véi! brinde durante a ao final da palestra!

    pegue sua cerveja, suco, refri, água ou toddy e vamos brindar!
  3. eaê, eu sou Junior Grossi twitter.com/junior_grossi github.com/jgrossi grossi.dev

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

  6. PHPMG Conference 2019 21/09/2019 - Belo Horizonte, Brazil https://conf.phpmg.com

  7. ahhhh os #elePHPants https://elephpant.me/herd/junior_grossi

  8. remote-first company php / golang / java / js https://glofox.com/careers

  9. / 2004 php / 2016 / 2019 ✈

  10. então... bora começar!

  11. CQRS & EVENT SOURCING não são um padrão de arquitetura

    de software são design patterns independentes
  12. 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)
  13. CQRS COMMAND QUERY RESPONSIBILITY SEGREGATION (quebrar a responsabilidade de "leitura"

    e "escrita") Command: operações de escrita Query: operações de leitura
  14. TÁ, MAS PQ? escalabilidade / performance (vamos assumir que todo

    dado já é obsoleto)
  15. 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); } }
  16. AVISO AOS NAVEGANTES! estamos usando um ORM! (ele é usado

    antes e depois das operações de DB)
  17. E COMO TÁ NOSSO DB?

  18. None
  19. CQRS é baseado em eventos comunicação assíncrona / filas (usar

    o melhor de cada "storage")
  20. None
  21. WRITING READING

  22. CONCEITO IMPORTANTE Command Handler (geralmente utilizado para operações de escrita)

  23. 1 Command para 1 Handler RegisterUserCommand + RegisterUserHandler (command pra

    dados + handler pra lógica)
  24. 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); } }
  25. COMMAND: PROS DADOS f nal class RegisterUserCommand { public function

    construct( private UserId $id, private string $name, private string $email ) {} getId(), getName(), getEmail() }
  26. 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() ) ); } }
  27. 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
  28. eita porra gente, perai! como assim? (você não precisa dele,

    na verdade! não agora!)
  29. BASEADO EM EVENTO posso mandar um "command" pra ser executado

    pela fila CommandBus (retorno rápido para o usuário)
  30. 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); } }
  31. 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)
  32. None
  33. E A LEITURA? query - CQRS

  34. 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; } }
  35. 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é?)
  36. ONDE APLICAR ISSO? microservices (comunicação assíncrona) casos de uso bem

    lentos (+ performance)
  37. EVENT SOURCING "TUDO É UM EVENTO" (mudanças de estado são

    consideradas eventos)
  38. CASO DE USO ChangeEmailHandler (alterar o endereço de e-mail do

    usuário)
  39. id name email 1 Junior Grossi junior@grossi.dev $handler handle( new

    ChangeEmailCommand( userId: new UserId(1), email: 'jgrossi@phpconference.com.br' ) );
  40. QUAL O RESULTADO ESPERADO? id name email 1 Junior Grossi

    jgrossi@phpconference.com.br
  41. 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é?
  42. isso aqui é Event Sourcing! [ { event: "UserWasRegistered", createdAt:

    "2020-10-19 07 33 18", payload: { id: 1, name: "Junior Grossi", email: "junior@grossi.dev" } }, { event: "UserEmailWasUpdated", createdAt: "2020-10-23 07 33 18", payload: { id: 1, newEmail: "jgrossi@phpconference.com.br" } } ]
  43. 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)
  44. todos os eventos são escritos no passado: OrderPaid (registro passo-a-passo,

    incluindo os dados)
  45. 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)
  46. event sourcing é parte do seu domínio! ⚠ você não

    deve usar um framework
  47. REFERÊNCIA github.com/prooph/common github.com/prooph/event sourcing (deprecated) ⛔ use somente como referência!

    seu domínio é só seu!
  48. DEFINIÇÃO IMPORTANTE ✔ o que são Aggregates? (usados para representar

    "o que aconteceu")
  49. 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)
  50. 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"
  51. 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, ]) ); } }
  52. mais informação sobre como eles funcionam http://docs.getprooph.org/tutorial/event_sourcing_basics.html

  53. RESUMÃO RÁPIDO

  54. CQRS responsabilidades diferentes entre leitura e escrita Command + Handler

    = Event CommandBus => Queue (sincroniza o DB baseado nos eventos)
  55. 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)
  56. CONSIDERAÇÕES FINAIS não use CQRS / Event Sourcing no projeto

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

    Sourcing possui uma curva de aprendizado maior)
  58. OBRIGADO! PALESTRA JÁ DISPONÍVEL ⭐ http://speakerdeck.com/jgrossi http://twitter.com/junior_grossi