I am building an application using Domain Driven Design using the Entity Framework.
My goal is to let my domain models (which are saved with EF) contain some logic.
Out of the box, the entity-structure does not quite limit how objects are added to the chart and then saved.
Take, for example, my domain as POCO (without logic):
public class Organization { private ICollection<Person> _people = new List<Person>(); public int ID { get; set; } public string CompanyName { get; set; } public virtual ICollection<Person> People { get { return _people; } protected set { _people = value; } } } public class Person { public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual Organization Organization { get; protected set; } } public class OrganizationConfiguration : EntityTypeConfiguration<Organization> { public OrganizationConfiguration() { HasMany(o => o.People).WithRequired(p => p.Organization);
My sample domain is that an Organization can have many people. A person may belong to only one Organization.
It is very simple to create an organization and add people to it:
using (var context = new MyDbContext()) { var organization = new Organization { CompanyName = "Matthew Widget Factory" }; organization.People.Add(new Person {FirstName = "Steve", LastName = "McQueen"}); organization.People.Add(new Person {FirstName = "Bob", LastName = "Marley"}); organization.People.Add(new Person {FirstName = "Bob", LastName = "Dylan" }); organization.People.Add(new Person {FirstName = "Jennifer", LastName = "Lawrence" }); context.Organizations.Add(organization); context.SaveChanges(); }
My test request.
var organizationsWithSteve = context.Organizations.Where(o => o.People.Any(p => p.FirstName == "Steve"));
The above class layout does not match how the domain works. For example, all people belong to an Organization with an organization that is an aggregate root. It makes no sense to do context.People.Add(...) as not how the domain works.
If we want to add some logic to the Organization model to limit the number of people in this organization, we could implement a method.
public Person AddPerson(string firstName, string lastName) { if (People.Count() >= 5) { throw new InvalidOperationException("Your organization already at max capacity"); } var person = new Person(firstName, lastName); this.People.Add(person); return person; }
However, with the current class layout, I can bypass the AddPerson logic either by calling organization.Persons.Add(...) or completely ignore the aggregate root by doing context.Persons.Add(...) , none of which I want to do .
My suggested solution (which does not work and why I post it here):
public class Organization { private List<Person> _people = new List<Person>();
This does not work because HasMany(o => o.People).WithRequired(p => p.Organization); conversion HasMany(o => o.People).WithRequired(p => p.Organization); does not compile because HasMany expects an HasMany ICollection<TEntity> , not an IReadOnlyCollection . I can show ICollection itself, but I want to avoid the Add / Remove methods.
I can ignore the People property, but I still want to be able to write Linq queries against it.
My second problem is that I do not want my context to show the ability to directly add / remove people.
In the context I would like:
public IQueryable<Person> People { get; set; }
However, EF will not populate the People property of my context, even if IDbSet implements IQueryable . The only solution I can come up with is to write a facade over MyDbContext that provides the functionality I want. There seems to be an abundance and a lot of maintenance for a read-only dataset.
How to achieve a clean DDD model using Entity Framework?
EDIT
I am using Entity-Framework v5