Skip to content

Instantly share code, notes, and snippets.

@codeliner
Created April 15, 2014 13:06
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save codeliner/10731034 to your computer and use it in GitHub Desktop.
Save codeliner/10731034 to your computer and use it in GitHub Desktop.
Sample to show interaction between two bounded contexts
<?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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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 <kontakt@codeliner.ws>
*/
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();
}
}
@bweston92
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment