Skip to content

Instantly share code, notes, and snippets.

@carousel
Created March 5, 2018 20:54
Show Gist options
  • Save carousel/6571bd42c660bb94e30c39d87490841e to your computer and use it in GitHub Desktop.
Save carousel/6571bd42c660bb94e30c39d87490841e to your computer and use it in GitHub Desktop.
PHP hexagon + CQRS example
<?php
interface ReadPostRepository
{
public function byId(PostId $id);
public function all();
public function byCategory(CategoryId $categoryId);
public function byTag(TagId $tagId);
public function withComments(PostId $id);
public function groupedByMonth();
}
interface WritePostRepository
{
public function byId(PostId $id);
public function add(Post $post);
}
//MySQL repository implementation
class PDOPostRepository implements WritePostRepository
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function byId(PostId $id)
{
$stm = $this->db->prepare(
'SELECT * FROM posts WHERE id = ?'
);
$stm->execute([$id->id()]);
return recreateFrom($stm->fetch());
}
public function add(Post $post)
{
$stm = $this->db->prepare(
'INSERT INTO posts (title, content) VALUES (?, ?)'
);
$stm->execute([
$post->title(),
$post->content(),
]);
}
}
class AggregateRoot
{
private $recordedEvents = [];
protected function recordApplyAndPublishThat(DomainEvent $domainEve
{
$this->recordThat($domainEvent);
$this->applyThat($domainEvent);
$this->publishThat($domainEvent);
}
protected function recordThat(DomainEvent $domainEvent)
{
$this->recordedEvents[] = $domainEvent;
}
protected function applyThat(DomainEvent $domainEvent)
{
$modifier = 'apply' . get_class($domainEvent);
$this->$modifier($domainEvent);
}
protected function publishThat(DomainEvent $domainEvent)
{
DomainEventPublisher::getInstance()->publish($domainEvent);
}
public function recordedEvents()
{
return $this->recordedEvents;
}
public function clearEvents()
{
$this->recordedEvents = [];
}
}
//Post aggregate (with events)
class Post extends AggregateRoot
{
private $id;
private $title;
private $content;
private $published = false;
private $categories;
private function __construct(PostId $id)
{
$this->id = $id;
$this->categories = new Collection();
}
public static function writeNewFrom($title, $content)
{
$postId = PostId::create();
$post = new static($postId);
$post->recordApplyAndPublishThat(
new PostWasCreated($postId, $title, $content)
);
}
public function publish()
{
$this->recordApplyAndPublishThat(
new PostWasPublished($this->id)
);
}
public function categorizeIn(CategoryId $categoryId)
{
$this->recordApplyAndPublishThat(
new PostWasCategorized($this->id, $categoryId)
);
}
public function changeContentFor($newContent)
{
$this->recordApplyAndPublishThat(
new PostContentWasChanged($this->id, $newContent)
);
}
public function changeTitleFor($newTitle)
{
$this->recordApplyAndPublishThat(
new PostTitleWasChanged($this->id, $newTitle)
);
}
protected function applyPostWasCreated(PostWasCreated $event)
{
$this->id = $event->id();
$this->title = $event->title();
$this->content = $event->content();
}
protected function applyPostWasPublished(PostWasPublished $event)
{
$this->published = true;
}
protected function applyPostWasCategorized(PostWasCategorized $event)
{
$this->categories->add($event->categoryId());
}
g'vvh,;'
protected function applyPostContentWasChanged(PostContentWasChanged $event)
{
$this->content = $event->content();
}
protected function applyPostTitleWasChanged(PostTitleWasChanged $event)
{
$this->title = $event->title();
}
}
//Application service
class PostService
{
private $postRepository;
public function __construct(PostRepository $postRepository)
{
$this->postRepository = $postRepository;
}
public function createPost($title, $content)
{
$post = Post::writeNewFrom($title, $content);
$this->postRepository->add($post);
return $post;
}
}
//Read model
class PostsController
{
public function listAction()
{
$client = new Elasticsearch\ClientBuilder::create()->build();
$response = $client->search([
'index' => 'blog-engine',
'type' => 'posts',
'body' => [
'sort' => [
'created_at' => ['order' => 'desc']
]
]
]);
return [
'posts' => $response
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment