DDD with Symfony: repositories
In my last “DDD with Symfony” story, I briefly explained how to put apart models. There, I left the mapping info inside the bundle: let’s fix this. But, first of all, let’s talk about repositories.
The repository pattern
I will assume that you have already read about the repository pattern, but I you haven’t done it, this won’t be difficult. The first step is creating an interface which is going to contain all the needed methods. Create a ModelRepository.php
file inside the Domain folder (be careful with the path, read the namespace
):
<?php
namespace Acme\Domain\Repository;
/**
* Interface ModelRepository.
*
* @author Jon Torrado
*/
interface ModelRepository
{
/**
* Finds whatever stuff.
*
* @return Model[]
*/
public function findWhatever();
}
The next step is creating the repositories for each infrastructure. This means that we can change from ORM to ODM because the methods to retrieve the objets will be the same as they will all implement this interface. Let’s fix the mapping info first.
The infrastructure
For terms of brevity, let’s go straightforward. Create the following folders inside Acme:Infrastructure/Persistence/Doctrine/ORM/Mapping
. Now, move the all the *.orm.yml
files that we left inside the Resources/config/doctrine
folder in the previous post (you can also delete that folder). Next step: tell the bundle to look for the mapping files inside the newly created folder.
<?php
namespace Acme\Bundle;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Class AcmeBundle.
*
* @author Jon Torrado
*/
class AcmeBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass($this->buildMappingCompilerPass());
}
public function buildMappingCompilerPass()
{
return DoctrineOrmMappingsPass::createYamlMappingDriver(
[__DIR__ . '/../Infrastructure/Persistence/Doctrine/ORM/Mapping' => 'Acme\Domain\Model'],
[],
false,
['AcmeBundle' => 'Acme\Domain\Model']
);
}
}
So far so good. Everything is still working… but we just have an interface which does nothing.
The infrastructure repository
We have to create a concrete repository for a concrete solution. As we are going to use Doctrine ORM, let’s create the following file: Infrastructure/Persistence/Doctrine/ORM/Repository/DoctrineORMModelRepository.php
. That file should contain something like this:
<?php
namespace Acme\Infrastructure\Persistence\Doctrine\ORM\Repository;
use Doctrine\ORM\EntityRepository;
/**
* Class DoctrineORMRModelRepository.
*
* @author Jon Torrado
*/
class DoctrineORMRModelRepository extends EntityRepository implements ModelRepository
{
/**
* {@inheritdoc}
*/
public function findWhatever()
{
return $qb
//->doWhatever()
->getQuery()
->getResult();
}
}
That’s cool! So, now, if you want to change from Doctrine ORM to ODM, you just need to create a new mapping file and implement a new DoctrineODMModelRepository.php
concrete repository. Hey! Still one thing missing.
Linking the model to the repository
This is something related to the infrastructure, so, in this example, edit the model.orm.yml
file and add there the concrete implementation.
Acme\Domain\Model\Model:
type: entity
table: model
repositoryClass: Acme\Infrastructure\Persistence\Doctrine\ORM\Repository\DoctrineORMModelRepository
[...]
In the next story, we will talk about repositories as services, so you don’t have to inject the complete Entity Manager to your services.