Tony Marston's Blog About software development, PHP and OOP

Value objects are worthless

Posted on 24th June 2024 by Tony Marston
Introduction
What are Value Objects?
Arguments FOR Value Objects
Value Objects should always be in a valid state
Value Objects can self-validate
Value Objects are immutable
Value Objects aid readability and maintainability
Value Objects reduce primitive obsession
Value Objects can be compared
Entity and Service Objects
Value Objects in PHP
Single-value objects
Multiple-value objects
Value Objects vs DTOs
Arguments AGAINST value objects
Everything is not an object, it's a string
Values are invalid until they pass validation
Values cannot self-validate
Values are not immutable
Value Objects decrease readability and maintainability
Values reduce object obsession
Values can be compared without being objects
Conclusion
References
Comments

Introduction

I have been designing and building enterprise applications for over 40 years. For the first 20 years I programmed using COBOL and UNIFACE, but since 2003 I have been using PHP with its Object Oriented (OO) capabilities. I have built a development framework in each of those three languages specifically to assist in the building of these applications, and with each new language my productivity has increased. Since switching to PHP I have learned how to use encapsulation, inheritance and polymorphism, and to use patterns such as Model-View-Controller and the Template Method. My current ERP application, which was built with the assistance of my RADICORE framework, contains over 4,000 user transactions (use cases) covering over 400 database tables.

I have written more than my fair share of entity objects (Models) and service objects (Views and Controllers), but never any value objects. Why not? Because PHP does not support them. By "support" I mean "natively support". If you search the PHP manual for the term "value object" you will find nothing. Nada. Zilch. Zero. Naff All. Sweet FA. In PHP all variables are simple primitive data types or scalars.

However, in the past few years I have encountered more and more articles extolling the virtues of value objects in PHP saying that they are the greatest thing since sliced bread. I have studied some of these articles, measured the effort required to convert scalars to value objects against the benefits obtained, and have reached one inescapable conclusion - they are not worth the effort, they are a complete waste of time. In the remainder of this articles I shall explain why.


What are Value Objects?

Look at this wikipedia page for a common definition of a Value Object.

Below are some other definitions which I found in other pages on the web.

Value Objects are a fantastic concept that we can use to improve our applications. They are small objects, such as Money, DateRange, Email, or Age, that we utilize on complex applications. They are key elements in creating efficient, understandable, and maintainable code.

Value Objects are characteristically immutable and are measured by state, not identity. Contrary to Entity Objects that have a distinct identity, Value Objects do not possess any unique identifier. Instead, they are entirely defined by their value, meaning that two Value Objects are said to be equal if their values match, regardless of whether they are separate instances.

For example, take two instances of a Money Value Object, one created to represent $10, and another separately instantiated also to represent $10. Although they are two distinct instances, within the application, we consider them equal because they represent the same value.

Value Objects make your code more explicit, readable, and less error-prone. They encapsulate related data together into logical and meaningful concepts and context, making them easier to manage, test, and debug. Understanding and correctly implementing Value Objects can significantly simplify complex business logic and improve the quality of our codebase.


Arguments FOR value objects

Here are some arguments FOR these artificial artifacts.

Value Objects should always be in a valid state

This argument I found in The Beauty of PHP Value Objects where the Cat object cannot be created without a valid name.

class Cat
{
    private string $name;
 
    public function __construct(string $name)
    {
        if (empty(trim($name))) {
            throw new InvalidArgumentException('Name your cat!');
        }
        $this->name = $name;
    }
 
    public function getName(): string
    {
        return $this->name;
    }
}
 
$taco = new Cat('Taco');

If we add other properties to the object, we can validate those, too. The end goal is that once we've constructed the Value Object, we know that it's in a valid state!

Value objects can self-validate

Simple data types don't have built-in checks to ensure the data is valid. This can lead to unexpected issues in our code.

We possibly have a lot of different places in our code that use those values, but we can trust no one, and we have to ensure that our data is consistent. Consequently, we must validate the data passed as an argument every time.

This leads to a validation logic duplication issue. Each of these pieces of duplicated logic could potentially differ from each other, leading to inconsistencies.

Value objects enforce validation rules internally, guaranteeing that the object never exists in an invalid state. This encapsulation of validation logic simplifies the code elsewhere, as the validity of the object need not be questioned once it's created.

Value objects are immutable

One of the core benefits of value objects is their immutability. Once instantiated, value objects cannot be altered. This immutability is a safeguard against unintended side effects, making the code more predictable and easier to debug. In PHP, this is achieved by ensuring that no methods can change the internal state of the object but instead return a new instance if a change is needed. This is shown in the following example:

