Reputation: 2825
I've read the Fowler's article about "Anemic Domain Model" (link: http://www.martinfowler.com/bliki/AnemicDomainModel.html), and I agree with his point of view.
I've tried to create an application where Entities are simple POPO, but in this way, I have a fat service layer, whereas put some logic into entities would be the simplest solution.
So I would have an architecture like this:
^
| Twig
| Controller | API
| Service
| Model
| Entity
Where:
Entity: would be simple POPO, just a bag of setter and getter
Model: would be Entity Object decorated with business logic
Service: contains all business logic which involve more than one Entity (here I would place also validation task), and acts like a converter entity -> model
Controller | API: just matches Request with Service, ParamConvert and check autorization
Twig: presentation layer
My problem is how to hide entity layer to controllers and works only with model. In order to decorate my entities with business logic, I thought build a service that uses repository and decorates results (I can't find another way to achieve that).
So, a stupid example:
namespace ...\Entity\Article;
class Article {
private $id;
private $description;
// getter and setter
}
namespace ...\Model\Article;
class Article {
private $article; // all methods will be exposed in some way
private $storeService; // all required services will be injected
public function __construct($article, $storeService) {
$this->article = $article;
$this->storeService = $storeService;
}
public function getEntity() {
return $this->article;
}
public function isAvailable() {
return $storeService->checkAvailability($this->article);
}
...
}
class ArticleService {
private $storeService; // DI
private $em; // DI
private $repository; // Repository of entity class Article
public function findById($id) {
$article = $this->repository->findById($id);
return new \Model\Article($article, $storeService);
}
public function save(\Model\Article $article) {
$this->em->persist($article->getEntity());
}
...
}
And upper layers are made in the usual way. I know it's not a good solution, but I can't find a better way to have a Model layer. And I really don't like to have something like:
$articleService->isAvailable($article);
instead of a more OO:
$article->isAvailable();
Upvotes: 13
Views: 2538
Reputation: 48865
I have the DoctrineEntity object Extend the DomainModel object. And while the controllers may actually receive DoctrineEntities, they only operate on the DomainModelInterface.
... namespace DomainModel;
interface ArticleDomainModelInterface ...
interface ArticleDomainModelRepositoryInterface ... // create, find, save, commit
class ArticleDomainModel implements ArticleDomainModelInterface
... namespace Doctrine;
class ArticleDoctrineEntity extends ArticleDomainModel
class ArticleDoctrineRepository implements ArticleDomainModelRepositoryInterface
... namespace Memory;
// Usually dont need a memory article object
class ArticleMemoryRepository implements ArticleDomainModelRepositoryInterface
All model creation and persistence are done through a repository. The controllers and other related services are only aware of the ArticleDomainModel methods. This gives you a nice separation and allows using different repositories for testing or for supporting different persistence mechanisms. It also allows using value objects in your domain model while still persisting them with Doctrine 2.
However, in php, I do struggle with the notion of what sort of useful business logic can actually be put in the domain model objects themselves. I tend to end up with most of the logic in services. And that is because most of my php applications are heavily crud oriented.
There is also the question: should controllers themselves have access to domain model objects?
One of the main developers of Doctrine 2, Benjamin Eberlei, has a number of blog posts on this subject. All of his articles are worth reading in detail. Here are some:
http://www.whitewashing.de/2013/07/24/doctrine_and_domainevents.html http://www.whitewashing.de/2012/08/22/building_an_object_model__no_setters_allowed.html http://www.whitewashing.de/2012/08/18/oop_business_applications__command_query_responsibility_seggregation.html
Upvotes: 1