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. CQRS & EVENT SOURCING
    arquitetura de software em alto nível,
    para todos os níveis!

    View Slide

  2. tô ficando véi!
    brinde durante a ao final da palestra!
    pegue sua cerveja, suco, refri, água ou toddy e vamos brindar!

    View Slide

  3. eaê, eu sou Junior Grossi
    twitter.com/junior_grossi
    github.com/jgrossi
    grossi.dev

    View Slide

  4. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  9. / 2004 php
    / 2016 / 2019

    View Slide

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

    View Slide

  11. CQRS & EVENT SOURCING
    não são um padrão de arquitetura de software
    são design patterns independentes

    View Slide

  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)

    View Slide

  13. CQRS
    COMMAND QUERY RESPONSIBILITY SEGREGATION
    (quebrar a responsabilidade de "leitura" e "escrita")
    Command: operações de escrita
    Query: operações de leitura

    View Slide

  14. TÁ, MAS PQ?
    escalabilidade / performance
    (vamos assumir que todo dado já é obsoleto)

    View Slide

  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);
    }
    }

    View Slide

  16. AVISO AOS NAVEGANTES!
    estamos usando um ORM!
    (ele é usado antes e depois das operações de DB)

    View Slide

  17. E COMO TÁ NOSSO DB?

    View Slide

  18. View Slide

  19. CQRS é baseado em eventos
    comunicação assíncrona / filas
    (usar o melhor de cada "storage")

    View Slide

  20. View Slide

  21. WRITING
    READING

    View Slide

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

    View Slide

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

    View Slide

  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);
    }
    }

    View Slide

  25. COMMAND: PROS DADOS
    f nal class RegisterUserCommand
    {
    public function construct(
    private UserId $id,
    private string $name,
    private string $email
    ) {}
    getId(), getName(), getEmail()
    }

    View Slide

  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()
    )
    );
    }
    }

    View Slide

  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

    View Slide

  28. eita porra gente, perai! como assim?
    (você não precisa dele, na verdade! não agora!)

    View Slide

  29. BASEADO EM EVENTO
    posso mandar um "command" pra ser executado pela fila
    CommandBus
    (retorno rápido para o usuário)

    View Slide

  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);
    }
    }

    View Slide

  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)

    View Slide

  32. View Slide

  33. E A LEITURA?
    query - CQRS

    View Slide

  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;
    }
    }

    View Slide

  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é?)

    View Slide

  36. ONDE APLICAR ISSO?
    microservices (comunicação assíncrona)
    casos de uso bem lentos (+ performance)

    View Slide

  37. EVENT SOURCING
    "TUDO É UM EVENTO"
    (mudanças de estado são consideradas eventos)

    View Slide

  38. CASO DE USO
    ChangeEmailHandler
    (alterar o endereço de e-mail do usuário)

    View Slide

  39. id name email
    1 Junior Grossi [email protected]
    $handler handle(
    new ChangeEmailCommand(
    userId: new UserId(1),
    email: '[email protected]'
    )
    );

    View Slide

  40. QUAL O RESULTADO ESPERADO?
    id name email
    1 Junior Grossi [email protected]

    View Slide

  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é?

    View Slide

  42. 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]"
    }
    }
    ]

    View Slide

  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)

    View Slide

  44. todos os eventos são escritos no passado:
    OrderPaid
    (registro passo-a-passo, incluindo os dados)

    View Slide

  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)

    View Slide

  46. event sourcing é parte do seu domínio!
    ⚠ você não deve usar um framework

    View Slide

  47. REFERÊNCIA
    github.com/prooph/common
    github.com/prooph/event sourcing (deprecated)
    ⛔ use somente como referência! seu domínio é só seu!

    View Slide

  48. DEFINIÇÃO IMPORTANTE

    o que são Aggregates?
    (usados para representar "o que aconteceu")

    View Slide

  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)

    View Slide

  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"

    View Slide

  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,
    ])
    );
    }
    }

    View Slide

  52. mais informação sobre como eles funcionam
    http://docs.getprooph.org/tutorial/event_sourcing_basics.html

    View Slide

  53. RESUMÃO RÁPIDO

    View Slide

  54. CQRS
    responsabilidades diferentes entre leitura e escrita
    Command + Handler = Event
    CommandBus => Queue
    (sincroniza o DB baseado nos eventos)

    View Slide

  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)

    View Slide

  56. CONSIDERAÇÕES FINAIS
    não use CQRS / Event Sourcing no projeto todo
    (somente onde você realmente precisa)

    View Slide

  57. COMO COMEÇAR?
    use CQRS primeiro!
    padrão Command + Handler
    (Event Sourcing possui uma curva de aprendizado maior)

    View Slide

  58. OBRIGADO!
    PALESTRA JÁ DISPONÍVEL

    http://speakerdeck.com/jgrossi
    http://twitter.com/junior_grossi

    View Slide