class Money
{
    private $amount;
    private $currency;

    public function __construct(float $amount, string $currency)
    {
        if ($amount < 0) {
            throw new InvalidArgumentException('Amount cannot be negative.');
        }
        $this->amount = $amount;
        $this->currency = $currency;
    }

    public function getAmount(): float
    {
        return $this->amount;
    }

    public function getCurrency(): string
    {
        return $this->currency;
    }

    public function add(Money $money): Money
    {
        if ($this->currency !== $money->getCurrency()) {
            throw new InvalidArgumentException('Currencies must match.');
        }

        return new Money($this->amount + $money->getAmount(), $this->currency);
    }
}

Value Objects aid readability and maintainability

By encapsulating related values and their operations within value objects, the code becomes more readable and maintainable. It shifts the focus from how things are implemented to what is being represented, aligning the code more closely with the business domain.

Value Objects reduce primitive obsession

Primitive obsession is a common anti-pattern where primitive data types are overused to represent domain concepts, leading to type errors and other bugs. Value objects mitigate this by encapsulating these primitives and their related operations, thus providing a more robust abstraction.

Value Objects can be compared

Comparing value objects goes beyond mere reference equality. Since value objects are defined by their values, two value objects are considered equal if all their fields are equal. As PHP doesn't have a way to override the equality operator, you should implement it by yourself. Implementing an equals method allows for this deep comparison:

public function equals(Money $other): bool
{
    return $this->amount === $other->getAmount() AND $this->currency === $other->getCurrency();
}

Entity and Service Objects

Value objects are not the only type of object which can exist in the OO universe, there are others which are more important. While researching for my article Dependency Injection is EVIL I came across the following:

They both identify the following types of object which may appear in your application:

The RADICORE framework is a combination of two well known architectural patterns which has produced objects which fall into the following categories:

I have an Entity object for each entity which exists in the outside world and which is of use in my application. As I deal only with database applications this means that every table is a separate entity with its own structure, and can be served best by having a separate class for each table. Each table has its own set of columns, and these are defined as properties within the class. Each table has its own set of business rules, so these are also defined within the table's class. This agrees with the similarities which are reported in Relational database entities vs. domain-driven design entities. Where we disagree is on the handling of the differences:

Because I recognised immediately that every table implements the same set of operations/methods I was able to define them in an abstract table class which could then be inherited by each concrete table (Model) class. This follows the advice I found over a decade later in Designing Reusable Classes which was published in 1988 by Ralph Johnson and Brian Foote. This I discuss further in The meaning of "abstraction". This abstract class contains a set of common table methods as well as a set of common table properties, and has enabled me to implement the Template Method Pattern which is used extensively in my framework.

When it comes to dealing with object associations and object aggregations I prefer to deal with then using generic code in the framework rather than custom code within each table class. My library of Transaction Patterns has several which deal with parent-child relationships.

Dealing with variables as primitives has been the natural way since the dawn of computing. Every programming language I have ever used has used primitives. Every value in an HTML document or SQL query is defined as a primitive, every value in my PHP classes is also defined as a primitive. This is reinforced by the fact that PHP has always supported primitives and never supported value objects.


Value Objects in PHP

As value objects are not described anywhere in the PHP manual I had to search the internet for examples. This produced results of varying degrees of usefulness and quality (or lack thereof) which confirmed the following statement which is attributed to a 1996 speech by Robert Wilensky:

We've heard that a million monkeys at a million keyboards could produce the complete works of Shakespeare; now, thanks to the Internet, we know that is not true.

Some of the articles I found are listed in the References section. On the GitHub platform I found several collections of ready-made classes which can be imported into any PHP application.

Single-value objects

When I first heard the term "value object" it was in a complaint levelled against PHP which went along the lines of PHP is not a proper OO language as it is not 100% object oriented - it does not support value objects. The details of this complaint were that each variable was nothing more than a container for a value which did not have any methods associated with it. This meant that you had to perform an external function on a variable, as in:

$string = strtoupper($string);
$string = strtolower($string);
$string = ucfirst($string);

According to the OO fanatics the correct way is as follows:

$string = $string->strtoupper();
$string = $string->strtolower();
$string = $string->ucfirst();

