5 ORMs and the Database

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Introduction

In the early 2000s we wrote all our SQL queries by hand. This was eventually recognized as a security hazard. We learned about using prepared statements. PHP introduced PDO, as a way to abstract away some database vendor-specific things that we did in our code. It turned out that PDO definitely wasn’t a complete database abstraction library. There were still many aspects of abstracting the database that Doctrine DBAL (Database Abstraction Layer) was able to add. Meanwhile, programmers wanted to move to a more object-oriented style of programming. We needed a way to write their domain logic in classes, but still save the state of domain objects in database tables. In other words, we wanted to map objects to records in a relational database. Hence, the name ORM - Object-Relational Mapper. The ORM abstracts the work that needs to be done to “save an object to the database”. But that’s not all, the ORM also needs to retrieve records and reconstitute the original objects. Nowadays, ORMs also handle schema migrations for you.

Since ORMs do so many things, we often don’t realize how much our domain model is coupled to it anymore. We only notice this when the need arises to migrate to a different ORM, because the old one uses out-dated programming techniques and design patterns, or is simply no longer maintained. We also don’t realize how much the ORM itself is still coupled to the relational database. It may abstract from a specific database vendor, but it has not abstracted from persistence to tables. Using the same ORM, it’s impossible to start saving object as, for instance, JSON files on disk, or to retrieve them from ElasticSearch. This shows how a domain model is often tightly coupled to the ORM and the database itself.

To find out how we can decouple from an ORM, we should indeed consider what happens if we remove it. Then we should consider how much work is needed if we want to switch from a relational to, say, a document database.

When scanning a code base for places where the model is coupled to the ORM, we’ll find the following use cases for the ORM:

  1. In some parts of the application we create an object and then save it to the database. Sometimes we load an existing object, modify it, and again, save it to the database.
  2. In other parts we load one or more objects from the database and show parts of the object on some kind of view (e.g. an HTML page, a JSON response, an email body). We don’t need to modify the object here, nor do we want to save anything to the database. We’re only interested in retrieving and presenting information.

We can summarize these two basic use cases in a more abstract way, as the “needs” of our application. We need to:

  1. Persist a domain model object, and to retrieve that object by its ID, so we can make further changes, persist it again, etc.
  2. Query some data for the user to view.

I’ve made these descriptions somewhat abstract on purpose: they don’t say anything about the source of the data itself. Does it come from a database? And if so, a relational, or a document database? It doesn’t matter. Our application just needs these abilities, and we have to implement them somehow. Defining our needs as abstractions is a way to decouple from their underlying technical implementations. We first look at how to properly abstract our code so that the first need is addressed in a decoupled way. For this we’ll need a repository, application-generated identifiers, and non-cascading updates.

Repository: an Abstraction for Persistence

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Trying an Alternative Implementation

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Application-generated IDs

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

PHPStan Rule: Disallow Auto-incrementing Model IDs

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Defining Our Own Object API

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Custom Mapping Code

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

PHPStan Rule: Only Allow Calls to fromDatabaseRecord() from Repository Classes

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Using Aggregate Design Rules

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Limiting Changes to One Entity

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Introducing View Models

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Provide Read Models When the Framework Needs Your Data

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Use Domain Events Instead of Persistence Events

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

PHPStan Rule: Forbidden Parameter Types

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.

Conclusion

This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/recipes-for-decoupling.