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();
Entity objects encapsulate business logic The best place to write your business logic is in entity objects, because they consistently enforce business logic for all views of data, accessed through any type of client interface.
The business logic should be placed in the model, and we should be aiming for fat models and skinny controllers. As a start point, we should start from the controller logic. For example: on update, your controller should direct your code to the method/service that delivers your changes to the model.
Two good reasons for putting the business logic in the database are: It secures your logic and data against additional applications that may access the database that don't implement similar logic.
All invariant to use-cases logic (business entities, business workflow components, e.g. Domain model, Domain services) goes to the Domain layer (Domain logic). This layer is responsible for concepts of the business domain and business rules.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With