EF 4.2代码优先和DDD设计问题 [英] EF 4.2 Code First and DDD Design Concerns

查看:156
本文介绍了EF 4.2代码优先和DDD设计问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我尝试用EF 4.2(或EF 4.1)代码进行DDD开发时,我有几个问题。我做了一些广泛的研究,但没有为我的具体问题提出具体的答案。这是我的担忧:


  1. 域不知道持久层,换句话说,域与EF完全分离。但是,为了将数据保存到数据库,每个实体必须附加到EF上下文或添加到EF上下文中。我知道你应该使用工厂来创建聚合根的实例,所以工厂可以用EF上下文注册创建的实体。这似乎违反DDD规则,因为工厂是域的一部分,而不是持久层的一部分。我应该如何去创建和注册实体,以便在需要时正确地保留到数据库?


  2. 应该是一个聚合实体是创建它的小孩实体?我的意思是,如果我有一个组织,而组织有一个集合 Employee 实体,应该组织有一个方法,如 CreateEmployee AddEmployee ?如果没有创建一个 Employee 实体在哪里记住,组织聚合根拥有每个 Employee 实体。


  3. 首先使用EF代码时,ID(以数据库)自动处理,一般不应由用户代码更改。由于DDD表示域与持久性无知分开,似乎暴露ID在域中是一件奇怪的事情,因为这意味着域应该处理向新创建的实体分配唯一的ID。我应该担心实体的ID属性吗?


我意识到这些是开放式的设计问题,但是我尽量在使用EF作为我的持久层的同时,坚持使用DDD设计模式。



提前感谢!

解决方案

1:我不是所有熟悉EF但使用基于代码优先/约会的映射方法,我认为映射不是太难具有getter和setter的POCO(甚至在另一个项目中保持 DbContext DbSet 属性类不应该是硬)。我不会认为POCO是总体根源。相反,它们表示要保留的聚合中的状态。以下示例:

  //这是持久化的
public class TrainStationState {
public Guid Id {get;组; }
public string FullName {get;组; }
public double Latitude {get;组; }
public double Longitude {get;组;

// ...更多状态
}

//这是你使用
public class TrainStation:IExpose< TrainStationState> {
TrainStationState _state;

public TrainStation(TrainStationState state){
_state = state;
//你也可以复制成员变量
//使这个
//对象工作所需的状态(认为纪念图案)。
//或者你可以有一个无参数的
//构造函数和一个显式方法
//来恢复/安装状态。
}

TrainStationState IExpose.GetState(){
return _state;
//再次,没有任何阻止你从
//手动组装这个状态对象
//。
}

public void IncludeInRoute(TrainRoute route){
route.AddStation(_state.Id,_state.Latitude,_state.Longitude);
}
}

现在,关于总体生命周期,是两个主要场景:


  1. 创建新聚合:您可以使用工厂,工厂方法,构建器,构造函数...你的需要当您需要持久化聚合时,查询其状态并持久化(通常此代码不在您的域中,并且非常通用)。

  2. 检索现有聚合:您可以使用一个仓库,一个道具,...适合你的需要。重要的是要明白,从永久存储中检索的是一个状态POCO,您需要注入原始聚合(或使用它来填充其私有成员)。这一切都发生在仓库/ DAO门面后面。不要用这种一般的行为来混淆你的呼叫站点。

2:有几件事情想到了。这里有一个列表:


  1. 聚合根是一致性边界。您在组织和员工之间看到什么一致性要求?

  2. 组织可以作为员工的工厂,而不会突变组织的状态。

  3. 所有权不是什么聚合。

  4. 聚合根通常具有在聚合中创建实体的方法。这是有道理的,因为根是负责执行集合中的一致性。

3:从外部分配标识符, , 继续。这并不意味着暴露他们(只有在POCO州)。


I have several concerns when trying to do DDD development with EF 4.2 (or EF 4.1) code first. I've done some extensive research but haven't come up with concrete answers for my specific concerns. Here are my concerns:

  1. The domain cannot know about the persistence layer, or in other words the domain is completely separate from EF. However, to persist data to the database each entity must be attached to or added to the EF context. I know you are supposed to use factories to create instances of the aggregate roots so the factory could potentially register the created entity with the EF context. This appears to violate DDD rules since the factory is part of the domain and not part of the persistence layer. How should I go about creating and registering entities so that they correctly persist to the database when needed to?

  2. Should an aggregate entity be the one to create it's child entities? What I mean is, if I have an Organization and that Organization has a collection of Employee entities, should Organization have a method such as CreateEmployee or AddEmployee? If not where does creating an Employee entity come in keeping in mind that the Organization aggregate root 'owns' every Employee entity.

  3. When working with EF code first, the IDs (in the form of identity columns in the database) of each entity are automatically handled and should generally never be changed by user code. Since DDD states that the domain is separate from persistence ignorance it seems like exposing the IDs is an odd thing to do in the domain because this implies that the domain should handle assigning unique IDs to newly created entities. Should I be concerned about exposing the ID properties of entities?

I realize these are kind of open ended design questions, but I am trying to do my best to stick to DDD design patterns while using EF as my persistence layer.

Thanks in advance!

解决方案

On 1: I'm not all that familiar with EF but using the code-first/convention based mapping approach, I'd assume it's not too hard to map POCOs with getters and setters (even keeping that "DbContext with DbSet properties" class in another project shouldn't be that hard). I would not consider the POCOs to be the Aggregate Root. Rather they represent "the state inside an aggregate you want to persist". An example below:

// 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);
  }
}

Now, with regard to aggregate life-cycle, there are two main scenario's:

  1. Creating a new aggregate: You could use a factory, factory method, builder, constructor, ... whatever fits your needs. When you need to persist the aggregate, query for its state and persist it (typically this code doesn't reside inside your domain and is pretty generic).
  2. Retrieving an existing aggregate: You could use a repository, a dao, ... whatever fits your needs. It's important to understand that what you are retrieving from persistent storage is a state POCO, which you need to inject into a pristine aggregate (or use it to populate it's private members). This all happens behind the repository/DAO facade. Don't muddle your call-sites with this generic behavior.

On 2: Several things come to mind. Here's a list:

  1. Aggregate Roots are consistency boundaries. What consistency requirements do you see between an Organization and an Employee?
  2. Organization COULD act as a factory of Employee, without mutating the state of Organization.
  3. "Ownership" is not what aggregates are about.
  4. Aggregate Roots generally have methods that create entities within the aggregate. This makes sense because the roots are responsible for enforcing consistency within the aggregate.

On 3: Assign identifiers from the outside, get over it, move on. That does not imply exposing them, though (only in the state POCO).

这篇关于EF 4.2代码优先和DDD设计问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