仓储模式和工作单元中的架构问题

5

我使用了Repository pattern和Unit of work来实现项目,但还存在一些架构问题。

比如说,假设我正在创建一个二手汽车零件在线商店。虽然我有对我的数据库的操作,但我也需要从不同的废料场执行对远程API的操作,例如需要运行搜索、可用性更新等等...

我决定尝试使用unit of work和repository pattern

大多数事情都像Mosh Hamedani所做的那样,但是针对asp.net core 2.1进行了调整 就像这个视频

只要我使用EF(或者其他与数据库通信的工具),Unit of work和repositories都可以正常工作并且很合理,但如果我需要通过不同的Web API获取一些数据,则这种模式就没有太多意义了。 (例如从不同的API获取市场上的汽车列表,它们的处理和检索方式相似但略有不同-我通过密钥检索到相同接口的不同实现)。

首先 我不喜欢在我的unit of work中拥有所有repositories的所有实例,但是在大多数情况下只需要一个。我知道这有助于重复使用上下文事务而不将其包装在自己的事务中,但仍然很愚蠢,因为它会产生不必要的实例。

其次 我应该在repository和unit of work中也实现远程API的检索逻辑和处理,还是放弃unit of work并做其他事情?只保留repositories?(有人曾提到我不熟悉的facade pattern)

我往往会过度工程化,现在我非常困惑。任何建议都有帮助。

3个回答

5

仓储库模式的唯一目的是将 SQL 与应用程序代码抽象分离。工作单元模式存在的唯一目的是支持和协调与多个仓库有关的事务操作。EF 和其他 ORM 已经为你处理了所有这些问题。特别是在 EF 中,每个 DbSet 就是一个仓库,DbContext 则是工作单元。当你使用像 EF 这样的 ORM 时,它就是你的数据层,而不是你可能传统上创建的某个自定义类库。你绝对不应该在 EF 等东西之上实现仓储库/工作单元模式。

你需要的是一个 API 网关。在微服务架构中,每个服务都处理一个离散的功能单元,这意味着使用许多甚至所有这些微服务的横切应用程序将具有荒谬的依赖关系。最典型的方法是创建一个或多个 API 网关,它们本质上打包了与所有或特定子集的微服务的通信。你的应用程序只直接与 API 网关交互,并且 API 网关协调与所有必要的微服务通信以便完成应用程序的请求。这还使你能够无缝地实现消息队列等层次结构。


有道理,我听说过它,但一直没有时间去跟进,直到现在。你知道从哪里开始最好吗?像链接、视频、例子之类的?我是一个自学的开发者,所以我的知识有点不太结构化,有些空缺,这就是为什么我正在尝试填补它,我想现在是微服务架构的时候了。Chris,你帮了我很多,非常感谢你,我不仅指这个问题,还包括过去几年中的其他问题。 - Dino
绝对不应该在EF之上实现repository/unit of work模式,EF应该是业务层声明的repository实现细节。如果您决定将数据存储在某些xml文件中或将数据库放在某些Web API后面,您肯定不希望实现大量的DbContext/DbSet - Fabio
好的,我已经了解了微服务架构并在昨晚看了几个视频,但对于一个小应用程序来说,它似乎需要更多的工作和一些过度设计?我错过了什么吗?据我所知,我需要将功能分离为独立运行的服务,然后我需要提供身份验证服务,它将作为身份提供者或其他东西,并在所有服务上验证用户,这让我不太确定如何做。然后还有Docker...也许我想得太多了? - Dino
可以,但是你不一定需要完全按照微服务的精神去做。首先,只有在公开暴露时才需要身份验证。如果将它们全部放在内部防火墙后面,特别是如果应用程序仅与网关通信而不是每个单独的服务通信,则它们不一定需要授权层。即使这样,您也不会在每个微服务上授权单个用户,而是授权客户端(即您的应用程序)。您将授予应用程序客户端ID和密钥,然后它将使用它来授权其请求。 - Chris Pratt
没错,这就是想法。即使是迈向正确方向的一小步也比什么都不做要好。 - Chris Pratt
显示剩余2条评论

2
尽管我同意Chris Pratt的回答,但是在某些情况下,即使您使用ORM如EF,创建存储库也是必要的。我已经在这里详细解释了这一点。
对于您的问题,我建议您继续像迄今为止那样使用UoW和Repository。正如这里所提到的,UoW不仅仅是事务。如果您正确使用DbContext,EF可以完美地为您实现这一点。
关于涉及当前存储库和UoW中来自其他来源的API的问题,它们应该是分开的。我建议您为消耗第三方API的复杂性创建单独的包装器。将其命名为任何您想要的名称; 存储库或外部API包装器或其他...

我猜我们有类似的想法。放弃了“工作单元”,但保留了存储库,因为我喜欢这种粒度。同时也希望只在需要时控制实例和事务。将远程API处理分离到一个独立的服务中,并通过抽象使其看起来像一个API。所以现在它看起来像这样: _vendorAPIManager.GetVendor(Vendor.JacksScrapyard).GetSaleItems() - Dino
我们俩都有类似的想法,这并不是坏事,但我将接受Chris的想法作为被采纳的答案。尽管Chris的“从小开始”的想法在某种程度上与我们的相同,但它试图在后期变得更加庞大,而我们的则保持单一,而他的则不同。感谢你的一切,我很感激。 - Dino

0

我使用了相同的课程并实现了我的工作单元...但后来,我改变了主意并删除了UoW类。DbContext已经实现了UoW和Repository模式:来自MS文档

DbContext类

代表了工作单元和仓储模式的组合,并使您能够查询数据库并将更改分组,然后将其作为一个单元写回存储。DbContext在概念上类似于ObjectContext。

我仍然添加了自己的存储库。我同意Chris的观点,DbSet是一个存储库,但我可以通过添加自己的存储库来添加更多专业化的方法。

在您链接的视频中,我们通过将所有存储库放入UoW类中来实现UoW。在我看来,这种实现可能违反了一些SOLID原则:

  1. 感觉我们违反了开闭原则...因为每次添加新的存储库,我们都需要修改UoW类以包含新的存储库。
  2. 感觉我们违反了接口隔离原则。ISP规定客户端不应被强制实现他们不使用的接口。UoW包含所有存储库的实现...但是消费客户端并不需要所有这些实现,客户端可能只需要其中一个。
  3. 感觉我们违反了单一职责原则,因为UoW负责太多的存储库。

“我认为将其包装在另一个UoW类中没有太多好处” - 可能会影响可模拟性。 - guillaume31
@guillaume31,感谢您的评论,我已经更新了答案。我认为我们仍然可以模拟存储库... - Hooman Bahreini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接