Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with id's in entity object design

For a while I have been thinking about how to deal with objects which are assigned identifiers by the database.

A typical object representing a table entity may look like:

public class Test
{
    public int Id { get; private set; }
    public string Something { get; set; }
}

Suppose we would like to use this object for inserting, retrieving and updating objects in the database. As for retrieving and updating we have no problems, since the Id field always has a value.

However, if we want to insert a new object of type Test into the database, the Id field will still need to have a value. We could just use "0", as it is unlikely to be used as a database key, but really this is not a good design.

Likewise, if we invert the situation and make the Id property nullable, we could use null for objects which have not yet been assigned an identifier by the database. However, it is now possible for an object retrieved from the database to not have an identifier (as allowed by the class design, but not the database design)

Any good ideas on how to make a good design for this problem?

like image 780
DEHAAS Avatar asked May 17 '11 14:05

DEHAAS


People also ask

Should domain objects have IDs?

Ids in domain entities is a design smell. In most cases, they indicate poor entity encapsulation. If you want proper separation of concerns, you should reduce the number of Ids in your domain entities to as low as possible.

Can value object have ID?

The key difference between an Entity and a Value Object is the identity. Entities contains an intrinsic identifier but Value Objects have no identity.


2 Answers

If you treat id as a way to identify/grant uniqueness to object within your application, this should be handled by database (unless of course, you have other ways to assign identifiers to objects).

If it's not (as in, it's object's property driven by business needs) - whether 0 is valid/good design value or not depends on those business needs purely. Is 0 valid value from say, end user point of view?

You can always wrap your object properties into separate class, if you feel that having objects without ids set around in your application is problematic. You'll use such class essentially only for carrying parameters for not yet created object (creation process is finalized with database insert). Once the object gets inserted, id assigned and stuff - you can work with your regular entity. Will your user go "Oh, snap! What is that?" or "Ok.. I know what to do." once approached by id = 0?

Edit

This question (or rather my answer about wrapping parameters) reminded me of a fact that once surprised me. When a child is born, she doesn't exist in system until her parents officialy register her and she gets personal identification number assigned. So technically, without id - child doesn't exist (at least from system point of view), even tho everybody knows she was born and stuff. It's the same with database/your app - object without id (one that database cannot identify) doesn't exist - it's just a set of parameters. Bit bizzare, but I hope my point is clear :)

like image 83
k.m Avatar answered Oct 04 '22 02:10

k.m


There is nothing wrong with designing a class so that an ID of 0 indicates that the entity has not yet been serialized. I have built systems in the past that successfully used this approach. Just make sure that these semantics are well defined in your API, and that this meaning is respected in all of the code.

One trap to watch out for is using the ID to define an equality relationship (such as for generating hash codes for a dictionary). This must only be done for non-zero IDs. For testing equality with two IDs of zero, reference equality may be used.

However, since unstored entities may have their ID change at some point in the future, it is very important that such objects are never stored in a Dictionary. Or, at the very least, such items must be removed from any dictionaries before saving and then restored afterwards using the new ID.

With that one reservation, this design should work fine.

public class Test : IEquatable<Test>
{ 
    /// <summary>
    /// The unique identifier for this Test entity, or zero if this
    /// Test entity has not yet been serialized to the database.
    /// </summary>
    public int Id { get; private set; } 

    public string Something { get; set; }

    public override bool Equals(object obj)
    {
        return Equals(obj as Test);
    }

    public bool Equals(Test other)
    {
        if (other == null)
            return false;
        // Distinct entities may exist with the Id value of zero.
        if (Id == 0)
            return object.ReferenceEquals(this, other);
        return Id == other.Id;
    }

    /// <summary>
    /// Gets a hash code for this Test entity. Warning: an instance with
    /// an Id of zero will change its identity when saved to the DB. Use with care.
    /// </summary>
    /// <returns>The hash code.</returns>
    public override int GetHashCode()
    {
        return Id;
    }
} 
like image 34
Jeffrey L Whitledge Avatar answered Oct 04 '22 04:10

Jeffrey L Whitledge