UUID over auto increment in PHP

We know that our Domain should not be dependent on our database implementation. Then why do we use auto increment as a default solution? Why not UUID?

Ask yourself the following questions. Why do we let our database implementations decide the identity of our domain objects? Why does our database decide what ID our user has?

This dependence on the database for an ID has its faults.  We can not create a valid Entity until we connect with the database and ask for an ID. We can’t correctly identify an Entity before we have connected to the database. This means we can not create Entities client side and be sure there are no duplicates.

And when we create public API’s. And show these auto incremented ID’s to the world. We unintentialy expose to much information about the size of your database and business to the outside world.

UUID to the rescue

We can use a UUID (Universally Unique IDentifier). With this we can hide information about the size of your database. And it can be generated everywhere and is always unique.

Let’s sum up the reason’s why to use an UUID over auto increment:

  • An UUID is universally unique. This means its not unique in the scope of a database table like an auto increment. But it is unique in the scope of the whole world. This UUID only exists once across all software systems in the world.
  • Your Entity (Domain Object) is always in a valid state. You can create an UUID on construction of your entity, before making any database call. This because its always unique. We don’t need to check any database to be sure of this.
  • Because of this, your application is also more decoupled from your persistence layer.
  • You can easily migrate or merge database information between different tables and databases. Because every record has a globally unique identifier, there won’t be any ID conflicts.
  • The primary ID can also not be abused for sorting. With auto increment you can sort by id to get the latest or first records in a database. But this is an abuse of logic, and might not always have the desired results.

There might be. And I’m pretty sure there are more reason’s to use UUID over auto increment. But there might still be one important question. What about performance and storage?

An UUID looks something like this: a28da54d-847d-4633-b1c8-fddcc0a2916c

Yes it takes up more space then a auto generated ID. And yes its slower on INSERT then an auto incremented ID. The database engine needs to look up to make sure your UUID that you inserted is actually unique.

Also debugging an UUID key is a bit more cumbersome then a simple integer ID.

So be aware of its advantages and disadvantages.

Implementing UUID

In this example I will show you how to implement UUID in your Domain Objects. We will be using Symfony and Doctrine here.

For generating the UUID we use the ramsey/uuid package. In our Entity we create the UUID on construction. So once we create a new entity. This entity will already have an ID before interacting with the database.

class User
{
    /**
     * @var Uuid
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    public function __construct()
    {
        $this->id = Uuid::uuid4();
    }
    
    // Getters and Setters
}

Now how to we map this to our database with Doctrine?

In XML we define our id field as type uuid.

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                   https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
    <entity name="App\Domain\Model\User\User" table="user">
        <id name="id" column="id" type="uuid"/>
        <field name="name" type="string"/>
    </entity>
</doctrine-mapping>

Or if you use annotations. Then you can use @ORM\Id to define your $id is an identifier. And then add @ORM\Column(type="uuid"). There is no auto generated feature to be enabled here.

What if you don’t have an ORM like Doctrine?

For your ID you need a primary key field in your database with char(36). Don’t enable auto increment here!
But do look for better and more optimal ways to store your UUID.

Conclusion

The most important message I want to give here is.  Don’t blindly use auto increment or even UUID. Know both solution. Know its advantages and disadvantages. Make a decision based on the needs of your application. No one is saying you need to exclusively use the one or the other. You can also use both at the same time. You could use an auto incremented ID together with a UUID.

Don’t blindly decline the use of UUID. Try it out, experiment with it. Run some benchmarks if performance is a concern. This also means don’t blindly use auto increment.  As always, think before you code!