Setting the identity of a Domain Entity

c#, domain-driven-design, entity-framework, onion-architecture, repository-pattern

Solution

In addition to Ilya Palkin's answer I want to post another solution which is simpler but a bit tricky:

- Make `DomainEntity.UniqueId` protected, so it can be accessed from its childs

- Introduce a factory (or static factory method) and define it inside City class, so it can access the `DomainEntity.UniqueId` protected field.

Pros: No reflection, code is testable. Cons: Domain layer knows about DAL layer. A little bit tricky definition of the factory.

The code:

public abstract class DomainEntity
{
    // Set UniqueId to protected, so you can access it from childs
    protected int? UniqueId;
}

public class City : DomainEntity
{
    public string Name { get; private set; }

    public City(string name)
    {
        Name = name;
    }

    // Introduce a factory that creates a domain entity from a table entity
    // make it internal, so you can access only from defined assemblies 
    // also if you don't like static you can introduce a factory class here
    // just put it inside City class definition
    internal static City CreateFrom(CityTbl cityTbl)
    {
        var city = new City(cityTbl.Name); // or use auto mapping
        // set the id field here
        city.UniqueId = cityTbl.Id;
        return city;
    }
}

public class CityTbl
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void Main()
{
    var city = new City("Minsk");

    // can't access UniqueId and factory from a different assembly
    // city.UniqueId = 1;
    // City.CreateFrom(new CityTbl());
}

// Your repository will look like
// and it won't know about how to create a domain entity which is good in terms of SRP
// You can inject the factory through constructor if you don't like statics
// just put it inside City class
public class CityRepository : ICityRepository
{
    public City Find(int id)
    {
        var cityTblEntity = context.Set<CityTbl>().Find(id);

        return City.CreateFrom(cityTblEntity);
    }
}

Problem

All entities in the domain need to have identity. By inheriting from `DomainEntity`, I am able to provide identity to classes. City domain entity (stripped down for easy reading): ``` public class City : DomainEntity, IAggregateRoot { public string Name { get; private set; } public Coordinate Coordinate { get; private set; } public City(string name, decimal latitude, decimal longitude) { Name = name; SetLocation(latitude, longitude); } public City(string name, decimal latitude, decimal longitude, int id) : base(id) { Name = name; Coordinate = coordinate; SetLocation(latitude, longitude); } public void SetLocation(decimal latitude, decimal longitude) { Coordinate = new Coordinate(latitude, longitude); } } ``` DomainEntity abstract class: ``` public abstract class DomainEntity { private int? uniqueId; public int Id { get { return uniqueId.Value; } } public DomainEntity() { } public DomainEntity(int id) { uniqueId = id; } } ``` When a new entity is first created, an identity does not exist. Identity will only exist once the entity is persisted. Because of this, when creating a new instance of the entity, `Id` does not need to be supplied: ``` var city = new City("Cape Town", 18.42, -33.92); ``` When cities are read from persistence using a `CityRepository`, then the second constructor will be used so to populate the identity property as well: ``` public class CityRepository : ICityRepository { public City Find(int id) { var cityTblEntity = context.Set<CityTbl>().Find(id); return new City(cityTblEntity.Name, cityTblEntity.Lat, cityTblEntity.Long, cityTblEntity.Id); } } ``` The problem I am having here is that I provide a constructor which can take in identity. This opens up a hole. I only want identity to be set in the repository layer, but client code could now also start setting `Id` values. What's stopping someone from doing this: ``` var city = new City("Cape Town", 18.42, -33.92, 99999); // What is 99999? It could even be an existing entity! ``` How can I provide ways to set entity identity in my repository but to hide that from client code? Perhaps my design is flawed. Could I use factories to solve this? Note: I understand that this is not a perfect implementation of DDD as entities should have identity from the beginning. The `Guid` type would help me solve this problem, but I don't have that luxury unfortunately.

Original source

Related problems