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