If we are talking about Value Objects, we are inside a specific context: Domainr-Driven Design. And What is Domain Driven Design? Is a way to produce quality in the software we develop. Some quality is reached by using tests avoiding delivering software with fatal number of bugs. Quality of software is reached via tests. Wuality of design could be reached with Domain-Driven Design. DDD gives you tools necessary to high-quality software. Value Objects are a vital building block of DDD.

What is a Value Object

Value Objects are Value Types that measures, quantify, or describe things. It's just a bundle ov values that comes and goes as needed. When designed correctly, a Value instance can be created, handed off, and forgotten about. A value object could be ...

  • numbers
  • text strings
  • dates
  • times
  • person's full name
  • currecy
  • color
  • phone number
  • postal addresses
  • email address
  • json
  • uri
  • url
  • ...
  • an event
  • http message
  • http status code

Ubiquitous Language

Is a shared team language. It's shared by domain experts and developers. In fact, it's shared by everyone on the project team. No matter your role on the team, since you are on the team you use the Ubiquitous Language of the project. Is not an adoption of business language. Is not industry standard. Is a language developed by the team, composed of both domain experts and software developers.

How do we determine if a domain concept should be modeled as a Value?

To answer, we need to pay attention to its characteristics. When you care only about the attributes of an element of the model, classify it as a Value Object. Treat the Value Object as immutable. Don't give it any identity and avoid the design complexities necessary to mantain Entities.

If we are interested of a value and not about an identity, we need a value object. Is ONE sum of values that an entity can assume. A value object, could be equals to more entities. And more entities could be equals to one value object.

Characteristics

When you are trying to decide if a concept is a Value, you should determine whether it possesses most of these characteristics:

  • measures, quantifies, or describes a thing in the domain
  • immutable
  • conceptual whole
  • replaceable
  • equality
  • Side-Effect-Free

Measures, Quantifies or Describes

A Value Object is not a thing of our domain. A person has an age. Age is not a thing. Age measures an amount of years. A person has a name. The name is not a thing. Name describes how the person is called. Name is a description? Name is a Value Object.

Immutable

This means that Value Object are stateless. We cannot modify a value object. Setters violate immutability.

In some implementation, in other languages, setters are allowed BUT with private visiblity. In these cases basic attribute initialization is oerfirned first by invoking basic private setters.

You can design a Value Object handled to one or more Entities. But is not a good idea because if Entity change, Value changes too, and this is a violation of the quality of immutability. State cannot change! Is Value Object you have disigned need state changement, consider the idea to use an Entity.

namespace Sensorario\PugMi\ValueObjects;

final class EmailAddress {
    // we need a public constructor?
    private function __construct() {
        // ...
    }
    // named constructor
    public static function withDomain($domain) {
        return new self(compact('domain'));
    }
    // named constructor
    public static function withAddress($address) {
        return new self(compact('address'));
    }
}

Conceptual Whole

Only toghether, the one, two or a number of individual attributes, form the complete intended measure or description. "50/EUR" contains two attributes: "50" and "EUR". Separately these attributes describes something else or nothing special. These attributes are a Conceptual Whole that describe a monetary measure. What we care about is not just an amount or just a currency. To properly describe a thing, it must be trated not as two separate attributes, but as a whole value.

namespace Sensorario\PugMi\ValueObjects;
public final class MonetaryMeasure {
   public static function withAmountAndCurrency(
       $amount,
       $currency
   ) {
       return new self(compact(
            'amount',
            'currency'
       ));
    }
}

Each attribute (necessary) is defined! A Value Object is a sort of defined state. A Value Object is a container of attributes.

$person = new Person();
$person->name('Simone');
$person->company('Onebip');

The name of attributes must reflect this characteristic.

MonetaryMeasure::withAmountAndCurrency(50, "EUR");

Replaceabiliy

When an entire value is replaced, and still represent the currently correct whole.

3 is a number. 42 is a number. 42 could completely replace 3. Che conceptual whole is "the answer". We'll not alter 3 value. We change total value to 42.

        namespace Sensorario\Pug\ValueObjects;

        $answer = 3;
        $answer = 42;
    

