DDD with Symfony: pulling apart models
One of the very first steps when doing DDD is pulling apart your models within the domain folder. But, before doing this, we must create the correct folder structure. Please, do not try to go from 0 to 100% in just one step. Do small iterations until you reach your final goal.
Folder structure
If you do some research about Symfony and DDD, you will see that there are different approaches. Every single one is good (or maybe not), so read my proposal carefully.
I usually start with the following project structure:
src/
Acme/
Bundle/
Domain/
Model/
If you are using Composer PSR-4 (you should), you must have something like this:
"autoload": {
"psr-4": {
"Acme\\": "src/Acme/"
},
After editing that section, you must launch composer dump-autoload
in order to get your files correctly loaded by Composer.
Creating the models
If you already have some entities created, just copy them to the Model
folder. But, DO NOT USE annotations because you are coupling your models to Doctrine ORM. With this done, your model can be used in any other PHP project.
After this, we need to create the mapping info. For reasons of brevity, I’m going to create it inside the bundle.
src/
Acme/
Bundle/
Resources/
config/
doctrine/
Model.orm.yml
Domain/
Model/
Model.php
I prefer using YAML, but you can also use XML. Check the documentation (and use an IDE to get autocompletion, for sure!).
At this point, Symfony is unable to find our models because they are not in the Entity
folder. We must change how this works. There are two ways of accomplishing this. Here is the one I use.
Changing the models folder
Did you ever read about CompilerPasses? If not, stop some minutes and read about them. Did you do that? Ok, let’s continue.
We can add a compiler pass that loads the recently changed model folder. Open your src/Acme/Bundle/AcmeBundle.php
file and add the following content:
<?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__ . '/Resources/config/doctrine' => 'Acme\Domain\Model'],
[],
false,
['AcmeBundle' => 'Acme\Domain\Model']
);
}
}
That’s it! If you read that code, it’s pretty straightforward. Just to explain a bit, the last line will allow you to work with repositories like this:
$doctrine->getRepository('AcmeBundle:Model');
To finish this story: be sure not to have anaemic models, please!