php-ddd icon indicating copy to clipboard operation
php-ddd copied to clipboard

Implementing the Specification Pattern with Doctrine Repositories

Open webdevilopers opened this issue 8 years ago • 10 comments

There are some nice tutorials out there:

  • http://culttt.com/2014/08/25/implementing-specification-pattern/ @philipbrown

For Zend Framework too by @UFOMelkor:

  • http://programming-php.net/author/ufomelkor/

Another implementation by @mbrevda:

  • https://github.com/mbrevda/SpecificationPattern

And @maximecolin:

  • https://github.com/maximecolin/satisfaction

But there is also a implementation for Doctrine by @Happyr @Nyholm @cakper:

  • https://github.com/Happyr/Doctrine-Specification

Anybody else with other implementations? What are your experiences? Any best practices? Thanks for the feedback!

webdevilopers avatar Jun 22 '16 12:06 webdevilopers

My implementation is not implemented, that repo was to TRY to and implement the pattern... wasn't very successful.

mbrevda avatar Jun 22 '16 12:06 mbrevda

Tried any of the others @mbrevda ? What was missing for success?

webdevilopers avatar Jun 22 '16 16:06 webdevilopers

The HappyDoctrineSpecification works. I've been using in in prod for over a year. I will soon tag version 1.0.

Nyholm avatar Jun 22 '16 16:06 Nyholm

It wasn't so much the spec part, as much as I wanted a Specification that can be turned in a query. That proved to be quite difficult and I ran out of time.

On Wed, Jun 22, 2016, 7:14 PM Tobias Nyholm [email protected] wrote:

The HappyDoctrineSpecification works. I've been using in in prod for over a year. I will soon tag version 1.0.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/webdevilopers/php-ddd/issues/17#issuecomment-227795606, or mute the thread https://github.com/notifications/unsubscribe/AAR4jP1GbTEHNnWUzA60lKsoEDAHXGOXks5qOV9SgaJpZM4I7tPY .

mbrevda avatar Jun 22 '16 21:06 mbrevda

@Nyholm Can you say something about your directory/namespacing schema for the custom specs?

mablae avatar Jun 23 '16 04:06 mablae

Another Bundle I found is RulerZ by @K-Phoen:

  • http://blog.kevingomez.fr/2015/03/14/rulerz-specifications-and-symfony-are-in-a-boat/
  • https://github.com/K-Phoen/RulerZBundle

Using RulerZ, the two previous repositories can be refactored:

<?php
interface CompanyRepository
{
    public function save(Company $company);
    public function find($slug);
    public function matchingSpec(Specification $spec);
}

class DoctrineCompanyRepository extends EntityRepository implements CompanyRepository
{
    // ...

    public function matchingSpec(Specification $spec)
    {
        $qb = $this->createQueryBuilder('c');

        return $this->rulerz->filterSpec($qb, $spec);
    }
}

class InMemoryCompanyRepository implements CompanyRepository
{
    private $companies = [];

    // ...

    public function matchingSpec(Specification $spec)
    {
        return $this->rulerz->filterSpec($this->companies, $spec);
    }
}

It is also possible to use the Specs for filtering directly inside Symfony Forms:

<?php
class CompanySearchType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $terms = $builder
            ->create('who')
            ->addModelTransformer(
                new SpecToStringTransformer(Spec\CompanyName::class, 'terms')
            );

        $location = $builder
            ->create('where')
            ->addModelTransformer(
                new SpecToStringTransformer(Spec\CompanyLocation::class, 'location')
            );

        $builder
            ->add($terms)
            ->add($location);
    }

    // ...
}

Personally I prefer using a Query passing the data to a Handler. Then the Handler creates the Specifications to pass them to my repository. The Handler then returns the Set from the Collection.

webdevilopers avatar Jun 26 '16 15:06 webdevilopers

A word on "coupling business logic with the specification pattern":

I can understand that looking at these concrete Doctrine implementations make you think, that business logic is linked to persistence. But coming from a DDD approach you would create a Base Specification like "filterGroup". Then you create a Doctrine (or Mysql, Mongo, InMemory) class based on that Spec and add only the stuff that will be related to the same Persistence storage: DoctrineUserRepository or InMemoryRepository. What you want is the coupling of the Business Logic to the Specification. And then you build your infrastructure around it.

http://www.whitewashing.de/2013/03/04/doctrine_repositories.html#comment-2751511485

I think @timglabisch also prefers this kind of approach? http://www.whitewashing.de/2013/03/04/doctrine_repositories.html#comment-1280738273

Coming from this nice article by @beberlei: http://www.whitewashing.de/2013/03/04/doctrine_repositories.html#comment-2751511485

Which was an inspiration on the @Happyr Doctrine-Specification btw..

webdevilopers avatar Jun 26 '16 15:06 webdevilopers

There is also an bundle by @rikbruil inspired by @Happyr Doctrine-Specification implementing the Doctrine Paginator: https://github.com/rikbruil/Doctrine-Specification

webdevilopers avatar Jun 26 '16 16:06 webdevilopers

BTW: Doing my first steps with @Happyr now:

/**
 * Class DoctrineContractRepository
 *
 * @package Rewotec\Contract\Infrastructure\Persistence\Doctrine
 */
class DoctrineContractRepository
    extends EntityRepository
    implements ContractRepository
{
    use EntitySpecificationRepositoryTrait;

Works like a charm @Nyholm!

Testing some more complex stuff now: https://github.com/Happyr/Doctrine-Specification/issues/128

webdevilopers avatar Jun 26 '16 17:06 webdevilopers

Where do you guys suggest to put the Specifications?

Normally I think they would go into the Domain / DomainModel namespace. But since the examples are used with Doctrine the are coupled to the infrastructure.

At least they should go into Acme\Infrastructure.

  • Acme\Infrastructure\Persistence\Doctrine (where als the repositories live)
  • Acme\Infrastructure\Persistence\Doctrine\FooRepository
  • Acme\Infrastructure\Persistence\Doctrine\FooSpecification

I've seen people adding a Domain namespace for this:

  • Acme\Infrastructure\Persistence\Domain\Foo\FooSpecification

Thoughts?

webdevilopers avatar Jun 27 '16 07:06 webdevilopers