![]() |
1
40
存储库 不是 访问数据库的网关。它是一种抽象,允许您从某种形式的持久性存储中存储和加载域对象。(数据库、缓存甚至普通集合)。它获取或返回域对象,而不是其内部字段,因此它是一个面向对象的接口。
不建议添加一些方法,如
总的来说,存储库模式的目标是创建一个存储抽象,在用例发生变化时不需要进行更改。 This article 详细讨论了领域建模中的Repository模式。你可能感兴趣。
对于第二个问题:如果我看到
顺便说一句,我在过去看到过许多只包装存储库类的服务类。我认为这是一种反模式。最好让服务的调用方直接使用存储库。 |
![]() |
2
17
所以,存储库就是为域实体上的CRUD操作提供接口。请记住,存储库处理整个聚合。
实例
业务逻辑应该在域实体中,而不是在存储库层中,应用程序逻辑应该在服务层中,就像您提到的那样,这里的服务充当着存储库之间的协调器。 |
![]() |
3
7
虽然我仍在努力解决这个问题,但我想发布一个答案,但我也接受(并希望)对此的反馈。
在示例中
首先,让我们从最初的需求开始思考。我们碰到了一个控制器,可能是类别控制器,所以你有这样的东西:
到目前为止,一切都很好。我们需要声明创建视图模型的“something”。 让我们简单地说:
好的,什么是依赖关系?我们 大概 需要类别树、产品、页面、总产品数量等。 因此,如果我们以存储库的方式实现这一点,它看起来或多或少是这样的:
相反,返回服务:
好多了,但里面到底是什么
让我们尝试另一种方法,即工作单元,我将使用实体框架作为UoW和存储库,因此无需创建它们。
因此,我们在这里使用“query”语法而不是方法语法,但我们可以使用ORM,而不是实现我们自己的复合体。此外,我们可以访问所有存储库,因此我们仍然可以在我们的服务中完成我们的工作单元。 现在我们需要选择我们想要的数据,我可能不想要实体的所有字段。 我能看到的最好的地方实际上是在ViewModel上,每个ViewModel可能需要映射它自己的数据,所以让我们再次更改服务的实现。
那么,所有的产品和内部类别都在哪里呢? 让我们来看看ViewModel,记住这只会将数据映射到值,如果你在这里做其他事情,你可能会给ViewModel太多的责任。
你可以想象
但是(为什么总是有一个但是??) 我们正在进行3次数据库命中,并且由于Find,我们正在获取所有类别字段。同样是懒惰加载 必须 启用。不是一个真正的解决方案,不是吗?
为了改善这一点,我们可以改变查找。。。但这将委托
还记得我说过“我还在挣扎吗?”这就是为什么。要解决此问题,我们应该从服务返回所需的确切数据(也称为……你知道的……是的!ViewModel)。 那么让我们回到我们的控制器:
内部
这很糟糕,现在我的服务知道了视图模型。。。让我们来解决这个问题。 我们创建了一个接口,这个接口是这个方法将返回的实际合约。
很好,现在我们只需要将ViewModel声明为
并按照我们想要的方式实施。
这个接口看起来有太多的东西,当然可以用
因此,当我们实现另一个viewModel时,我们可以选择只执行
当我进入一个完全可以工作的代码时,我可能会创建一篇博客文章或github repo,但目前我还没有,所以这就是目前的全部内容。 |
![]() |
4
6
我认为存储库应该只用于CRUD操作。
因此,IRepository将有:Add、Remove、Update、Get、GetAll,以及可能采用列表的每个版本,即AddMany、RemoveMany等。 为了执行搜索检索操作,您应该有第二个接口,例如IFinder。你可以使用一个规范,所以IFinder可以有一个接受标准的Find(criteria)方法。或者你可以使用IPersonFinder之类的东西,它可以定义自定义函数,例如:FindPersonByName、FindPersonByAge等。
替代方案是:
第二种方法更为复杂。你需要为标准制定一个策略。你是要使用某种查询语言,还是更简单的键值关联等。简单地看界面也很难理解它的全部功能。使用这种方法也更容易泄露实现,因为标准可能基于特定类型的持久性系统,比如以SQL查询为标准。另一方面,它可能会阻止您继续返回IFinder,因为您遇到了一个需要更具体查询的特殊用例。我说可能,因为您的标准策略不一定涵盖您可能需要的100%查询用例。 您还可以决定将两者混合在一起,让一个定义Find方法的IFinder和实现IFinder的IMyObjectFinders,但也可以添加自定义方法(如FindByName)。 该服务充当主管。假设您需要检索一个项,但在将该项返回到客户端之前还必须对其进行处理,并且处理可能需要在其他项中找到信息。因此,服务将使用存储库和查找器检索所有适当的项,然后将要处理的项发送到封装必要处理逻辑的对象,最后返回客户端请求的项。有时,不需要处理,也不需要额外的检索,在这种情况下,您不需要服务。您可以让客户端直接调用存储库和查找程序。这是洋葱和分层架构的一个区别,在洋葱中,外部的一切都可以访问内部的一切,而不仅仅是之前的层。 存储库的作用是加载正确构造返回的项所需的全部层次结构。因此,如果您的存储库返回的项目具有另一种类型的项目的列表,那么它应该已经解决了这个问题。不过就我个人而言,我喜欢设计我的对象,这样它们就不会包含对其他项目的引用,因为这会使存储库更加复杂。我更喜欢让我的对象保留其他项目的Id,这样,如果客户真的需要其他项目,他可以在给定Id的适当存储库中再次查询它。这会使存储库返回的所有项目变平,但如果需要,仍然可以创建层次结构。 如果您真的觉得有必要,您可以在您的存储库中添加一个约束机制,这样您就可以准确地指定您需要的项目的哪个字段。假设你有一个Person,并且只关心他的名字,你可以做Get(id,name),Repository不会麻烦获取Person的每个字段,只需要它的name字段。但是,这样做会给存储库增加相当大的复杂性。对层次对象执行此操作甚至更为复杂,尤其是如果您希望将字段限制在字段的字段中。所以我并不真的推荐它。对我来说,唯一好的理由是性能至关重要,而无法采取其他措施来提高性能。 |
![]() |
5
4
在域驱动设计中,存储库负责检索整个聚合。 |
![]() |
6
0
洋葱和六边形架构的目的是颠倒对域的依赖性->数据访问。
现在,对于服务,有不止一种类型的服务:
@Bart Calixto,你可以看看CQRS,当你试图使用为域逻辑设计的存储库时,构建你的视图模型太复杂了。 您可以为ViewModel重写另一个repo,例如使用SQL联接,它不必在域中 |
![]() |
7
0
简短回答:是的,如果存储库的结果是一个层次结构 存储库的作用是在 任何形状 你需要,从 数据源 (例如, Lucene 索引等)。
让我们假设一个存储库(接口)具有
让我们关注结果:无论是形状(列表、层次结构等)还是存储库 实施 应该做的 每件事 必须归还。
如果使用的是NoSql数据库,则
存储库与域服务 根据 The Onion Architecture : part 1 ,我指的是官方页面,而不是其他人的解释:
请注意 域服务 上层 领域模型 一 从第二官方页面开始, The Onion Architecture : part 2 ,作者忘记了 域服务 层,并正在描述IConference 存储库 作为 对象服务 正上方的层 领域模型 ,更换 域服务 层这个 对象服务 层在中继续 The Onion Architecture : part 3 ,所以我问:什么 域服务 ?:)) 在我看来,作者的意图 对象服务 或 域服务 只由存储库组成,否则他不会为其他事情留下任何线索。 |
![]() |
Tony Raimo · 域实体是否应该调用存储库? 7 年前 |
![]() |
Seb · DDD只读存储库返回“值对象” 7 年前 |
![]() |
tlt · 使用嵌套对象和大集合进行聚合根优化 7 年前 |
![]() |
PatrickSJ · DDD,状态对象/值对象 7 年前 |
![]() |
msmani · DDD更改聚合根id 7 年前 |
![]() |
DuskMcDusk · 逻辑和性能中的聚合根冲突 7 年前 |