DEV Community

Cover image for Domain-Driven Design - The Factory in PHP

Domain-Driven Design - The Factory in PHP

Fluttershy on February 17, 2019

EDIT: Added usage example in the modified TimeSpanFactory. In Domain-Driven design we want our domain model to be bullet proof. Sometimes it is ne...
Collapse
 
canderson93 profile image
Carl Anderson

Is there any reason we use a factory class instead over a factory function?

class TimeSpan
{
    /** @var \DateTimeImmutable **/
    private $from;

    /** @var \DateTimeImmutable **/
    private $until;

    private function __construct(\DateTimeImmutable $from, \DateTimeImmutable $until) 
    {
        if ( $from >= $until ) {
            throw new \InvalidArgumentException('Invalid time span.');
        }
        $this->from = $from;
        $this->until = $until;
    }

    // Some other useful stuff goes in here...

    public static function createFromConfiguration(\TimeSpanConfiguration $timeSpanConfiguration, \DateTimeImmutable $from, \DateTimeImmutable $until): TimeSpan
    {
        // We just ask the configuration if the given from-until time span is valid.
        // That way we don't need any getters on the configuration. Neat.
        if ( !$timeSpanConfiguration->isValidTimeSpanFromUntil($from, $until) ) {
            throw new \DomainException('This time span is too long!');
        }

        return new TimeSpan($from, $until);
    }
}

// Usage:
$config = new TimeSpanConfiguration(new \DateInterval('PT2D'));
$timeSpan = TimeSpan::createFromConfiguration($config, new \DateTimeImmutable('2019-02-17 17:00:00'), new \DateTimeImmutable('2019-02-17 18:00:00'));

I've not dealt much with factory classes, but naïvely a function seems cleaner to me -- That being said, I've noticed that the class pattern is very popular, so I was wondering if you could explain some of the reasoning behind it.

Collapse
 
coajaxial profile image
Fluttershy

You are absolutely right! It actually depends what you are constructing. Sometimes it's not that simple like in this example, sometimes you may have multiple dependencies that you need to create a class, that's where a own dedicated factory may be more maintainable than passing multiple dependencies to your constructor/factory method. Also, dependency injection is way easier if you have a dedicated factory class :)

Collapse
 
daanwilmer profile image
Daan Wilmer

In languages like Java or C# you can declare a certain method (including the constructor) to be only visible to the same package (Java) or project (C#). This makes sure you don't have to jump through the hoops of reflection to let the Factory use the constructor without making it available to the rest of the world. This makes it a lot cleaner.

The main reason you would want a separate class is separation of concerns: one class that deals with the logic of being a Thing (a TimeSpan, in this case), one class that deals with the logic of creating a thing (the TimeSpanFactory).
If the business logic for creating a Thing is more involved than a simple check, this is easier to contain in a ThingFactory class than in the Thing::factory(...) function.
If you need multiple different ways to create a Thing, for example if you have different types of configuration or if you want to mock the factory for testing, it's easier to have that in separate ThingFactory classes than to have it in the Thing.
There are a couple more reasons why you would want this separation, and most of them have to do with rising complexity of having it all in one class. I will concede that it's not always worth it in PHP, where it's a bit harder to separate the classes, but it does pay off as complexity increases.

 
coajaxial profile image
Fluttershy

Because the factory gets its dependencies through the constructor - the most used way to inject dependencies. You can see the factory more as a service. On the other hand, the factory method gets both dependencies and data. This is hard or even impossible to configure in a standard dependency injection container.

Collapse
 
anwar_nairi profile image
Anwar

Thanks so much for sharing, I really like DDD examples because this is a pattern that I am not used to. Very interesting!

Collapse
 
dramentol profile image
David Ramentol

Hi,

just 2 questions:

What are the side effects? and what and why is illegal?

Thanks in advance.