We have replaced the entire value. This is the point of Value Objects. The point of replacement. Is not an oversemplification. It is exactly what replacement does even when Value Object type is more complex than an integer.

        namespace Sensorario\Pug\ValueObjects;

        $amount = "3/EUR";
        $amount = "4/EUR";
        $amount = "3/USD";
        $amount = "42/EUR";
    
        namespace Sensorario\Pug\ValueObjects;
        $fullName = FullName::fromSurnameName("Gentili", "Simone");
        $fullName = FullName::fromSurameNickAndName("Gentili", "Demo", "Simone");

        $fullName = FullName::box([
            'surname' => "Gentili",
            'name'    => "Simone",
        ]);

        $fullName = FullName::box([
            'surname'  => "Gentili",
            'nickname' => "Demo",
            'name'     => "Simone",
        ]);
    

Use a method to change the state of che concept "full name" should violate the immutability. Instead of change the value, we replace the previous fill name with the noew one. An entity should use our new Value Object to set the full name of Person class.

Replacement is not practical. But replacement also responds to another characteristics of Value Objects: Side-Effect-Free Behavior. Each time we make some operation with a value object, if the kind (the class) is the same, the instance is totally renewed.

Equality

If both types and their attributes are equal, the Value are considered equal. Further, if any two or more Value instances are equal, you could assign any one of the equal Value instances to an Entity's property and the assignment would not change the value of the property.

Ask yourself if the concept you are designing must be an Entity identified uniqeuely from all other object or if it sufficiently supported using value equality. If the consept itself doesnt require unique identity, model it as a Value Object.

        namespace Sensorario\PugMi\ValueObjects;

        $a = new Currency('USD');
        $b = new Currency('USD');

        var_dump($a == $b);  // bool(true)
        var_dump($a === $b); // bool(false)

        $c = new Currency('EUR');

        var_dump($a == $c);  // bool(false)
        var_dump($a === $c); // bool(false)
    

Side-Effect-Free Behavior

"Since no modification occurs when executing a specific operation, that operation is said to be side-effect free." All methods of a Value Objects are side-effect free, because they must not violate the immutability characteristics. We might see Value Objects only as attribute container

Pure functional programming languages allow nothing but side-effect-free behavior, requiring all closures to receive and produce only immutable value objects.

        namespace Sensorario\Pug\ValueObjects;

        $fullName = new FullName("Gentili", "Simone");
        $fullName->withNickname("Demo"); // modifiche perse
        $fullName = $fullName->withNickname("Demo");
    

The method withNickname() must respect the side-effect-free rule. Value Objects are immutable. Thus, the method will generate new container of attributes. Thw use of methods withSomething(), make all more expressive. The method will be implemented in this way:

        namespace Sensorario\Pug\ValueObjects;

        class FullName {
            private function __construct(array $params = []) {
                // ...
            }
            public static function withNickname($nickname) {
                return new self([
                    'name' => $this->name(),
                    'surname' => $this->name(),
                    'nickname' => $nickname,
                ]);
            }
        }
    

Method must not modify the state. Every function will generate new value.

This method also capture important business logic.

When we pass an entity as a function parameter, we must not modify that entity. Why? the side-effect free does not regard only Value Object: it also regard the other domain parts. If we need to change the state of the entity, function can take the entity as input and return something the entity could use to modify itself, on its own terms and responsibility.

Standard Types are descriptive obejects that indicates the type of things. Synonimous od standard type are called type code or lookup. PhoneNumber is a value. But phone number hide an information about the type. We need to describe it as personal number or work number. These descriptions represent standard type.

In case of currency, standard types could be EUR, USD, ... and so on. Using a standard type helps avoid bogus currencies. Use a Value Object helps development avoiding bogus in values.

Depending on the level of standardisation: type could be defined in a txt file, exel, ... OR accepted as international standards.

        namespace Sensorario\PugMi\ValueObjects;

        final class HTTPStatusCodes {
            const OK        = 200;
            const NOT_FOUND = 404;
        }
    

