Created
March 5, 2018 20:54
-
-
Save carousel/6571bd42c660bb94e30c39d87490841e to your computer and use it in GitHub Desktop.
PHP hexagon + CQRS example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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