3

I'm having my User entity value objects loosely coupled, and because of that I use a UserFactory to build the object whenever it comes from the database or when creating a entirely new entity to the domain.

Would it be okay to tightly couple the value objects so I can get rid of its Factory and having my Application Services bloated with individual value object instantiation logic (before being able to inject them) when updating the entity its properties? Aren't value objects tightly related to their root anyways?

For example, when I update one of the properties with the loosely coupled version I would have to instantiate the value object first, and then inject it. But in the tightly coupled example I would be able to just enter the new values directly without having to explicitly go through the process of instantiating the VOs.

Example:

// Updating User's name (loosely coupled version)
$firstName = new FirstName('John');
$lastName  = new LastName('Doe');
$fullName  = new FullName($firstName, $lastName);

$user->setFullName($fullName);

// Updating User's name (tightly coupled version)
$user->setFullName('John', 'Doe');

Loosely coupled:

class User extends Entity
{
    private $fullName;
    private $email;

    public function getFullName()
    {
        return $this->fullName;
    }

    public function setFullName(FullName $fullName)
    {
        $this->fullName = $fullName;

        return $this;
    }

    public function getEmail()
    {
        return (string) $this->email;
    }

    public function setEmail(Email $email)
    {
        $this->email = $email;

        return $this;
    }

    // etc.
}

Tightly coupled:

class User extends Entity
{
    private $fullName;
    private $email;

    public function getFullName()
    {
        return $this->fullName;
    }

    public function setFullName($firstName, $lastName)
    {
        $firstName      = new FirstName($firstName);
        $lastName       = new LastName($lastName);
        $this->fullName = new FullName($firstName, $lastName);

        return $this;
    }

    public function getEmail()
    {
        return (string) $this->email;
    }

    public function setEmail($email)
    {
        $this->email = new Email($email);

        return $this;
    }

    // etc.
}
Kid Diamond
  • 2,232
  • 8
  • 37
  • 79
  • Be curious to see what kind of answers you get. PHP does not have all the fancy data transfer object generation tools that other platforms tend to have. For myself, when I do use value objects, then I try to stay "pure" and use your first approach. I suspect you are trying to use your DDD objects directly in forms and such? – Cerad Sep 18 '14 at 13:43
  • @Cerad Somewhat. I can use the first approach, but it bloats my code by having to instantiate the individual value objects first (for the properties I want to update) before I can actually update the entity by injecting them. The second approach gets rid of it, as well as its factory. And since value objects are more often then not tightly related to their entities, I was just wondering if it would be okay to tightly couple them as well then. – Kid Diamond Sep 18 '14 at 13:48
  • Yep. I cheat a bit and allow my Name value object to be mutable. $user->getName() returns a clone of the Name value object. I can then change the individual properties and use $user->changeName($name) to update. But in general, I'd have to say that DDD is not overly concerned about code bloat. – Cerad Sep 18 '14 at 14:33

1 Answers1

2

I think that the example is very simplistic and does not show the true extent of the problem/question. I try to add more scenarios which will better demonstrate the difference between "loosely" & "tightly" coupled solutions.

Using a sophisticated Value Object shows that it is not the "setter" responsibility to build a Value Object, because for setting of date you need locale (or let's imagine other values - just for demonstration sake) and not only the string value of date. So passing around date as Value Object makes more sense and clearer demonstrate the intent.

class User extends Entity
{

    private $dateOfBirth;

    public function setDateOfBirth(\Zend_Date $date)
    {
        $this->dateOfBirth= $date;
    }

    public function setDateOfBirth2($date = null, $part = null, $locale = null)
    {
        $date = new \Zend_Date($date, $part, $locale);

        $this->dateOfBirth = $date;
    }
}

As you can see the method User::setDateOfBirth2() does not look right - it has two responsibilities and thus breaks SRP. And in case you needed to set the date using Zend_Date object you would have to add another method. In next example you can see that the setter should accept the Value Object only and for creation of "complex" Value Objects you can either create a helper (factory) method or a Factory - depends how complicated it is:

class User extends Entity
{

    private $dateOfBirth;

    public function setDateOfBirth(\Zend_Date $date)
    {
        $this->date = $date;
    }

    public function createDate($date = null, $part = null, $locale = null)
    {
        return new \Zend_Date($date, $part, $locale);
    }
}

$user = new User;

$user->setDateOfBirth($dateOfBirth);

// or

$user->setDateOfBirth($user->createDate($date, $part, $locale));
Tomas Dermisek
  • 778
  • 1
  • 8
  • 14
  • +1 The only exception I see would be if VO creation needs a lot of data from the Entity -- then it would be better to delegate VO creation to the Entity entirely – guillaume31 Sep 22 '14 at 11:57