将DTO界面传递到Domain Factory?

时间:2013-01-01 15:38:00

标签: c# domain-driven-design dto

我正在尝试在我当前的项目中应用DDD的主体。我会尝试用一个希望有意义的例子来问这个(冗长的)问题。

创建新成员时,我的表示层调用在应用层中定义的MemberService.CreateMember(MemberDTO memberDTO)。

我的表示层有这样的东西:

MemberDTO member = new MemberDTO(); //Defined in Application Layer

member.Username = username;
member.Password = password;
//...etc

我的应用层在域层中调用以下Factory方法来创建成员:

public static Member MemberFactory.CreateMember(string memberDTO.Username, string memberDTO.Password...)
{
  var member = new Member(); //Domain.Model.Member

  member.Id = GenerateIdentity();

  member.Username = memberDTO.Username;

  //... etc

  return member;
}

将成员传递回MemberService(应用层),将其保存(基础架构层中的存储库)并将其映射到MemberDTO(使用AutoMapper)并将其传递回表示层。

因此,我的表示层在MemberDTO中设置值,然后我的域层(通过工厂)采用单个参数并设置成员的值。如果没有工厂,它会很简单,但我在这里生成了Id。创建域服务而不是生成ID是错误的吗?例如,从:

更改MemberService.CreateMember(MemberDTO memberDTO)方法
public MemberDTO CreateMember(MemberDTO memberDTO)
{
   var member = MemberFactory.CreateMember(memberDTO.Username, memberDTO.Password); 
   //Domain.Model.Member

   SaveMember(member);

   //Pass DTO to presentation layer
   return Mapper.Map<Member, MemberDTO>(member);
}

对此:

public MemberDTO CreateMember(MemberDTO memberDTO)
{
   var member = new Member(); //Domain.Model.Member

   Mapper.Map<MemberDTO, Member>(memberDTO);

   //Add this method into a Domain Service to generate the ID and any other defaults
   Domain.MemberService.Initialise(member);

   SaveMember(member);

   //Pass DTO to presentation layer
   return Mapper.Map<Member, MemberDTO>(member);
}

对不起那个冗长的问题感到抱歉,即使答案可能是简单的是或否!

3 个答案:

答案 0 :(得分:2)

关于DDD,如果此标识不是域标识(即基于有界上下文定义用户的内容),则它不属于您的域模型。在我看到的大多数情况下,用户名是有界上下文中用户的标识,任何生成的ID都是为了数据库访问而完成的。我怀疑这可能是你的情况,如果我是对的,那么生成的ID是基础设施问题,应该由你的存储库实现来完成。您的域名不应包含此生成ID的任何概念。

另一方面,假设用户名是您上下文中用户的标识,并且您希望使用某个域公式生成该标识。在这种情况下,生成标识将是域服务方法,因为它实际上不属于任何域对象的实例。然后生成的ID将传递到您的工厂,您将拥有:

public MemberDTO CreateMember(MemberDTO memberDTO)
{
   //Domain.Model.Member
   var member = MemberFactory.CreateMember(MemberService.GenerateUserName(), memberDTO.Password); 

   SaveMember(member); //Repository method?

   //Pass DTO to presentation layer
   return Mapper.Map<Member, MemberDTO>(member);
}

总而言之,答案是否定的,假设ID是有界上下文的实际身份,创建域服务以生成ID将 错误。事实上,这就是你应该做的。但是,如果ID不是有界上下文的用户身份,那么让您的存储库担心如何生成ID,访问数据库以及转换到域模型或从域模型转换。

答案 1 :(得分:1)

我不确定与DDD有关的这个问题,因为我没有看到需要额外复杂性的复杂域。但是,可能存在一个论点,即在另一个结构中定义生成身份,但这主要是为了实现单一责任(SRP)。

我建议您创建一个名为IGenerateMemberIdentity的界面。然后,您可以隔离该复杂性并对其进行测试。我认为你的直觉,这个功能不属于工厂是正确的。如果这是一个复杂的例程,它似乎不是关于对象的创建。

然而,对我来说,Initialize静态方法看起来像代码味道。最好在提供该功能的接口后面注入依赖项。

专门回答您的问题。不,我不认为这是错的。事实上,为了保持纯粹的业务功能而建模复杂性是DDD最擅长的。

答案 2 :(得分:1)

简短回答:是的,您可以将DTO对象传递给工厂。

答案很长: 域对象工厂负责创建完全初始化的域对象。但是,只要您可以提供所需信息,您就可以自由选择任何形式的输入。在这种情况下,在您的工厂中使用任何一种方法都是个人选择的问题:   - public Member CreateMember(string userName,string password,...){...}   - 公共成员CreateMember(MemberDTO memberDTO){...}

从分层的角度来看,域对象工厂是域模型的一部分。因此,它可以被域模型层和应用层中的不同对象使用和依赖。根据您的描述,MemberService似乎属于您的应用程序(服务)层。如果将创建逻辑移入其中,您可能会发现困难的依赖性问题。例如,如果一个Booking域对象在确认时创建了一个Member域对象,那么最终可能会让Booking对象依赖于MemberService,这不是一个好主意。这个例子如下所示:http://thinkinginobjects.com/2012/09/05/abstract-factory-in-domain-modelling

如果我们稍微退一步并从某个角度看问题,我想问一下,会员管理器和会员服务的确切用途是什么。 DTO对象是否用作一组数据字段的包装器? MemberService是否只是将create调用委托给您的工厂和存储库?如果答案是肯定的,那么如果我们一起删除MemberDTO和MemberService并让演示文稿直接调用MemberFactory和MemberRepository,那么代码就会简单得多。我在这里做了很多假设,但如果您的表示层和服务层在物理上分开,您可能会发现DTO和服务仍然是可取的。