Mat*_*hew 7 c# domain-driven-design entity-framework aggregateroot
我正在使用使用Entity Framework的Domain Driven Design构建应用程序.
我的目标是允许我的域模型(通过EF持久化)包含一些逻辑.
开箱即用,实体框架对于如何将实体添加到图形然后保持不变非常不受限制.
举例来说,我的域名为POCO(没有逻辑):
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); //.Map(m => m.MapKey("OrganizationID"));
}
}
public class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
HasRequired(p => p.Organization).WithMany(o => o.People); //.Map(m => m.MapKey("OrganizationID"));
}
}
public class MyDbContext : DbContext
{
public MyDbContext()
: base(@"Data Source=(localdb)\v11.0;Initial Catalog=stackoverflow;Integrated Security=true")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new PersonConfiguration());
modelBuilder.Configurations.Add(new OrganizationConfiguration());
}
public IDbSet<Organization> Organizations { get; set; }
public IDbSet<Person> People { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我的示例域名是组织可以有很多人.一个人只能属于一个组织.
创建组织并向其添加人员非常简单:
using (var context = new MyDbContext())
{
var organization = new Organization
{
CompanyName = "Matthew's 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();
}
Run Code Online (Sandbox Code Playgroud)
我的测试查询是.
var organizationsWithSteve = context.Organizations.Where(o => o.People.Any(p => p.FirstName == "Steve"));
Run Code Online (Sandbox Code Playgroud)
上面的类布局不符合域的工作方式.例如,所有人都属于组织为组合根的组织.能够这样做context.People.Add(...)是没有意义的,因为这不是域的工作方式.
如果我们想要为Organization模型添加一些逻辑来限制该组织中有多少人,我们可以实现一个方法.
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;
}
Run Code Online (Sandbox Code Playgroud)
但是,对于类的当前布局,我可以AddPerson通过调用organization.Persons.Add(...)或完全忽略聚合根来避开逻辑,这context.Persons.Add(...)两者都不是我想做的.
我提出的解决方案(不起作用,也就是我在这里发布的原因)是:
public class Organization
{
private List<Person> _people = new List<Person>();
// ...
protected virtual List<Person> WritablePeople
{
get { return _people; }
set { _people = value; }
}
public virtual IReadOnlyCollection<Person> People { get { return People.AsReadOnly(); } }
public void AddPerson(string firstName, string lastName)
{
// do domain logic / validation
WriteablePeople.Add(...);
}
}
Run Code Online (Sandbox Code Playgroud)
这不起作用的映射代码HasMany(o => o.People).WithRequired(p => p.Organization);不编译为HasMany预期的ICollection<TEntity>,而不是IReadOnlyCollection.我可以暴露ICollection自己,但我想避免使用Add/ Remove方法.
我可以"忽略"该People属性,但我仍然希望能够针对它编写Linq查询.
我的第二个问题是我不希望我的上下文暴露直接添加/删除人的可能性.
在上下文中我想要:
public IQueryable<Person> People { get; set; }
Run Code Online (Sandbox Code Playgroud)
但是,People即使是IDbSet实现,EF也不会填充我的上下文的属性IQueryable.我能想到的唯一解决方案是编写一个外观,MyDbContext它可以暴露我想要的功能.对于只读数据集来说,似乎过度杀伤和大量维护.
如何在使用Entity Framework时实现干净的DDD模型?
编辑
我正在使用Entity-Framework v5
Wik*_*hla 14
正如您所注意到的,持久性基础结构(EF)对类结构施加了一些要求,因此不会像您期望的那样"干净".我担心与之斗争会最终导致无休止的斗争和脑力颠簸.
我建议另一种方法,一个完全干净的域模型和一个单独的持久性模型在较低层.您可能需要在这两者之间使用转换机制,AutoMapper会很好.
这将完全解除您的顾虑.没有办法"削减"只是因为EF使事情变得必要并且域层不能提供上下文,因为它只是来自"另一个世界",它不属于域.
我已经看到人们制作部分模型(又名"有界背景")或只是创建一个普通的EF poco结构并假装这个IS DDD,但它可能不是,你的担忧正好在头脑中.