It would be difficult to find enterprise projects that do not talk to other systems (external or internal), unless yours is an application that is purely a sourcing system or is a system that is independently catering to a small audience or a department. On my current project, we are talking to other internal systems within the enterprise and need to store references to entities inside external systems within our system. To be more precise, this storing boils down to two cases:

  1. Storing the reference to an external entity. An example would be all Country or State related information being managed in an external system and we need to store reference to the country/state, in this case we store only the id of the country (not the surrogate id, but countryCode which becomes a symbol that uniquely identifies a country or state within a country).
  2. Storing few concerned attributes along with external reference. An example, would be storing price details of an Item along with UPC (Universal Product Code), because price keeps on changing and we need to reflect the price that was applied at the time the transaction was created.

In either of the above cases, at minimum, we need to store reference to that external entity. As we need to express external entity as a part of our model, we end up creating an abstraction for it in our system. We should note the following characteristics of such an external entity:

  1. We never manage the life-cycle of an external entity.
  2. Hence it is implied that we never assign Id to an external entity.
  3. We use these entities in Read-Only Mode and usually query for these entities by criteria.

In an effort to make this distinction clear in the model, I extended the DDD Entities concept by classifying Entities as Internal Entity and External Entity.

  1. Internal Entity – Any object whose life-cycle is managed within our module/project is an Internal Entity.
  2. External Entity – Any object whose life-cycle is not managed within our module/project, but only store references (ocassionally along with few attributes) to those entities is an External Entity.

A sample implementation of such a classification could be rendered with simple interface based static hierarchy or you could choose to use Annotations (in Java)/Attributes (in C#) to achieve this classification. Below is a sample implementation using marker interfaces and abstract classes in C#.

public interface Entity
{
}
  1. Internal Entity Implementation – Mark Internal entities with a marker interface InternalEntity or you can have an abstract class InternalEntity. Surrogate Id generation is taken care-of within this and so is identity based equality implementation.
    // Any entity whose life-cycle is controlled by our project/module is an Internal Entity and hence we
    // assign a surrogate Id to our entities and hence this is manifested as "id" field in this abstract class. All
    // our internal entities will derive from this class.
    public abstract class InternalEntity : Entity
    {
      private int id;
    
      public virtual int Id
      {
        get { return id; }
        private set { id = value; }
      }
    
      public override string ToString()
      {
        return GetType().Name + " Id = " + id;
      }
    
      public override bool Equals(object obj)
      {
        if (ReferenceEquals(obj, null))
        {
          return false;
        }
        if (ReferenceEquals(obj, this))
        {
          return true;
        }
        if (this.GetType() != typeof(obj))
        {
          return false;
        }
        InternalEntity that = (InternalEntity) obj;
        return (Id == that.Id);
      }
    
      public override int GetHashCode()
      {
        return id.GetHashCode();
      }
    }
    

  2. External Entity Implementation – Mark all External entities with a marker interface ExternalEntity. We cannot provide a common behavior as we do not know the primary keys that will uniquely identify this entity.
    // Any entity whose life-cycle is not controlled within our module/project is an external entity for us.
    // Hence, any reference that we need to store in our module/project for external entities should
    // be assigned by the classes implementing this interface.
    public interface ExternalEntity : Entity
    {
    }
    

Having applied this on my project, I found that this segregation has following positive side-effect:

  1. Developers while doing analysis can quickly discern which entities are internal or external to the system.
  2. Helps improve code readability.
  3. While writing services that we use for querying these external entities, we have been able to treat that external persistent store as a xxxQueryRepositoryService. For example, we could then have model layer services like the ICountryQueryRepositoryService or IItemQueryRepositoryService. Again, the implementation could talk to the external system via a Remote Method call or a Web service call, is completely shielded from clients…essentially, still maintaining Persistence Ignorance in spirit.
  4. We continue to apply the repository pattern to internal entities, by using xxxRepository. For example, we can have IPurchaseOrderRepository