首先尝试使用EF 4.2(或EF 4.1)代码进行DDD开发时,我有几个问题。我做了一些广泛的研究,但没有为我的具体问题提出具体的答案。以下是我的担忧:
域名无法了解持久层,换句话说,域名与EF完全分开。但是,要将数据持久保存到数据库,必须将每个实体附加到EF上下文或添加到EF上下文。我知道你应该使用工厂来创建聚合根的实例,这样工厂就可以用EF上下文注册创建的实体。这似乎违反了DDD规则,因为工厂是域的一部分而不是持久层的一部分。我应该如何创建和注册实体,以便在需要时正确地保存到数据库?
聚合实体是否应该创建它的子实体?我的意思是,如果我有Organization
并且Organization
有Employee
个实体的集合,那么Organization
应该有CreateEmployee
或AddEmployee
等方法{1}}?如果不是,创建Employee
实体的位置请记住,Organization
聚合根'拥有'每个Employee
实体。
首先使用EF代码时,每个实体的ID(以数据库中的标识列的形式)会自动处理,通常不会被用户代码更改。由于DDD声明域与持久性无知是分开的,因此在域中公开ID似乎很奇怪,因为这意味着域应该处理为新创建的实体分配唯一ID。我是否应该关注公开实体的ID属性?
我意识到这些是一些开放式的设计问题,但我正努力坚持DDD设计模式,同时使用EF作为我的持久层。
提前致谢!
答案 0 :(得分:24)
开始1:我并不熟悉EF,但使用基于代码优先/基于约定的映射方法,我认为用吸气剂和设定器映射POCO并不太难(即使保留" DbContext
与DbSet
属性"在另一个项目中的课程也不应该那么难。我不认为POCO是聚合根。相反,它们代表了你想要坚持的聚合中的状态"。以下示例:
// This is what gets persisted
public class TrainStationState {
public Guid Id { get; set; }
public string FullName { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
// ... more state here
}
// This is what you work with
public class TrainStation : IExpose<TrainStationState> {
TrainStationState _state;
public TrainStation(TrainStationState state) {
_state = state;
//You can also copy into member variables
//the state that's required to make this
//object work (think memento pattern).
//Alternatively you could have a parameter-less
//constructor and an explicit method
//to restore/install state.
}
TrainStationState IExpose.GetState() {
return _state;
//Again, nothing stopping you from
//assembling this "state object"
//manually.
}
public void IncludeInRoute(TrainRoute route) {
route.AddStation(_state.Id, _state.Latitude, _state.Longitude);
}
}
现在,关于聚合生命周期,有两个主要方案:
On 2:想到几件事。这是一个列表:
开启3:从外部分配标识符,克服它,继续前进。但这并不意味着暴露他们(仅在州POCO)。
答案 1 :(得分:2)
EF-DDD兼容性的主要问题似乎是如何持久保存私有属性。 Yves提出的解决方案似乎是在某些情况下缺乏EF功率的解决方法。例如,您无法使用Fluent API执行DDD,这需要将状态属性设置为公共属性。 我发现只有使用.edmx文件进行映射才能让Domain Entities保持纯粹。它不会强制您将事物公开或添加任何依赖于EF的属性。
实体应始终由某些聚合根创建。查看Udi Dahan的精彩文章:http://www.udidahan.com/2009/06/29/dont-create-aggregate-roots/ 始终从那里加载一些聚合和创建实体也解决了将实体附加到EF上下文的问题。在这种情况下,您无需手动附加任何内容。它将自动附加,因为从存储库加载的聚合已经附加并且具有对新实体的引用。虽然存储库接口属于域,但存储库实现属于基础结构,并且知道EF,上下文,附加等。
我倾向于将自动生成的ID视为持久性存储的实现细节,必须由域实体考虑但不应公开。所以我有一个私有ID属性,映射到自动生成的列和另一个对域有意义的公共ID,如Person类的身份证ID或Passport号。如果没有这样有意义的数据,那么我使用Guid类型,它具有创建(几乎)唯一标识符的强大功能,而无需数据库调用。 因此,在这种模式中,我使用那些Guid / MeaningfulID来从存储库加载聚合,而数据库在内部使用自动生成的ID来加快连接速度(Guid不利于此)。