Domain driven design 存储库添加和创建方法

Domain driven design 存储库添加和创建方法,domain-driven-design,repository-pattern,data-access-layer,ddd-repositories,Domain Driven Design,Repository Pattern,Data Access Layer,Ddd Repositories,为什么存储库的.Add方法通常实现为接受要添加的实体实例,并且.Id已经“设置”(尽管可以通过反射再次设置),这应该是repo的责任? 将其实现为.CreateAndAdd,不是更好吗 例如,给定一个人实体: public class Person { public Person(uint id, string name) { this.Id = id; this.Name = name; } public uint Id { g

为什么存储库的
.Add
方法通常实现为接受要添加的实体实例,并且
.Id
已经“设置”(尽管可以通过反射再次设置),这应该是repo的责任?
将其实现为
.CreateAndAdd
,不是更好吗

例如,给定一个
实体:

public class Person
{
    public Person(uint id, string name)
    {
        this.Id = id;
        this.Name = name;
    }

    public uint Id { get; }
    public string Name { get; }
}
为什么存储库通常实现为:

public interface IRpository<T>
{
    Task<T> AddAsync(T entity);
}
公共接口Ipository
{
任务AddAsync(T实体);
}
而不是:

public interface IPersonsRpository
{
    Task<Person> CreateAndAddAsync(string name);
}
公共接口IPERSOnspository
{
任务CreateAndAddAsync(字符串名称);
}
为什么存储库通常实现为

有几个原因

从历史上看,
领域驱动设计
深受引入该术语的Eric Evans书的影响。在那里,Evans建议存储库提供集合语义,提供“内存中集合的幻觉”

人的集合中添加
字符串
,甚至是
姓名
,没有多大意义

更广泛地说,找出如何从一组参数中重构实体是存储的一项独立职责,因此,去那里可能没有意义(注意:存储库通常负责从存储的纪念品中重新构建实体,因此它并不完全是外来的,但通常有一个额外的抽象,即“工厂”,真正起作用。)

使用通用存储库接口通常是有意义的,因为通过检索/存储操作与集合中的单个元素交互不需要大量定制。存储库可以支持对不同类型实体的定制查询,因此专门调用它可能会很有用

public interface IPersonRepository : IRepository<Person> {
    // Person specific queries go here
}
公共接口IPersonRepository:IRepository{ //特定于个人的查询转到此处 }

最后,
id
…事实是身份,作为一个概念,有很多“它取决于”在某些情况下,存储库为一个实体分配一个id可能是有意义的——例如,使用数据库生成的唯一键。通常,您会希望在存储库之外控制标识符。马匹用于课程。

关于这个问题已经有了很好的答案,我只想添加一些我的想法。(它将包含一些与上一个答案重复的内容,因此如果这是一件坏事,请告诉我,我将删除它:))

ID生成的职责可以属于组织或系统的不同部分

有时,ID将由一些特殊规则生成,例如。此编号可用于系统中人员的ID,因此在创建人员实体之前,必须从特定的SSNGenerator服务生成此代码

我们可以使用随机生成的ID,比如UUID。UUID可以在存储库之外生成,并在创建过程中分配给实体,存储库只会将其存储(添加、保存)到数据库中

由数据库生成的ID非常有趣。您可以使用RDBMS中的顺序ID、MonogoDB中的UUID或一些散列。在这种情况下,ID生成的责任分配给DB,因此它只能在存储实体后发生,而不是在创建实体时发生。(我在这里允许自己自由,因为您可以在保存事务或阅读最后一个事务之前生成它,等等。但我喜欢在这里进行概括,避免讨论带有竞争条件和冲突的情况)。这意味着您的实体在保存完成之前没有标识。这是好事吗?当然要视情况而定:)

这个问题就是一个很好的例子

当您实施一个解决方案时,有时使用的技术会影响它。例如,您必须处理这样一个事实,即ID是由数据库生成的,该数据库是基础设施的一部分(如果您在代码中定义了这样一个层)。即使使用RDBMS,您也可以通过使用s UUID来避免这种情况,但是您必须在这些ID上加入(同样是特定于技术的东西:),所以有时候人们喜欢使用默认值

你可以使用Save方法,而不是AddAddAndCreate来做同样的事情,这只是一些人喜欢的一个不同的术语。存储库确实经常被定义为“内存中的集合”,但这并不意味着我们必须严格遵守它(大多数时候这样做可能是一件好事,但仍然…)

如前所述,如果数据库生成ID,存储库似乎是分配ID(存储之前或之后)的一个很好的候选者,因为它是与数据库对话的那个

如果您以生成ID的方式使用事件,则会影响事情。例如,假设您想要将UserID作为s属性的UserRegisteredEvent。如果使用DB生成ID,则必须先存储用户,然后创建并存储/调度事件或执行类似操作。另一方面,如果事先生成ID,则可以将事件和实体一起保存(在事务或同一文档中并不重要)。有时这会变得棘手

背景、技术和框架方面的经验、对文学、学校和工作中术语的接触会影响我们对事物的看法,以及什么术语对我们来说更合适。此外,我们(大部分时间)在团队中工作,这会影响我们如何命名事物以及如何实现它们。

使用定义:

存储库在域和数据映射层之间进行调解, 作用类似于内存中的域对象集合。客户端对象 以声明方式构造查询规范