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.

Junior Grossi

June 09, 2019
Tweet

More Decks by Junior Grossi

Other Decks in Programming

Transcript

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

    de So ware É um padrão de código independente
  2. 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)
  3. 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); } }
  4. 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); } }
  5. 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() }
  6. 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() ) ); } }
  7. 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
  8. BASEADO EM EVENTOS Posso jogar um Handler na fila, por

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

    novamente (pode parar o banco e a fila irá ficar esperando)
  10. 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; } }
  11. PROBLEMA? Qual a mudança foi feita? O que causou a

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

    "2019-06-08", payload: {id: 1, name: "Junior Grossi", email: "[email protected]"} }, { event: "EmailWasChanged", createdAt: "2019-06-09", payload: {id: 1, newEmail: "[email protected]"} } ]
  13. 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)
  14. 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)
  15. 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
  16. 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"
  17. 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. } }
  18. CQRS Separar leitura de escrita Command + Handler = Event

    (sincronizar os bancos com base neste evento)
  19. 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)
  20. COMO COMEÇAR? Aplique CQRS primeiro! Command + Handler pattern (Event

    Sourcing possui uma curva de aprendizado maior)