Skip to content

Instantly share code, notes, and snippets.

@benglass
Last active August 29, 2015 14:07
Show Gist options
  • Save benglass/36eec9a2f76bf9380d84 to your computer and use it in GitHub Desktop.
Save benglass/36eec9a2f76bf9380d84 to your computer and use it in GitHub Desktop.
<?php
class PostController
{
public function publishAction(Post $post)
{
$this->postManager->publish($post);
return $this->redirectTo('post_edit', array('id' => $post->getId()));
}
}
<?php
class PostManager
{
// Manager classes are part of the Service layer and are responsible for orchestrating communication between various services
// In this case we are tying together the repository service that loads/persists objects and the event dispatcher service
// This is also where transactions are being handled and if we were to add caching it would happen here
// Having these domain events emmitted means we can customize the behavior without having to rewrite the code in the manager and also significantly reduces the dependencies of the manager class which mostly becomes a Facade for the application so it can do operations without knowing about the complexity involved
public function save(Post $post)
{
$this->repository->save($post);
$this->repository->flush($post);
$events = $post->releaseEvents();
for ($event in $events) {
$this->eventDispatcher->dispatch($event::NAME, $event);
}
}
// Managers also expose an API onto the domain to the application (controllers, commands, etc.)
// This means they should include explicit actions for anything that can happen to domain objects (entities)
// That way if the logic of how a post is published changes then it can happen in this one place
// This also reduces controller action size
public function publish(Post $post)
{
$post->publish();
$this->save($post);
}
// In certain circumstances we may have to dispatch domain events directly in the manager
// For example a Post cant really dispatch a deleted event, so I think we have to do it here
// It seems this may also be true of the creation of new posts if we need an event for that then the PostManager::save method
// would need to check if $post->getId() is set and if not dispatch the post.added event, otherwise its a post.edited event
// whenever possible we want to avoid really generic events like post.edited and prefer specific events like post.published, post.archived, etc.
public function delete(Post $post)
{
$this->repository->delete($post);
$this->repository->flush($post);
$postDeleted = new PostDeletedEvent($post);
$this->eventDispatcher->dispatch($postDeleted::NAME, $postDeleted);
}
}
<?php
class Post
{
private $pendingEvents = array();
// The models define the domain objects and how they can interact with each other
// A domain object is something that would make sense to the client (a Post, a Category, etc.)
// Instead of having a method like setStatus we create methods that encapsulate actions that can be taken on these objects
// So in this case there would be a publish and unpublish method, any changes to how that state is stored within the object is not our concern
// Since these objects represent the domain, they are the logical place for emitting events that occur within the domain.
// A single action might raise multiple domain events, I dont have a good example of this
// A domain event is something that is significant to the client (a post is published, a post is archived, a category is added)
public function publish()
{
$this->status = 'published';
$this->raise(new PostPublishedEvent($this));
}
public function raise(Event $event)
{
$this->pendingEvents[] = $event;
}
public function releaseEvents()
{
$this->pendingEvents = array();
return $this->pendingEvents;
}
}
<?php
// There might be a single EmailListener for the entire application which listens to multiple domain events and sends emails
// We dont have to create tons of listeners, especially if we can group them in some logical way
class PostPublishedEvent
{
const NAME = 'post.published';
protected $post;
public function __construct(Post $post)
{
$this->post = $post;
}
public function getPost()
{
return $this->post;
}
}
<?php
// The repository is responsible for encapsulating all persistence operations (save, find, remove) behind a simple interface
// It should not really be used directly in most places since we want to force the application to go through the Manager which will ensure that the right events are dispatched
// The logic about querying should live inside the repository as well
class PostRepository
{
public function findById($id)
{
return $this->find($id);
}
public function save(Post $post)
{
return $this->find($id);
}
public function delete(Post $post)
{
return $this->em->remove($post);
}
public function flush(Post $post = null)
{
return $this->em->flush($post);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment