Another rant about ORMs

ORM are wonderful pieces of technology. They tackle a thorny problem and solve it well in many situations. If you start building a web app, I would advise you to rely on the ORM to simplify access to the database. It will allow you to save a consequent amount of time, preventing the burden of mapping data. And even delay the decision of the data store you will use.

Sadly though, it won't solve all your problems.

SQL is very expressive

Once you've chosen PostgreSQL as a data store, why would you restrict yourself with the ORM's API?

SQL language is actually good. So much so every modern big data system includes a SQL dialect to access data.

I'm currently working with Laravel, and I'm not so OK with its ORM. Using SQL, if you do not want to rely on operator precedence (which has precedence between OR and AND?) with parenthesis. Not so easy with your ORM.

Much funny, a generated query changed when I updated to Laravel 8 (a change not documented, of course).

Worse, using ORM entities like explained in framework tutorials leads to leak data access in the whole app.

The SELECT N+1 problem

It's never funny when you unexpectedly face a SELECT N+1 problem while serializing entities to send them back in HTTP Response. I faced this kind of problem on a legacy project I worked on recently. It did not end well.

The usage depends on your context. Decisions to rely on lazy loading or eager loading depends on the data you want to receive. When I saw the demo of Ruby On Rails, consisting in coding a blog in 15 minutes, I was so amazed. Yet we do not work on blogs with articles linked with comments and author. When you have more levels relations things get worse. With SQL you can choose to prioritize queries on roots or on leaves of your aggregates. Why rely on 2 nested loops when you can have all needed data in 1 request ?

Is there room for business logic?

Putting business logic in ORM entities is tempting. But how do you test it? You'll eventually be forced to use a database and to mount the whole framework. Your test suite takes longer to run and your tests become harder to implement.

So what can you do? I propose the hard and ugly way. You can start with the ORM, but hide all the ORM entities behind a repository class. Map your aggregates to pure objects that implements your business logic. To be simple, consider your ORM as a syntaxic sugar over plain SQL. When you'll need to tune your database access, you can easily decide to access your data through SQL statements without touching your business logic.

Your strategy to gather data can depend on the context, and you won't be surprised by a SELECT N+1 issue.

You can test your business logic using pure unit tests.

Posted on 2021-04-19 at 22:00

Tags: design

Previous Back Next