Created
April 15, 2014 13:06
-
-
Save codeliner/10731034 to your computer and use it in GitHub Desktop.
Sample to show interaction between two bounded contexts
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 | |
/** | |
* This file is an example gist to show the interaction between an ArticleContext and a LikeContext | |
* | |
* @example: | |
* | |
* We have to bounded contexts: ArticleContext and LikeContext and show the simplified process of following use case: | |
* | |
* In order to create a valid article Like, I should validate the article ID given to the LikeContext. | |
* | |
* The ArticleContext is our core doamin and the LikeContext is a supporting sub domain. | |
* | |
* The ArticleContext is the owner of the use case. A client can only create a Like for an Article via | |
* {@method \ArticleContext\API\ArticleService#createLikeForArticle}. | |
* | |
* The ArticleService ensures that the given articleId references an existing Article in the ArticleContext. | |
* If this is true then it delegates the creation of new Like for this Article to an ExternalLikeService, which is a | |
* TranslationAdapter between the ArticleContext and the LikeContext. | |
* | |
* The LikeContext has it's own business rules of how a referenceId (f.e. ID of an Article, but other references are also possible) should look like. | |
* If the LikeContext successfully creates a new Like the related LikeId is returned by each layer and finally sent back to client. | |
* | |
* Follow the way of the use case through the classes, starting with a simplified RestController which is responsible for | |
* the request: POST /articles/{articleId}/likes | |
* | |
* | |
* Date: 4/15/14 - 9:30 AM | |
* (c) Alexander Miertsch <[email protected]> | |
*/ | |
namespace Application\Rest\Controller { | |
use ArticleContext\API\ArticleService; | |
/** | |
* Class ArticleLikeController | |
* | |
* A rest controller which is responsible for ArticleLike resources | |
* | |
* @package Application\Rest\Controller | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class ArticleLikeController | |
{ | |
/** | |
* @var ArticleService | |
*/ | |
private $articleService; | |
/** | |
* Assume following route matches to this action: | |
* Route: /articles/{$articleId}/likes | |
* HTTP-Method: POST (with empty body) | |
* | |
* @param $articleId | |
* @return array Assume that we have a generic logic to translate the response array to JSON and set the correct response headers | |
*/ | |
public function createLike($articleId) | |
{ | |
$newLikeId = $this->articleService->createLikeForArticle($articleId); | |
return array('id' => $newLikeId); | |
} | |
} | |
} | |
namespace ArticleContext\Model\Article { | |
/** | |
* Class Article | |
* | |
* Simple Article entity, requires an integer as identifier | |
* | |
* @package ArticleContext\Model\Article | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class Article | |
{ | |
/** | |
* @var int | |
*/ | |
private $id; | |
/** | |
* @param int $id | |
*/ | |
public function __construct($id) | |
{ | |
$this->setId($id); | |
} | |
/** | |
* @return int ID of Article | |
*/ | |
public function id() | |
{ | |
return $this->id; | |
} | |
/** | |
* Private validation method for Article ID | |
* | |
* @param int $id | |
* @throws \InvalidArgumentException | |
*/ | |
private function setId($id) | |
{ | |
if (! is_int($id)) { | |
throw new \InvalidArgumentException("ArticleId must be of type integer"); | |
} | |
$this->id = $id; | |
} | |
} | |
/** | |
* Interface ArticleRepositoryInterface | |
* | |
* Simplified repository contract | |
* | |
* @package ArticleContext\Model\Article | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
interface ArticleRepositoryInterface | |
{ | |
/** | |
* @param int $id of Article | |
* @return null|Article | |
*/ | |
public function get($id); | |
/** | |
* Add or update given Article | |
* | |
* @param Article $anArticle | |
* @return void | |
*/ | |
public function store(Article $anArticle); | |
} | |
} | |
namespace ArticleContext\API { | |
use ArticleContext\Model\Article\Article; | |
use ArticleContext\Model\Article\ArticleRepositoryInterface; | |
/** | |
* Interface LikeServiceInterface | |
* | |
* Defines domain requirement for an external like service (TranslationAdapter between ArticleContext and LikeContext) | |
* | |
* The implementing class is placed in the infrastructure to follow a hexagonal architecture aka. Ports and Adapters: | |
* \ArticleContext\Infrastructure\Adapter\ExternalLikeService | |
* | |
* | |
* @package ArticleContext\Model\Service | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
interface LikeServiceInterface | |
{ | |
/** | |
* @param Article $anArticle | |
* @return int Like ID of new created Like | |
*/ | |
public function createLikeForArticle(Article $anArticle); | |
} | |
/** | |
* Class ArticleService | |
* | |
* Responsible for the use case: In order to create a valid article like, I should validate the article ID given to the LikesContext. | |
* | |
* @package ArticleContext\Model\Service | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class ArticleService | |
{ | |
/** | |
* @var LikeServiceInterface | |
*/ | |
private $likeService; | |
/** | |
* @var ArticleRepositoryInterface | |
*/ | |
private $articleRepository; | |
/** | |
* @param int $articleId | |
* @throws \RuntimeException | |
* @return int Like ID of the new created Like | |
*/ | |
public function createLikeForArticle($articleId) | |
{ | |
$article = $this->articleRepository->get($articleId); | |
if (! $article) { | |
throw new \RuntimeException("Article can not be found: $articleId"); | |
} | |
return $this->likeService->createLikeForArticle($article); | |
} | |
} | |
} | |
namespace ArticleContext\Infrastructure\Adapter { | |
use ArticleContext\API\LikeServiceInterface; | |
use ArticleContext\Model\Article\Article; | |
use LikeContext\API\LikeService; | |
/** | |
* Class ExternalLikeService | |
* | |
* Adapter to translate between ArticleContext and LikeContext | |
* | |
* @package ArticleContext\Infrastructure\Adapter | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class ExternalLikeService implements LikeServiceInterface | |
{ | |
/** | |
* @var LikeService part of the LikeContext and responsible for creating Likes for different referenced entities | |
*/ | |
private $likeContextService; | |
/** | |
* @param Article $anArticle | |
* @return int Like ID of the created Like | |
*/ | |
public function createLikeForArticle(Article $anArticle) | |
{ | |
//Use the LikeService from supporting sub domain to create a new Like for given Article | |
$likeId = $this->likeContextService->createLike('Article', (string)$anArticle->id()); | |
return $likeId; | |
} | |
} | |
} | |
namespace LikeContext\API { | |
use LikeContext\Model\Like; | |
use LikeContext\Model\LikeRepositoryInterface; | |
/** | |
* Class LikeService | |
* | |
* @package LikeContext\API | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class LikeService | |
{ | |
/** | |
* @var LikeRepositoryInterface | |
*/ | |
private $likeRepository; | |
/** | |
* Create a new Like on basis of given reference type and id | |
* | |
* @param string $referenceType | |
* @param string $referenceId | |
* @return int LikeId of new created Like | |
*/ | |
public function createLike($referenceType, $referenceId) | |
{ | |
$like = new Like($this->likeRepository->nextLikeId(), $referenceType, $referenceId); | |
//@TODO: Add transaction handling | |
$this->likeRepository->store($like); | |
return $like->id(); | |
} | |
} | |
} | |
namespace LikeContext\Model { | |
/** | |
* Class Like | |
* | |
* The Like entity represents a Like of an entity from another context, f.e. a Like of an Article | |
* | |
* @package LikeContext\Model | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class Like | |
{ | |
/** | |
* @var int | |
*/ | |
private $id; | |
/** | |
* @var string | |
*/ | |
private $referenceType; | |
/** | |
* @var string | |
*/ | |
private $referenceId; | |
/** | |
* @param int $id | |
* @param string $referenceType | |
* @param string $referenceId | |
*/ | |
public function __construct($id, $referenceType, $referenceId) | |
{ | |
$this->setId($id); | |
$this->setReferenceType($referenceType); | |
$this->setReferenceId($referenceId); | |
} | |
/** | |
* @return int | |
*/ | |
public function id() | |
{ | |
return $this->id; | |
} | |
/** | |
* @param int $id | |
* @throws \RuntimeException | |
*/ | |
private function setId($id) | |
{ | |
if (! is_int($id)) { | |
throw new \RuntimeException("Like ID must be of type integer"); | |
} | |
$this->id = $id; | |
} | |
/** | |
* @param string $referenceType | |
* @throws \InvalidArgumentException | |
*/ | |
private function setReferenceType($referenceType) | |
{ | |
$allowedTypes = array( | |
'Article', | |
'Product', | |
//more things that are likable | |
); | |
if (! in_array($referenceType, $allowedTypes)) { | |
throw new \InvalidArgumentException( | |
sprintf( | |
'ReferenceType is invalid. Must be one of the values: %s', | |
implode(",", $allowedTypes) | |
) | |
); | |
} | |
$this->referenceType = $referenceType; | |
} | |
/** | |
* @param string $referenceId | |
* @throws \RuntimeException | |
*/ | |
private function setReferenceId($referenceId) | |
{ | |
if (! is_string($referenceId)) { | |
throw new \RuntimeException("Reference ID must be of type string"); | |
} | |
$this->referenceId = $referenceId; | |
} | |
} | |
/** | |
* Interface LikeRepositoryInterface | |
* | |
* Simplified repository contract | |
* | |
* @package LikeContext\Model | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
interface LikeRepositoryInterface | |
{ | |
/** | |
* @param int $id of Article | |
* @return null|Article | |
*/ | |
public function get($id); | |
/** | |
* Add or update given Like | |
* | |
* @param Like $aLike | |
* @return void | |
*/ | |
public function store(Like $aLike); | |
/** | |
* Assume we work with a sequence based identifier | |
* | |
* @return int | |
*/ | |
public function nextLikeId(); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just a quick one, how would you get the next like id if it was integers or would you turn a uuid into an int?