Domain Driven Design Auto Incremented Entity Key
Asked Answered
S

3

9

Just starting with Domain Driven Design and I've learned that you should keep your model in a valid state and when creating a new instance of a class it's recomended to put all required attributes as constructor parameters.

But, when working with auto incremented keys I just have this new ID when I call an Add method from my persistent layer. If I instanciate my objects without a key, I think they will be in a invalid state because they need some sort of unique identifier.

How should I implement my architecture in order to have my IDs before creating a new instance of my entity ?

Salto answered 16/12, 2015 at 15:15 Comment(1)
I believe this is an answer to your question as well: https://mcmap.net/q/602834/-ddd-entity-identity-before-being-persistedFootsie
P
14

Generated Random IDs

The pragmatic approach here is to use random IDs and generate them before instantiating an entity, e.g. in a factory. GUIDs are a common choice.

And before you ask: No, you won't run out of GUIDs :-)

Sequential IDs with ID reservation

If you must use a sequential ID for some reason, then you still have options:

  • Query a sequence on the DB to get the next ID. This depends on your DB product, Oracle for example has them).
  • Create a table with an auto-increment key that you use only as key reservation table. To get an ID, insert a row into that table - the generated key is now reserved for you, so you can use it as ID for the entity.

Note that both approaches for sequential IDs require a DB round-trip before you even start creating the entity. This is why the random IDs are usually simpler. So if you can, use random IDs.

DB-generated IDs

Another possibility is to just live with the fact that you don't have the ID at creation time, but only when the insert operation on the DB succeeds. In my experience, this makes entity creation awkward to use, so I avoid it. But for very simple cases, it may be a valid approach.

Part answered 16/12, 2015 at 17:9 Comment(1)
With the DB-generated IDs approach, I will have an entity key of 0 (if using integer IDs) until the entity is inserted. This means if I am adding bunch of such entities, all would be having a key of 0. Wouldn't that be against the principle of DDD where each entity needs to be unique?Amaral
D
0

IN adition to theDmi's comments

1) You can in your factory method make sure your entity gets stored to the database. This might or might not be applicable to your domain but if you are sure that entity is going to be saved that might be a valid approach

2) You can separate the ID from the primary key from the database. I've worked with a case there something was only an order if the customer payed and at that point it would be identified by it's invoice id (a sequentual ID). that doesn't mean in the database i would need an column ID which was also the primary key of the object. You could have a primary key in the database (random guid) and till have an ID (int?) to be sequentual and null if it hasn't be filled yet.

Dennadennard answered 17/12, 2015 at 14:24 Comment(0)
S
0

Well, i usually to use it like this:

I have a value object ID mutable:

// Value Object id.ts
import IValueObject from "@shared/domain/implementation/IValueObject";
import CustomError from "../service/custom-error";

export default class Id implements IValueObject<Id> {
    constructor(private id: string) {
        this.id = String(id)
        if (!id) throw new Error('ID not provided');
    }

    value() {
        return this.id
    }

    equals(vo: Id): Boolean {
        return this.value() === vo.value()
    }

    compare(id: string) {
        return this.equals(new Id(id))
    }

    static generate(): string {
        return 'temp_' + Math.random().toString(36).substring(2);
    }

    isGenerated() {
        return this.id.startsWith('temp_');
    }

    setRealId(id: string) {
        if (this.isGenerated()) {
            this.id = String(id);
        } else {
            throw new CustomError("ID has already been set and cannot be changed.", 'implementation failure');
        }
    }

    static build(id: string | Id){
        if(id instanceof Id) return id;
        return new Id(id)
    }
}

in the my entities i have a method create to create a entity instance that is new data, so, on the create method i make my id with Id.generate() and in the save method on repository, i call the entity method setId, to set the auto increment id, it replace the random id, like this:

setId(id: string){ this.Id.setRealId(id) return this }

them, all another entities and aggregate root tha make reference with my entitiy Id, will go replace too, because have the same instance.

Stepmother answered 21/9 at 3:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.