Value Objects

What’s a value object and when should we use one? Patterns of Enterprise Application Architecture says:

The key difference between reference and value objects lies in how they deal with equality. A reference object uses identity as the basis for equality—maybe the identity within the programming system, such as the built-in identity of OO programming languages, or maybe some kind of ID number, such as the pri- mary key in a relational database. A Value Object bases its notion of equality on field values within the class. Thus, two date objects may be the same if their day, month, and year values are the same.

We’ll be talking about value objects at todays developer discussion in Boston. As usual, I’ll post notes following that discussion, but chime in on the topic anytime. Here’s some reference material:

In today’s dev discussion, we first talked about what a value object is, what types of code in your application make for good extractions to value objects, and how we would implement value objects.

What is a value object?

We all agreed that the most important tenet of a value object is that equality must be based on the value the object represents not on object or database identity. This doesn’t mean you must implement the == method. If that’s not important to your interface, you don’t need to implement it.

We discussed the idea that value objects should be immutable. Allowing a value object to mutate is dangerous, though in Rails we tend not to worry about this at the implementation level. Personally, I tend to think of these things as immutable by agreement. We hypothesized that this might be due to the extremely short lifetime of value objects in our rails application.

@jferris’s note on the discussion topic also mentioned that value objects should not have behavior. We discussed what would actually constitute behavior. That is, if you add formatting to (to_s, to_html) to your value object, are you adding behavior? We decided that methods relying only on the value represented by the object and had no external side effects were okay for a value object.

In the classic example of a value object representing money, a value object representing $100 USD could know to print itself as $100.00 but it probably should not know how to convert itself to pesos. The value of $100 knows that it’s 100 dollars - it does not know how many pesos it is.

** What types of things make for good value objects?**

  • Parameter objects - A set of parameters passed together to a method. These are easy to spot when you pass them to multiple methods, but can also make sense when a group of parameters is passed only to one method. Can you name that group?
  • View Object - something you’re passing to a view for rendering.
  • Money, Addresses, etc

** How do you implement a value object?**

The simplest way is probably with a struct:

Point = Struct.new(:x, :y)

Structs, however, are mutable and are allowed to be instatiated with fewer than the number of required parameters (Point.new(1)), which sets the remaining parameters to nil. Extracting a class allows you to overcome the initialization issues and give you a convenient place to try to overcome mutation issues (you have to write the constructor, so you might as well clone there).

The values gem gives you a struct-like creation while overcoming the mutability and initialization issues. This led us to wondering why we often find ourselves unwilling to pull in small libraries such as this which seem like a clear win if you were ever planning on writing a struct. Perhaps a ruby thing? A fear of dependencies (particularly those with more dependencies)? In any event, we took a dive into all 45ish lines of the values gem and learned some interesting things that were quite out of scope for the discussion. It’s worth a look.

2 Likes

I started using value objects after reading the (increasingly famous) CodeClimate blog post about patterns for refactoring AR models: 7 Patterns to Refactor Fat ActiveRecord Models | Code Climate I have been really happy with the effect it has had on my coding.

They are really useful when you need to compare concepts that aren’t just straight database fields!

My note about not having “behavior” was pretty vague and poor. I agree that not having side effects is the important part. I also think Value Objects shouldn’t have external dependencies, like reading from files or databases. Methods on value objects should depend on internal state.

@geoffharcourt Are you using some gem or writing your own Value Objects?

I haven’t tried using a gem yet, they have all been built from scratch.

The use case where value objects were most helpful for me was in creating standings for competitions that needed to be compared. Teams accrued points, but there was also a win-loss-tie record, with some further tiebreakers in certain situations, etc. Using the value object for sorting purposes made it much simpler to build, maintain, and evolve that logic (and easy to test!).

@benorenstein @jferris could we have a chance to bring this topic into weekly iteration?

1 Like

I can’t promise when, but it’s likely that Value Objects will be the subject of a future episode.

Until then, we do have an exercise to practice refactoring to a Value Object: https://whetstone.thoughtbot.com/exercises/10

1 Like

Here another gem that have value objects functionality and other stuffs too. Virtus. I’ve been using value objects for a while now a couple of days ago I found myself with a form and some defaults values to be displayed as part of a drop down at first I added a new method to the model in question and put the values in an array there but later I just switch that to what I think is a value object it just wraps the array and the values inside it and have a convenient method to be call on it like ```Statuses.to_a```` and it just returns it’s values what do you guys think about this approach?

I will throw another vote behind TCrayford’s Values Gem

Definitely one of my favorite tools in the box.