There are several way to test code. To test a single class, we have unit tests. To test a group of object collaboration, we have functional tests. At the end, to test the whole application, we have end-to-end tests.

In functional tests, we use to mock collaborators. But what happens during a functional test, where collaborator is a Value Object? Need we to mock its behavior? I dont think: because of Value Objects characteristics, we dont (IMHO). Value objects, returns alwais same results. Two value objects are equals if their values are equals.

There is no reasons to mock a value object. Value objects, are immutable so just create new instance and use it. Value object is a container of values.

Value cannot change their state. So, 42 is always 42. 21 May 2015 is always 21 May 2015. So we cant mock a value. It is easier and less error prone, to use a real type. A value should be so simple that there is no benefit to mock them. Mock objects, aims to mime real objects.

Now let's talk about open/closed principle. The principle says "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification". "Closed for modification, in this context, means that when your code exposes some behavior to the outside world, that interface should be stable. Providing an API is a responsibility: by allowing other code to access features of your system, you need to give certain guarantees of stability or low change frequency. The behaviour should be deterministic. It does not mean your implementation can not change. The changes should not affect outside code.". If a method is private, is removable. If an element is needed only inside an'object, is removable. Hidden attributes, behaviors and methods, ... are removable and closed to change. Nothing depends on it and. "Somewhere, perhaps invisible to you, something might depend on it. Closing it, or even making a small change, will break that code."

If all setter has private scope, there is no opportunity for attributes to be exposed to mutation by consumers.

Fluent interface!

        namespace Sensorario\PugMi\ValueObjects;

        $valueObject = ValueObject::box([
            'attribute' => 'value',
        ]);

        $request = Request::withCode(200);
        $request = $request->andHeader('Referer', 'http://sensorario.github.io');
    

Value Objects and encapsulation. Mmmm. "Functions should have a small number of arguments. No argument is best, followed by one, two, and three. More than three is very questionable and should be avoided with prejudice." But, what hannpes when we need more and more third party class in a static method of our value object? My question is, ... How many third party elements can I accept, and still think this object as a Value Objecr?

        namespace Sensorario\PugMi\ValueObjects;

        use Sensorario\Stuff;

        class MyValueObject
        {
            public static function withEncapsulation()
            {
                $foo = new Stuff('foo');
                return new self(compact('foo'));
            }
        }
    

Whatching Value Objects characteristics, ... this class measures, is immutable, conceptual whole, is replaceable, respect equality and has side-effect-free. This characteristics are respected only if Stuff class has static behavior. Remember that for immutability, if a Value Object is related to an entity and the entity change, also Value Object changes. If this happens, you have not a Value Object in your hand.

There are a variety of ways to persit Value Object instances to a persistent store. We are interested in persisting Values along with the state of the aggregate instances that contain them. These examples are based on the assumpion that an Aggregate is beign added to or read from its Repository, and its contained Values are persisted and reconstituted behind the scenes along with the Entity that contains them.

ORM such Doctrine2 is popular and widely used. Using an ORM to map every class to a table and every attribute to a column adds complexity, which may be unwarranted.

Most times that a Value Object is persisted to a data store, it is stored in a denormalized fashion. Its attributes are stored in the same database table row as its Entity object. This makes the storage and retrieval of Values clean and optimized and prevents any persistence store leakage into the model. It's both a pleasure and a relief when Values can be persisted this way.

There are times when Value Objects in the model will of necessary be stored as an Entity with respect to a relational persistence store. When persisted an instance of a specific value object type will occupy its own row in a relational database table that exists specifically for its own row in a relational database table that exists specifically for its type, and it will have its own database primary key column. This happens when, when supporting a collection of Value Object instance with ORM. In such cases the Value type persistent data is modeled as a database entity.

Is this an indication that the domain model object should reflect the design of the data model and be an Entity rather.

Persisting a single Value Object instance to a database is straightforward.

Interesting Github repositories

Suggested Books

Links