As PHP did not support this idea I dismissed it as the ravings of a lunatic and carried on handling data in the old fashioned way. When I say PHP does not support value objects I mean that it does not have native support for value objects. They are not mentioned anywhere in the manual, so they do not exist. If you want them you have to either create them yourself or install a 3rd-party library like PhalueObjects which contains classes for each of the data types, such as boolean, integer, float/double, string, array, object, resource and null. Each of these only allows for a single value in each object.

Multiple-value objects

Later on I found articles which showed value objects which contained more than one value, such as a Money object which holds both an amount and a currency.

There are other examples such as:

A problem I have with objects which hold multiple values is that they can easily be confused with entities or even DTOs, and this confusion can lead to to misuse.

Value Objects vs DTOs

The article Understanding Value Objects in PHP has this to say regarding Data Transfer Objects:

Some people may get confused when we talk about Value Objects and Data Transfer Objects and sometimes even mix their concepts, but they serve different purposes and have distinct characteristics.

Value Objects (VOs) are objects that represent a specific value rather than an entity. They are typically used to encapsulate a single piece of data or a combination of related data, such as a date, color, or currency amount. They are designed to be used as immutable data containers and are often used to enforce business rules or validate data.

On the other hand, Data Transfer Objects (DTOs) are objects that are used to transfer data between different processes. They carry data from one part of the system to another and are primarily used for communication and serialization purposes, you can also add validation rules to them but they usually never have any logic. They often represent a subset of data from an entity or multiple entities, and they can include additional fields or transformations to meet specific requirements of the communication channel or client.

This distinction between the two is also echoed in Is it a DTO or a Value Object?

The clue should be in the name - a DTO it used to transfer collections of data from one process to another. The motivation for its use is that communication between processes is usually done resorting to remote interfaces (e.g., web services), where each call is an expensive operation. I personally do not use DTOs in web services as I cannot guarantee that the service I am using was written in PHP and can understand a PHP object. That is why all my inter-process communication is done using XML files as they are easily readable by every web site on the planet.

Martin Fowler, the author of PoEAA, had this to say in Local DTO:

DTOs are called Data Transfer Objects because their whole purpose is to shift data in expensive remote calls. They are part of implementing a coarse grained interface which a remote interface needs for performance. Not just do you not need them in a local context, they are actually harmful both because a coarse-grained API is more difficult to use and because you have to do all the work moving data from your domain or data source layer into the DTOs.

DTOs can also be classed as Anemic Domain Models which Martin Fowler described in the following way:

The basic symptom of an Anemic Domain Model is that at first blush it looks like the real thing. There are objects, many named after the nouns in the domain space, and these objects are connected with the rich relationships and structure that true domain models have. The catch comes when you look at the behavior, and you realize that there is hardly any behavior on these objects, making them little more than bags of getters and setters.
...
The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together. The anemic domain model is really just a procedural style design, exactly the kind of thing that object bigots like me (and Eric) have been fighting since our early days in Smalltalk. What's worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.

When it comes to the transfer of data between the various objects in my infrastructure I follow the KISS principle and do the simplest thing that could possibly work (which is also discussed in this article from artima.com) and leave everything in its original form which is an array of strings (but not string objects). All data posted from an HTML form is presented as an associative array, and all data retrieved from the database is presented as an indexed array of associative arrays, so why bother splitting it up into a multitude of getters and setters?


Arguments AGAINST value objects

My starting argument against value objects is that it is not necessary for every value to be an object. This means that all the other arguments FOR them are without foundation and therefore utterly useless.

Everything is not an object, it's a string

This idea that everything is an object originated in Smalltalk, the first object-oriented language, where it says:

Smalltalk-80 added metaclasses, to help maintain the "everything is an object" (except variables) paradigm by associating properties and behavior with individual classes, and even primitives such as integer and Boolean values (for example, to support different ways to create instances).

Note where it hints that values may be objects but variables are not.

Later on it says:

Smalltalk is a "pure" object-oriented programming language, meaning that, unlike C++ and Java, there is no difference between values which are objects and values which are primitive types. In Smalltalk, primitive values such as integers, Booleans and characters are also objects, in the sense that they are instances of corresponding classes, and operations on them are invoked by sending messages. A programmer can change or extend (through subclassing) the classes that implement primitive values, so that new behavior can be defined for their instances - for example, to implement new control structures - or even so that their existing behavior will be changed. This fact is summarized in the commonly heard phrase "In Smalltalk everything is an object", which may be more accurately expressed as "all values are objects", as variables are not.

While Smalltalk might have been built that way PHP most definitely was not. In php every value is a string. PHP was specifically designed to aid in the construction of dynamic web pages which means that it has to deal with HTML at the front end and SQL at the back end. Neither of these technologies support value objects or even data types other than strings. If you don't believe me then consider the following:

The only difference between HTML and SQL is that empty columns are presented as empty strings while in SQL they are presented as NULL values.

It is precisely because all values in both HTML and SQL are strings that PHP was specifically designed to be dynamically and weakly typed, as against being statically and strictly typed, which is why it was given the ability to coerce values from one type to another depending on the context. This mechanism of coercion meant that the developer did not have to insert special code to manually convert values from their original string type into a different type.

Values are invalid until they pass validation

One of the first things I learned many decades ago about writing programs which dealt with input from outside sources is to never trust a user's input. It is so easy for a user to make a mistake, either by accident or design, and enter a value which the program cannot handle and which causes it to abort. Hopefully it crashes just that single process and not the whole operating system, but in every case it is a bad User Experience (UX).

When writing a database application the golden rule is that every piece of data which is destined to be processed in some way, which includes being stored in a database, must be compared with the column's specifications BEFORE that data is sent to the database in order to guarantee that the SQL query will not fail with an invalid value. If you look at the common table methods which are present in every one of my table classes you will see method names such as validateInsert(), validateUpdate() and commonValidation() which perform this validation. Note that primary validation is performed automatically by the framework while secondary validation is performed in one of the customisable "hook" methods.

Note that if any of the validation checks fail then the operation will terminate without updating the database. Instead control will be returned to the user with a suitable error message so that they can correct their mistake and try again.

Values cannot self-validate

The fact that you CAN create an object for each value and perform its validation within that object does not mean that you SHOULD. I always have a one-to-one relationship between entities (tables) in my database and entities (objects) in my software. This means that I have a separate class for each database table. I have always put the code which validates data inside the entity object that deals with each database table, which means that all the validation for that entity's data is contained within a single class and not spread across multiple classes. To do otherwise would, in my humble opinion, be a violation of the principles of encapsulation and high cohesion. Everything concerning a particular database table should go into the (one and only) class which is responsible for (or concerned with) that table.

Values are not immutable

I have never understood the logic behind this statement, or why it has to be treated as a "requirement". In all the decades that I have been programming it has been common practice, in fact the ONLY practice, to update any value as and when necessary. When a value is displayed on a screen it is not immutable, it can be changed at will (unless circumstances require it to be read-only). I have never seen a situation where the original value is displayed in one part of the screen and its replacement value is entered in a different part of the screen. When the contents of a screen is sent to the application it has a single value for each column, and it is up to the software to determine if a column's value has been changed or not.

When a value is held in a database table it is not immutable, it can be changed at will - even if it is within a primary key.

Value Objects decrease readability and maintainability

A good programmer should aim to complete complex tasks using simple code, not to complete simple tasks using complex code

Some people have peculiar ideas on what "readability and maintainability" actually means. When I am reading the processing rules for an object I expect that object's rules to be contained within a single class so that I only need to browse through the contents of a single file. If I have to start switching from one file to another I can quickly loose track of which file I came from and which file I am going to. The more files there are then the more difficult and frustrating it becomes.

If you don't believe me that searching through multiple class files for the business rules for a single entity is frustrating then imagine that you are reading an instruction manual for a process or procedure. You expect that manual to be in a single book, right? You expect that book to be broken down into a series of chapters, right? You expect those chapters to appear in a logical sequence, right? Now imagine that some bright spark has invented a wonderful set of rules which dictates that those chapters be distributed randomly across a collection of different books. His "rules" also dictate that each chapter cannot be greater than a certain size (where that size is roughly equivalent to the IQ of the rule maker), and that each book can contain no more than a certain number of chapters. The end result is that when moving from one chapter to the next you have to mark your place in the current book before you start searching through the other books for the relevant chapter.

If, like me, you find that having the instruction manual for a procedure is more readable when it is contained within a single book then you should appreciate why I think that having the rules for a particular entity contained within a single class is much better than the alternative. Having the processing rules for an entity split across multiple classes, which is what having the validation rules for each value contained within each value's object actually entails, is to me a step in the wrong direction, and it is a step which I am not prepared to take.

It is also obvious to me that simple code is easier to read, understand and maintain than complex code. Whenever I see some of the examples of "clever" solutions being promoted in various blog posts the first thing I have to do is try to work out what the hell it is that they are trying to achieve. That, to me, is time wasted. This usually ends up with the observation that what they have written seems more like a Rube Goldberg machine than a cost-effective solution, that I have already written code to achieve the same result, but that code has been shorter and simpler.

Values reduce object obsession

What exactly does the word "obsession" mean? Here are some definitions which I found on the interweb:

As an example, having a shower once a day is normal, but having a shower ten times a day is obsessive, it exhibits an obsession with cleanliness. Doing what is necessary is normal, doing what is more than necessary is obsessive.

Working with primitives is not an obsession, it is the normal state of affairs when dealing with values. I have dealt with primitives, in both strongly typed and weakly typed languages, for the past 40 years. I don't have to think about them, they are just there. In a PHP application which deals with HTML at the front end and SQL at the back end every value is a primitive, either coming in or going out. Working with primitives does not take any effort, it is as easy as falling off a log.

It is the act of converting each primitive into a value object which takes extra effort, and it is this effort which classifies it as an obsession. Programmers do not do it because it has benefits, because there aren't any, they do it to impress other programmers. It is if they are saying Look at me! Aren't I clever? I'm holier than the Pope. Instead of following the KISS principle (Keep It Simple, Stupid) they are doing the exact opposite by following the KICK principle (Keep It Complex, Knucklehead)

Values can be compared without being objects

The fact that value objects can be compared is totally irrelevant as it is also possible to compare primitives, and has been since the dawn of computing. It is nothing new, it is nothing special, so what is the point of making a song and dance about it? Take the following example, which I have been using for several decades:

function _cm_commonValidation ($fieldarray, $originaldata)
// perform validation that is common to INSERT and UPDATE.
{
    if ($fieldarray['start_date'] > $fieldarray['end_date']) {
        // 'Start Date cannot be later than End Date'
        $this->errors['start_date'] = getLanguageText('e0001');
        // 'End Date cannot be earlier than Start Date'
        $this->errors['end_date']   = getLanguageText('e0002');
    } // if
    
    return $fieldarray;
}

It is simple, to the point, and effective. Why should I waste time in creating a special value object to contain this pair of dates, then create a bunch of methods to deal with every type of comparison that I may need? If the value object can only throw a single error, how can I get it it flag both dates on the screen?


Conclusion

Programmer Productivity takes Preference over Paradigm Purity

Because the internet is open to everybody it is possible for anybody to post an opinion on anything. Some of those opinions might be worthy of further thought while others are not. Just as some people think that what I write about is complete rubbish I think the same about some of the articles which I read. The first two decades of my career were spent working for software houses where we had to compete against other companies for business, and we found that the only way to win new business was to demonstrate that we would be more cost-effective than our competitors. This meant using practices which increased our levels of productivity and efficiency. Unfortunately far too many of today's programmers are being taught "best practices" which concentrate on theories of OO purity which ignore any ideas of productivity and efficiency. They are rules-oriented and dogmatic whereas I am results-oriented and pragmatic.

Productivity is the efficiency of production of goods or services expressed by some measure. Measurements of productivity are often expressed as a ratio of an aggregate output to a single input or an aggregate input used in a production process, i.e. output per unit of input, typically over a specific period of time

https://en.wikipedia.org/wiki/Productivity

Efficiency is the often measurable ability to avoid making mistakes or wasting materials, energy, efforts, money, and time while performing a task. In a more general sense, it is the ability to do things well, successfully, and without waste

https://en.wikipedia.org/wiki/Efficiency

As you should be able to see from the above definitions this meant being able to achieve the expected result with the minimum of effort, which can be directly equated with writing less code. In my long experience there are two ways of writing less code:

  1. The ability to recognise patterns of similarities so that you can put the similarities into some sort of reusable object, such as a reusable function or a template, so that you can spend most of your time on dealing with the differences. This technique is known as programming-by-difference and is discussed in Designing Reusable Classes which was published in 1988 by Ralph Johnson and Brian Foote.
  2. The ability to write only that amount of code which is necessary to do the job as anything more would be a waste.

I consider the use of value objects to be a violation of point #2. I can achieve what I want to achieve without the use of value objects, so I consider any time spent in creating and using value objects to be a total waste of time. There is nothing I could do with a value object that I cannot already do with a primitive, so why should I spend any time in writing extra code that does not provide any benefit?

Another problem I have with value objects, especially when each property has its own class with its own validation rules, is the proliferation of classes which would need to be loaded before an entity object could be instantiated. This sounds like the problem for which autoloaders were invented.


References

Here are some articles on the use of value objects in PHP:

These are reasons why I consider some ideas on how to do OOP "properly" to be complete rubbish:


counter