Skip to content

Instantly share code, notes, and snippets.

@webdevilopers
Created February 25, 2019 18:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save webdevilopers/fa3bbfbb8e2b36a4a4619217e6469fdc to your computer and use it in GitHub Desktop.
Save webdevilopers/fa3bbfbb8e2b36a4a4619217e6469fdc to your computer and use it in GitHub Desktop.
Using Entity as Factory for Value Object
<?php
abstract class DormerType implements DormerTypeInterface
{}
<?php
interface DormerTypeInterface
{
public function canHaveGutterInstalled(): bool;
}
<?php
class FlatRoofDormer extends DormerType
{
public function canHaveGutterInstalled(): bool
{
return true;
}
}
@webdevilopers
Copy link
Author

webdevilopers commented Feb 25, 2019

Version 1:

<?php

final class Dormer
{
    /** @var DormerType $type */
    private $type;

    public static function construct(DormerType $type)
    {
        $this->type = $type;
    }

    public function addGutter(Gutter $gutter)
    {
        if (!$this->type->canHaveGutterInstalled()) {
            throw new DormerTypeCannotHaveGutterInstalledException();
        }
 
       $this->gutter = $gutter;
    }
}
final class DormerCalculationFactory extends AbstractCalculator
{
    private $dormerTypeRepository;

    public function createWithDto(CalculateDormerCommand $dto)
    {
        $dormerType = $this->dormerTypeRepository->ofId(DormerTypeId::fromInteger($dto->dormerTypeId));

        /** @var Dormer $dormer */
        $dormer = Dormer::construct($dormerType);
        $dormer->addGutter(Gutter::fromDto($dto->gutter));
    }
}

Version 2:

<?php

class DormerType()
{
    private $id;

    public function calculateDormer(Gutter $gutter)
    {
         if (!$this->canHaveGutterInstalled()) {
            throw new DormerTypeCannotHaveGutterInstalledException();
        }
       
        return new Dormer($this->id(), $gutter);
    }
}

Version 3:

final class DormerCalculationFactory extends AbstractCalculator
{
    private $dormerTypeRepository;

    public function createWithDto(CalculateDormerCommand $dto)
    {
        $dormerType = $this->dormerTypeRepository->ofId(DormerTypeId::fromInteger($dto->dormerTypeId));

        /** @var Dormer $dormer */
        $dormer = Dormer::construct($dormerType->id());

        if (null !== $dto->gutter && $dormerType->canHaveGutterInstalled()) {
            $dormer->addGutter(Gutter::fromDto($dto->gutter));
        }
    }
}

@webdevilopers
Copy link
Author

Since DormerType is an Entity I like the idea of only passing the DormerTypeId in Version 2.

@webdevilopers
Copy link
Author

webdevilopers commented Feb 26, 2019

After reading the following post on @culttt by @philipbrown I added a Version 3 with a pure factory.

Due to the coupled nature of the Factory and the object, it is usually fine to allow the Factory to protect the invariants of the object.

At the bottom line versions 1 and 2 "enforce" the invariants. A guttercan not be added to a DormerType that does not support it.
Version 3 protects the invariants but a developer could still create a Dormer by passing any type and any gutter.

But is this a requirement at all - protecting an object from inconsistently being created by the developer?

Version 3 "feels right".

What are your thoughts on this?

@webdevilopers
Copy link
Author

Discussion continued on phpddd:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment