Ejemplo n.º 1
0
<?php

// Domain Events describe things that have happened, but where do they come from? Wouldn't it be nice if we could make
// the objects themselves responsible for recording whatever happened to them? We can, by implementing the
// `RecordsEvents` interface.
namespace Buttercup\Protects\Tests;

use Buttercup\Protects\DomainEvent;
use Buttercup\Protects\DomainEvents;
use Buttercup\Protects\RecordsEvents;
use Buttercup\Protects\Tests\Misc\ProductId;
// Being good TDD'ers, let's write our tests first.
$test = function () {
    // We pick up a Basket, add a product, and remove the product again.
    $basket = Basket::pickUp(BasketId::generate());
    $basket->addProduct(new ProductId('TPB123'), "The Princess Bride");
    $basket->removeProduct(new ProductId('TPB123'));
    // We'll want the recorded events to reflect that these three operations have happened.
    $events = $basket->getRecordedEvents();
    it("should have recorded 3 events", 3 == count($events));
    it("should have a BasketWasPickedUp event", $events[0] instanceof BasketWasPickedUp);
    it("should have a ProductWasAddedToBasket event", $events[1] instanceof ProductWasAddedToBasket);
    it("should have a ProductWasRemovedFromBasket event", $events[2] instanceof ProductWasRemovedFromBasket);
    // We'll want a way to clear the events, so that the next time we call a method on Basket, we don't get a list with
    // old and new events mixed in the same result.
    $basket->clearRecordedEvents();
    it("should have no more events after clearing it", 0 == count($basket->getRecordedEvents()));
};
// We'll implement the simplest Basket we can to satisfy the tests. By ignoring the PHP syntax, you can read the class
// definition as **natural language**:
// > A basket records events.
    }
    /**
     * @param RecordsEvents $aggregate
     * @return void
     */
    public function add(RecordsEvents $aggregate)
    {
        // To store the updates made to an Aggregate, we only need to commit the latest recorded events to the `EventStore`.
        $events = $aggregate->getRecordedEvents();
        $this->eventStore->commit($events);
        $aggregate->clearRecordedEvents();
    }
    /**
     * @param IdentifiesAggregate $aggregateId
     * @return Basket
     */
    public function get(IdentifiesAggregate $aggregateId)
    {
        // Fetching a single `Basket` is extremely easy: all we need is to reconstitute it from its history! Compare
        // that to the complexity of traditional ORMs.
        $aggregateHistory = $this->eventStore->getAggregateHistoryFor($aggregateId);
        return Basketv4::reconstituteFrom($aggregateHistory);
    }
}
$basketId = BasketId::generate();
$basket = BasketV4::pickUp($basketId);
$basket->addProduct(ProductId::fromString('TPB01'), "The Princess Bride");
$baskets = new BasketRepository(new InMemoryEventStore());
$baskets->add($basket);
$reconstitutedBasket = $baskets->get($basketId);
it('should reconstitute a Basket to its state after persisting it', $basket == $reconstitutedBasket);
    public function __construct($basketId)
    {
        $this->basketId = (string) $basketId;
    }
    public static function fromString($string)
    {
        return new BasketId($string);
    }
    public function __toString()
    {
        return $this->basketId;
    }
    public function equals(IdentifiesAggregate $other)
    {
        return $other instanceof BasketId && $this->basketId == $other->basketId;
    }
    // A nice convention is to have a static generate() method to create a random BasketId. The example here is a bad
    // way to generate a UUID, so don't do this in production. Use something like https://github.com/ramsey/uuid
    public static function generate()
    {
        $badSampleUuid = md5(uniqid());
        return new BasketId($badSampleUuid);
    }
}
// Sample usage:
$basketId = BasketId::fromString('12345678-90ab-cdef-1234-567890abcedf1234');
// Casting to string gives you the original string back:
it("should cast to string", (string) $basketId == '12345678-90ab-cdef-1234-567890abcedf1234');
// Testing equality:
it("should equal instances with the same type and value", (new BasketId('same'))->equals(new BasketId('same')));
it("should not equal instances with a different value", !(new BasketId('other'))->equals(new BasketId('same')));