7 Patterns to Refactor JavaScript Applications: Value Objects

June 9, 2014 | By Michael Phillips, Engineer

On October 17, 2012, Bryan Helmkamp, founder of Code Climate, wrote a blog post outlining 7 patterns to refactor fat ActiveRecord models in Ruby on Rails. Here at Crush & Lovely, this post is a core reference for all Rails developers on how to separate concerns, write modular, concise and expressive code, and make testing exceedingly simple.

This series of posts demonstrates these concepts in the JavaScript environment; they are no less applicable to data models in JavaScript, and are equally as valuable. Each week, one of the seven patterns will be explained. This week, we’ll be talking about Value Objects.

Patterns

  1. Service Objects
  2. Value Objects
  3. Form Objects
  4. Query Objects
  5. View Objects
  6. Policy Objects
  7. Decorators

Value Objects

Bryan’s article describes Value Objects as “…simple objects whose equality is dependent on their value rather than an identity.” Because JavaScript adheres to the “pass-by-reference” principle for all JavaScript objects, there are no native examples of this in ECMAScript 5 or even Harmony, save for primitives. For example:

The first example assigns primitive integers to the variables foo and bar, which are equal by their value, even though primitives are technically still objects in JavaScript. The Number constructor, even though it provides a wrapper for primitives, is a Plain Old JavaScript Object, and is therefore equal by reference, not value. Thus, in the second example, foo does not equal bar, even though both Number instances represent the same integer value.

But Value Objects offer a great place for domain logic to reside. Almost every value in your application has logic associated with it, such as equality, and the best place for that logic is on an instance of a value object.

Example

Consider a student grading application, where students aggregate percentage scores are used to assign letter grades and determine whether or not the student is passing or is improving.

This has the added benefit of making your code base much more expressive, allowing you to write code such as:

A couple things to note about integrating Value Objects into an application:

  • The valueOf and toString methods have special purposes in the ECMAScript specification, and are suggested for all custom Value Objects. Using the above Grade object, we have enabled it for standard ECMAScript syntax using the valueOf method we defined, giving us:

Even if two separate objects return the same value from valueOf, they will still not evaluate as equal using ===:

For converting your value object using JSON.stringify, the convention is to specify a method toJSON that returns the value you want stringified. If no toJSON method is specified, JSON.stringify will evaluate the valueOf method. If no valueOf method is defined, the object will evaluate as an object, which is almost certainly undesired.

It is a good pattern to have the valueOf method return the same value that the object was initialized with, so that you can rebuild the object on the other end of the transport. This is particularly useful if the application has both a client-side and server-side application and share Value Objects. If you have the input and output use the same value, you can work with a Value Object on the server-side, send down the value to the client using valueOf, and then rebuild it on the client-side again.

If you prefer a more functional programming approach to Value Objects, you can add methods to the constructor function instead of the prototype. Consider the following example:

Both the object-oriented approach and the functional approach are valid, it just depends on your particular style.

Testing

Because this pattern centralizes logic into a single object, testing becomes much easier and quicker, and allows a small suite of tests to cover a lot of application logic. Consider the following tests:

One benefit of testing a value object like this is that the setup for testing couldn’t be easier. Testing multiple permutations is quick and efficient, allowing you to avoid building fake models or writing complex logic. Additionally, the logic is isolated from the any model tests, so the test suites are smaller and more focused.


In the next post, we’ll take a look at Service Objects, which are great tools for isolating procedural code.

Special thanks to Justin Reidy and Anisha Vasandani for editorial and code review.