4
votes

Well I'm trying to introduce myself to DDD, I'm very new to it and some concepts are still unclear.

Here is what I understood so far:

  • The domain is basically about data
  • The persistence layer is not tied to the domain, but business logic transaction may be.

When using Doctrine2, we use either EntityRepository or a CustomEntityRepository implementation.

In DDD, the Repository pattern seems a bit different, I've looked at .NET & Java examples as well as messages from the DDD mailing list, and people tends to accord that the repository should returns QueryObject, in Doctrine2, I project to return QueryBuilder instance from my repository.

Therefore to hide the complexity of working with QueryBuilder then a Query then an Hydrated result set, I implemented another service layer which I called Manager.

Here is how my domain looks like:

src/Domain/
├── Entity
│   ├── AbstractComment.php
│   ├── Comment.php
├── Manager
│   ├── CommentManager.php
└── Repository
    └── CommentRepository.php

The Entity folder is just about pure POPO.

The CommentRepository looks like this:

<?php
namespace Acme\Domain\Repository;

use Doctrine\Common\Collections\Criteria;

class CommentRepository
{
    private $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    /**
     * @param $id
     *
     * @return \Doctrine\ORM\QueryBuilder
     */
    public function findOneById($id)
    {
        $qb = $this->getEntityManager()
            ->getRepository('Acme:Domain\Entity\Comment')
                ->createQueryBuilder('c');

        $criteria = new Criteria();

        $criteria->andWhere(
            $criteria->expr()->eq('c.id', ':id')
        );

        $qb->addCriteria($criteria);

        $qb->setParameter('id', $id);

        return $qb;
    }
}

And the CommentManager:

<?php

namespace Acme\Domain\Manager;

class CommentManager
{
    protected $repository;

    public function __construct(CommentRepository $repository)
    {
        $this->repository = $repository;
    }

    public function findOneById($id)
    {
        return $this->repository->findOneById($id)->getQuery()->getOneOrNullResult();
    }
}
  1. Is it the correct approach for managing "entities"?
  2. Following such pattern, where do I have to handle the persistence?

I mean, if I'm right, the repository is basically like a collection, therefore it should provide add(Entity $e) and remove(Entity $e) methods, but where do I actually persist the entity?

Is it safe to do it inside the add() & remove() methods? Is it better to add a save() method to handle updates?

Thank you for your time.

1
If all you want to do is findOneById in your manager, you can also just: $this->repository->find($id) (assuming the id you are referring to is your primary key).Colin M
@ColinMorelli, I know, it was for the sake of the example. That's not about DDD : )Trent
@Trent I currently have a similar structure: repository reads only anyway, and I pass the repository also to consumers of the manager (manager is not the central point). The manager object mainly handles logic related with the persistence of the entities for me, which involves also all the business logic sorrounding changes and events related to our entities. I avoid proxying calls from the manager to the repositories: it becomes obnoxious and annoying on the long run: if you really need to handle some logic during fetches, bind the manager to the repository via a listener/events.Ocramius

1 Answers

8
votes

I started a series about DDD with Symfony2 that should answer your questions: http://williamdurand.fr/2013/08/07/ddd-with-symfony2-folder-structure-and-code-first/.

I mean, if I'm right, the repository is basically like a collection

Yes.

therefore it should provide add(Entity $e) and remove(Entity $e) methods

Yes.

but where do I actually persist the entity?

In this repository. However, this is probably not a Doctrine repository. Doctrine uses Entity/Repository terms, but they don't have the same meaning in DDD.