Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CQRS and Event sourcing

CQRS and Event sourcing

Presentation given at GDG-Natal, January/2017

Emmerson Siqueira

February 16, 2017
Tweet

More Decks by Emmerson Siqueira

Other Decks in Programming

Transcript

  1. Command • Represents user intention • Class name is written

    in the imperative • Data Transfer Object • Immutable • Serializable
  2. Command code example final class DisenrollStudentFromSubject { private $student; private

    $enrollment; public function __construct(StudentId $student, EnrollmentId $enrollment) { $this->student = $student; $this->enrollment = $enrollment; } public function getStudent() : StudentId { return $this->student; } public function getEnrollment() : EnrollmentId { return $this->enrollment; } }
  3. Command handler • Accomplish a business task with data from

    the command; • Can use service objects; • Should affect only one entity/aggregate; • Can fire events; • Command handlers and commands have a relationship 1:1; • Can throw exceptions; • Should not return anything;
  4. Command handler code example use Command\DisenrollStudentFromSubject as Command; class DisenrollStudentFromSubject

    { private $students; public function __construct(RepositoryInterface $students) { $this->students = $students; } /** * @throws EnrollmentNotFound */ public function handle(Command $command) : void { $student = $this->students->find($command->getStudent()); $student->disenroll($command->getEnrollment()); $this->students->save($student); } }
  5. Command bus • Take a command and match it to

    its handler • Ability to queue the command • Apply decorators to run around handlers
  6. Command bus $commandBus->dispatch( new DisenrollStudentFromSubject($student, $enrollment) ); • Take a

    command and match it to its handler • Ability to queue the command • Apply decorators to run around handlers
  7. Read model • Can have direct access to the database;

    • Should report data back to the client using DTO tailored to the specific needs of the view; • It only queries data and can do calculations with the support of domain objects.
  8. Event sourcing Storing all the changes (events) to the system,

    rather than just its current state by using an append-only and immutable store.
  9. CQRS + Event sourcing “Event sourcing is commonly combined with

    the CQRS pattern by performing the data management tasks in response to the events, and by materializing views from the stored events.”
  10. Events • Events have already occurred; • Each event represents

    a set of changes; • Events are immutable and serializable; • We describe events using Ubiquitous Language and in the past tense; • We can use events to express history; • Events can't be undone; • If something went wrong, we compensate it with other event.
  11. Event code example interface DomainEvent { public function getAggregateRootId() :

    string; public function getOccurredOn() : DateTimeImmutable; }
  12. Event code example final class StudentWasDisenrolledFromSubject implements DomainEvent { private

    $student; private $enrollment; public function __construct( StudentId $student, EnrollmentId $enrollment, DateTimeImmutable $occurredOn ) { /** assign attributes here */ } public function getOccurredOn() : DateTimeImmutable { return $this->occurredOn; } public function getAggregateRootId() : string { return (string) $this->student; } }
  13. Aggregates (ES context) • Record events • Do not expose

    state • Protect invariants • Final state is built by replaying stored events
  14. Aggregate code example class Student extends EventSourcedAggregateRoot { public function

    disenroll(EnrollmentId $enrollment) { $this->apply( new StudentWasDisenrolledFromSubject($this->id, $enrollment) ); } }
  15. Aggregate code example class Student extends EventSourcedAggregateRoot { public function

    disenroll(EnrollmentId $enrollment) { $this->apply( new StudentWasDisenrolledFromSubject($this->id, $enrollment) ); } private function applyStudentWasDisenrolledFromSubject( StudentWasDisenrolledFromSubject $event ) { $this->enrollments ->filter(function ($enrollment) use ($event) { return $enrollment->getId()->equals($event->getEnrollment()); }) ->forget(); } }
  16. Loading and saving aggregates // reconstitute aggregate by replaying events

    $student = $this->students->find($command->student()); // registering events $student->disenroll($command->enrollment()); // appends new events to the event store $this->students->save($student);
  17. Loading and saving aggregates // reconstitute aggregate by replaying events

    $student = $this->students->find($command->student()); // registering events $student->disenroll($command->enrollment()); // appends new events to the event store $this->students->save($student);
  18. Projector code example public function projectStudentWasDisenrolledFromSubject( StudentWasDisenrolledFromSubject $event ) {

    $this->redisClient->incr('disenrolledStudents'); } public function projectStudentWasRegistered( StudentWasRegistered $event ) { $studentProfile = new StudentProfile( $event->getName(), $event->getEmail(), $event->getBirthdate() ); $this->elasticSearchClient->save($studentProfile); }