我正在尝试在我当前的项目中应用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);
}
对不起那个冗长的问题感到抱歉,即使答案可能是简单的是或否!
答案 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和服务仍然是可取的。