Value Objects are my new favorite thing. We’re all familiar with Entities. Entities are objects that have an important sense of identity. Like entities, value objects also live in the model layer, but, unlike entities, they don’t have that same sense of identity. A very practical way of looking at it is that value objects don’t get their own row in a database, they usually get represented in a single field of some other object’s row.

Why are value objects so awesome? Two big reasons: first, they have a distinct type; and second, they can give a home to behavior that doesn’t otherwise have a place. Let’s go in a little deeper.

Types

In my last blog post, Tests and Types, I talk about how useful it is to have the computer checking that the right objects are going to the right places. This is a simple extension of that. Darren Hobbs calls it Tiny Types, and it’s a simple class that lets you wrap some existing type (usually a Foundation type like NSString or NSDate) in a class that has its own type. That way, you can never put a FirstName object into a slot for a LastName type.

I made a quick gist with an implementation for Objective-C that implements -isEqual:, -description, -compare:, -hash, and NSCopying, which you can find here: https://gist.github.com/khanlou/170cb132c90c86f68e72. From, there, it’s trivial to make subclasses for your value objects. For example:

SKFirstName.h :

@interface SKFirstName : SKValueObject

- (instancetype)initWithString:(NSString*)firstName;

@property (nonatomic, readonly) NSString *firstName;

@end

SKFirstName.m :

@implementation SKFirstName

- (instancetype)initWithString:(NSString *)firstName {
    return [self initWithBackingObject:firstName];
}

- (NSString *)firstName {
    return self.backingObject;
}

@end  

That’s all the code you need for a new tiny type! Now a FirstName object will never accidentally go into the slot made for a ServerHostname.

Behavior

The other great thing is that behavior around this name now has a great home. Instead of doing validation on the Person entity, we can just add it as a method here, optionally returning nil in the initializer if we get malformed input.

- (BOOL)isValid {  
	return [self.firstName matchesRegex:@"^[A-Z][a-zA-Z]+$"]; //this is a terrible regex for a name  
}  

Coupling behavior with state is the whole point of Object-Oriented Programming!

- (NSString *)slug {  
	return [self.firstName lowercaseString]; //probably also want to convert spaces and apostrophes to hyphens  
}  

In the next post, I’ll talk about how we can use the same concept to make way better enumerations.