Slide 1

Slide 1 text

CQRS & EVENT SOURCING arquitetura de software em alto nível, para todos os níveis!

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

https://github.com/corcel/corcel

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

/ 2004 php / 2016 / 2019 ✈

Slide 10

Slide 10 text

então... bora começar!

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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)

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

E COMO TÁ NOSSO DB?

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

WRITING READING

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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)

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

E A LEITURA? query - CQRS

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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)

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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)

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

DEFINIÇÃO IMPORTANTE ✔ o que são Aggregates? (usados para representar "o que aconteceu")

Slide 49

Slide 49 text

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)

Slide 50

Slide 50 text

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"

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

RESUMÃO RÁPIDO

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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)

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

OBRIGADO! PALESTRA JÁ DISPONÍVEL ⭐ http://speakerdeck.com/jgrossi http://twitter.com/junior_grossi