morleyc
morleyc

Reputation: 2431

Entity Identity - using class as identity instead of simple type

With Vaughn Vernon's Implementing Domain Driven Design samples in C# (https://github.com/VaughnVernon/IDDD_Samples) there is the identity type from which all classes dedicated for identity are built:

public class CalendarId : Identity
{
    public CalendarId() { }

    public CalendarId(string id)
        : base(id)
    {
    }
}

Identity class:

public abstract class Identity : IEquatable<Identity>, IIdentity
{
    public Identity()
    {
        this.Id = Guid.NewGuid().ToString();
    }

    public Identity(string id)
    {
        this.Id = id;
    }

    // currently for Entity Framework, set must be protected, not private.
    // will be fixed in EF 6.
    public string Id { get; protected set; }

    public bool Equals(Identity id)
    {
        if (object.ReferenceEquals(this, id)) return true;
        if (object.ReferenceEquals(null, id)) return false;
        return this.Id.Equals(id.Id);
    }

    public override bool Equals(object anotherObject)
    {
        return Equals(anotherObject as Identity);
    }

    public override int GetHashCode()
    {
        return (this.GetType().GetHashCode() * 907) + this.Id.GetHashCode();
    }

    public override string ToString()
    {
        return this.GetType().Name + " [Id=" + Id + "]";
    }
}

Identity interface:

public interface IIdentity
{
    string Id { get; }
}

With a Calender class using the identity as so:

public class Calendar
{
    public Calendar(Tenant tenant, CalendarId calendarId, string name, string description, Owner owner, IEnumerable<CalendarSharer> sharedWith = null)
    {
         // ... store passed parameters, including identity
         this.id = id;
    }

    CalendarId calendarId;

    public CalendarId CalendarId
    {
        get { return this.calendarId; }
    }
}

What benefits does the above identity class provide over using say simpler types such as a GUID, or a string for the identity?

Upvotes: 1

Views: 606

Answers (2)

Christophe Herreman
Christophe Herreman

Reputation: 16085

One reason might be that when you pass identity objects between bounded contexts, the identity object itself carries the date when the identity (and hence the entity) was created and might also have information about the context it originated from. This is explained in the book on page 177.

Upvotes: 3

Eben Roux
Eben Roux

Reputation: 13256

This comes down to design preference I guess.

Some time ago I did more-or-less the same thing. I had an Identity<T> concept since the key types could vary. Mostly Guid but sometimes int or string.

I ended up abandoning the practice since it added more overhead than value and since my repositories always knew what type and key they were dealing with there was no real need to abstract it.

If, however, you find yourself with some generic bits that would, for instance, accept a repository and make the Get call for you then you may find it useful it. For example:

public class IllGetYourEntity
{
    public TEntity Get<TEntity>(IRepository<TKey> repository, Identity id)
    {
        return repository.Get(id);
    }
}

But I have even used a more fine-grained, role-specific repository interface structure. I'll rather have this:

public interface ICanGet<TEntity, TId)
{
    TEntity Get(TId id);
}

public interface ICanAdd<TEntity>
{
    void Add(TEntity entity);
}

These can then be used by another repository interface:

public interface ICustomerRepository: 
    ICanGet<Customer, Guid), 
    ICanAdd<Customer> 
{
}

But if you don't need that then a good entity specific repository interface will do:

public interface ICustomerRepository
{
    Customer Get(Guid id);
    void Add(Customer customer);
} 

Long story short: it doesn't buy you too much and I'd forgo it unless a real need arises.

Upvotes: 3

Related Questions