Why I Don’t Use Value Objects in Laravel Anymore

My other post talked about how to implement Value Objects in Laravel with Eloquent. While it’s possible technically, I don’t think Value Objects are useful in Laravel at all.

Here’s why.

As long as you use Eloquent and have your models inherit from Illuminate\Database\Eloquent\Model, you’re basically signing up to the idea that your models are merely the code representation of your table structure.

The Model is tightly tied to the underlying database structure. Heck, the model is the database. You must accept the database as The Source of Truth for your models and thus, for your entire domain.

Nothing wrong with that generally speaking, since this is the idea of the Active Record Pattern after all.

But this is not what Domain-driven Design is about and we must take concepts of DDD into account, since Value Objects are directly taken from it.

In Domain-driven Design, there is the concept of Persistence Ignorance. You want to reflect the domain model with code. Thinking in terms of the database layout comes later.

We want: App is separate from Eloquent + DB = Persistence Ignorance

If you’re like me and if you are familiar with Laravel, the first thing you probably do when you fire up a new app is to think about tables and table relationships and start to write your first migrations.

Again, nothing wrong with that, but it’s not Domain-driven Design.

Actual Problems with Value Objects in Laravel

Let’s take a look at actual code.

Say, you implemented an EmailAddress Value Object as I did in my last post. You can now only do this:

$user = new User;
$user->email = new EmailAddress('foo@bar.com');

Thing is, what you can still do and what you cannot prevent easily is this:

$user = new User;
$user->bananaKingName = 'Donkey Kong';
$user->wantsBratwurst = true;

Only when you try to save this, Laravel will throw a QueryException. But until you save it, you can pass around a model with nonsensical attributes in your code.

Even worse is this:

$user = new User;
$user->save(); // all empty

This won’t even throw an Exception but will simply create an empty row in your database.

So, what’s the problem here? The idea of Value Objects is to keep your domain model consistent within code. It simply shouldn’t be possible to assign random garbage to your entities, especially if you’re working on a larger team. Now you have parts that suggest that the domain model is enforced through types and class methods (i .e. EmailAddress), but other parts aren’t enforced. This makes the entire code base inconsistent.

You could throw out Eloquent as your base model and go with some kind of repository-style abstraction plus Laravel’s Query Builder, but then you’d also lose other core features of Laravel such as Validation. Validation (such as the unique validator) requires access to the database.

Furthermore, if you use Value Objects, Validators are a bit of a pain to implement, because you must set them up manually and tell them how to cast your Value Objects to native types and back.

If you decide to use Laravel as the framework for your web app, you either use all of its power and also its drawbacks or you choose another framework altogether (maybe Lumen, which comes with Eloquent disabled).

More thoughts

Laravel has no Model Validation

Even if you use Laravel’s Validation service in your controllers, a programmer can still create objects which aren’t consistent within the Domain Model.

To prevent this, you’d need validation in the model. A reply in this interesting discussion on Laracasts says:

Model validation and form validation are separate concepts, and you should have both in place. Models should validate their own data to make sure they’re not setting garbage into the database or being malformed.

So, in reality, Eloquent’s Model looks more like this:

As long as you use Eloquent as your base model for your Entities, business logic, the ORM and the database aren’t separated but depend on each other.

By the way, there is an old Laravel 4 Model Validation Package, which tries to bring model validation to Eloquent.

Repositories to the Rescue?

Maybe you think you could separate the Domain from the database by using Repositories. After all, this is a huge topic in the forums of Laracasts and Jeff made a good video about how people take this concept way too far.

The thing is: As long as you extend Eloquent’s Model, using repositories is entirely pointless. Read this very interesting discussion about Models, repositories and Eloquent.

Eloquent is an Anti-Pattern

If we talk about Active Record, we must think in terms of tables and native types, because that’s what the database talks about. So it makes sense that my approach to Value Objects (see other post) uses Mutators to set the native PHP type of a value object.

In Laravel, Eloquent’s Model tries to fulfill both, being the application’s core model and implementing Active Record at the same time by sitting somewhere in between. It has a lot of responsibility and again, has nothing to do with Domain-driven Design.

Laravel's Eloquent Model
The ideal: Eloquent is between (and independent of) the app and the database.

What I’m Doing Now

Value Objects don’t offer any benefit over native types in Laravel. I set up solid controller-level validation and try to keep my models slim and put core behavior in plain old PHP classes. But then, I didn’t use Laravel for large projects yet.