2

I'm trying really hard to refactor some of my legacy code in the project using best practices and design patterns + DDD so I'd love some feedback on an issue I'm currently having.

Let's assume that I have two entity classes:

class Dog
{
    protected $name;

    function __construct($name)
    {
        $this->name = $name;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    function bark()
    {
        echo 'Rawr';
    }
}

class Husky extends Dog
{
    /**
     * @var Sledge
     */
    protected $sledge;

    function __construct($name, Sledge $slegde)
    {
        parent::__construct($name);
        $this->sledge = $slegde;
    }

    function pull()
    {
        echo $this->sledge->pull();
    }
}

Dog is my regular entity and his only responsibility is to map database fields. Husky on the other hand has same responsibilities as dog but also delegates Sledge pulling.

Normally, both entities would have different repositories (should they? Since one inherits from one another) to call for, however business requirement implies that client does not have to specify dog's type (so it can either be a "basic" Dog or special Husky), just its name http://localhost/animal/fluffy.

What is more, currently they both reside in the same database table (recognized by type field) and right now there are no technical plans to change that (performance and time reasons).

What is the best way to do it?

  • Should I create some AnimalRepository, pull the data and treat it as DTO, detect it's type and then create appropriate class?
  • Should I create some kind of higher abstraction level mapper? How should it look like?
3
  • Just a feeling, but this sounds like a 100% CRUD application.
    – Songo
    Commented Oct 12, 2014 at 17:10
  • I really can't see how you can draw that conclusion based on the question...
    – JDT
    Commented Mar 11, 2015 at 19:20
  • There is a [PHP] tag available. No need to put it into the title.
    – JensG
    Commented Jun 10, 2015 at 9:41

3 Answers 3

1

We can solve any problem by introducing an extra level of indirection. I doubt that adding another level of abstraction would solve your problem.

I think the question you should be asking is 'do I need two repositories for this or just one' and the answer to this depends on a lot more than can be found here... No harm in starting off with the one Dog repository and moving off into separate repositories when it becomes clear that the Dog repository needs to have methods that are only valid for Huskies and your program structure would benefit from the separation.

0

I am not an expert on this subject, but will try an answer. According to this article the Repository pattern is specifically designed to solve your problem. So in my understanding you should have one repository that will return the concrete or abstract Animal required.

0

I think that answer to behavior extension cannot be abstracted itself - it depends on concrete problem. If we assume that pulling sledge is unusual dog behaviour* (encapsulate what varies) another level of abstraction would be the answer, but it would be different in case of pulling-sledge-contest-app where a dog can pull sledge in generall, but some are to weak to pull at all or don't have sledge equipped. You may also have some general dog contest where strategy pattern applies with some startCompetition() method.

*) In this context Husky class is not only a Dog, but a composition of a Dog and Sledge. I would start with Husky class signature that would look something like

class Husky extends Dog implements SledgePullingInterface {}
//or with another level of inheritance
class Husky extends SledgeDog {}

Now it's easier to "sniff the code". It looks strange when Husky is not a direct subtype of a Dog - maybe the name should be different? Maybe SledgeDog class is sufficient or race specific classes are separated for another reason? - can't tell.

If you map husky as a dog only (using DogMapper) it cannot be used in Husky class context. Just a dog that happens to be husky which you can tell by getting some 'race' field only - square is a rectangle, but not an abstract one. For a complete Husky class object you need

new PullingSledgeDogMapper(DogMapper $dog, SledgeMapper $sledge);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.