Event Routing

87 views
Skip to first unread message

Marijn

unread,
Aug 1, 2012, 4:58:14 AM8/1/12
to ddd...@googlegroups.com
Hi everyone,

I was just wondering how everyone generally handles routing events when Aggregates are reloaded from the event store.
For now I have a very straightforward implementation where the AggregateRoot routes the loaded Event to the appropriate Entity in the Aggregate or itself, but I'm wondering what others are using.

An example to clear things up:

<?php


namespace Domain

{

    use DateRange;

    use DomainException;

    use Doctrine\Common\Collections\ArrayCollection;

    use Domain\Events\ConcertCreated;

    use Domain\Events\BookingAdded;

    use Domain\Events\BookingConfirmed;

    use Rhumsaa\Uuid\Uuid;


    final class Concert extends AggregateRoot

    {

        private $id;


        static public function create(Uuid $id, $name, DateRange $duration)

        {

            $concert = new Concert();

            

            $concertCreated = new ConcertCreated(

                $id,

                $concert->getNextPlayheadPosition(), // get the next event sequence counter

                $name,

                $duration->getStart(),

                $duration->getEnd()

            );

            

            // apply handles routing each event to the corresponding handler

            $concert->apply($concertCreated);

            // record marks the event as scheduled for persistence

            $concert->record($concertCreated);

            

            return $concert;

        }


        public function addBooking(Uuid $artistId, DateRange $timeSlot, $fee)

        {

            $bookingAdded = new BookingAdded(

                $id,

                $this->getNextPlayheadPosition(), // get the next event sequence counter

                $artistId,

                $timeSlot->getStart(),

                $timeSlot->getEnd(),

                $fee

            );


            $this->apply($bookingAdded);

            $this->record($bookingAdded);

            

            return $this->bookings->get($artistId);

        }


        // is invoked when loading from the database or when created with constructor.

        // yes, it's an artificial constructor... :-(

        protected function initializeCollections()

        {

            $this->bookings = new ArrayCollection();

        }

        

        protected function applyConcertCreated(ConcertCreated $concertCreated)

        {

            $this->id = $concertCreated->getConcertId();

        }


        protected function applyBookingAdded(BookingAdded $bookingAdded)

        {

            $booking = new Booking($this);


            $booking->apply($bookingAdded);


            $this->bookings->set($bookingAdded->getArtistId(), $booking);

        }


        protected function applyBookingConfirmed(BookingConfirmed $bookingConfirmed)

        {

            $this->bookings->get($bookingAdded->getArtistId())->apply($bookingAdded);

        }

    }

    

    final class Booking extends Aggregate

    {

        private $eventId;

        private $artistId;

        private $event;

        private $confirmed;

        

        public function confirm()

        {

            $this->assertNotConfirmed("You cannot confirm a booking twice");


            $bookingConfirmed = new BookingConfirmed(

                $this->eventId,

                // acquire the next playhead sequence from the AggregateRoot

                $this->event->getNextPlayheadPosition(),

                $this->artistId

            );


            // have the AggregateRoot route the event back here

            $this->event->apply($bookingConfirmed);

            // have the AggregateRoot mark the event for persistence

            $this->event->record($bookingConfirmed);

        }

        

        public function assertNotConfirmed($message)

        {

            if ($this->confirmed) {

                throw new DomainException($message);

            }

        }

        

        protected function __construct(Concert $event)

        {

            parent::__construct();


            $this->event = $event;

        }


        protected function applyBookingAdded(BookingAdded $bookingAdded)

        {

            $this->eventId = $bookingAdded->getEventId();

            $this->artistId = $bookingAdded->getArtistId();

            $this->confirmed = false;

        }


        protected function applyBookingConfirmed(BookingConfirmed $bookingConfirmed)

        {

            $this->confirmed = true;

        }

    }

}


On the one hand I really like it because it's very clear what is happening, to me at least. The risk however is that my AggregateRoots become loaded with a bunch of routing methods... But then again, in my biggest AR 1/4 of its LOC are consumed by those methods so it's not that big a deal.

What are some other strategies to deal with Event Routing within Aggregates? Are there any objections against this method?

Looking forward to your input.

Cheers,

Marijn

Reply all
Reply to author
Forward
0 new messages