架构分层和工作单元模式

时间:2015-03-13 09:35:07

标签: architecture repository-pattern unit-of-work

我试图找出一个很好的做法来构建/分层和工作单元。

我们使用C#,编写MVC前端应用程序。目前的正常结构是:

MVC 服务/域层 存储库层 EF6

MVC只会调用该服务,(这里没有逻辑)。该服务包含所有域逻辑。存储库使用EF6处理数据访问。将存储库层置于EF之上的主要三个原因是:

1)单一责任(SRP),该服务处理业务关心的内容,存储库处理获取和保存数据。使用存储库意味着它们不会混合到同一个方法中。 2)测试,它使存储库更容易。 (我知道你现在可以模拟dbcontext,老实说还没有尝试过这种方法)。 3)从域中抽象出EF,因为它并不真正关心它,(尽管这是一个非常微弱的争论)。

我们将为系统的不同部分提供单独的服务和存储库,例如:

1)CustomerService  2)InvoiceService  3)OrdersService

每个存储库都有自己的实例(即没有通用存储库),允许我们准确编辑我们想要的内容并创建返回我们想要的查询,而不是获取非常大的对象或进行大量的单独调用(并避免必须从域中传递大量的包含状态。)

这种结构通常很有效。我们发现服务层类有时会非常大(最终破坏SRP)。认为我们应该最终将它们分成子服务。如果是CustomerService,可能会成为CustomerQueryService和CustomerAdminService。我们还发现这个结构对存储库有帮助,因为我们的一些查询最终变得非常大,将数据转换为正确的格式,而不是拉出比所需数据更多的数据。

当我想要使用交易时,它对我而言就失败了。如果我将每个存储库转换为一个工作存储库(我认为),这仍然可以工作。当您需要执行跨越两个或更多服务的操作时,会出现此问题。例如,创建订单可能还需要调用发票服务来创建发票。但是如果出于某种原因,如果第二部分失败了,你也会想要回滚订单并向客户端返回一个有用的错误。

我不确定如何实现这一点,或者如果这是一个坏主意。我如何设置一个工作单元模式,允许多个服务和存储库(分离囚犯和SRP)?

1 个答案:

答案 0 :(得分:5)

您的问题中有许多概念与技术部分无关。我一直试图在技术上解决它,但从长远来看总是失败。重要的还有商业专家所说的。如果正确应用,这就是 Domain Driven Design 闪耀的地方。我们作为开发人员试图假设业务如何运作,而不是真正问他们如何解决他们的问题。这导致我们一切都是交易的东西, 2PC cripled and monolithical runtime monsters 。我的第一个问题是:"为什么订单必须与发票服务进行交易?" 。问一位商业专家。 "您是否因为发票系统中的错误而丢失了50M的订单?" 这对我来说似乎非常错误。向商务人士询问他们如何处理这种失败案例。

根据我的理解,即使没有发票,人们总是会接受订单。但是我缺乏你工作的业务背景,所以我不能给你一个非常彻底的答案。也许有一些我不知道的角落情况。

对您的问题的回答非常复杂,仅提供技术解决方案是错误的,但我会尝试指出一些可能有助于您进一步发展的资源。如果您正在设计复杂系统,那么查看域驱动设计将是一个良好的开端,因为许多此类问题都可以解决。

  1. 向商界人士询问应如何处理此案件。他们经常会告诉你他们想要接受订单,即使发票不起作用也是如此。发票不起作用的概率是多少?如果这是1%的时间,那么失败并不是非常重要,可以由商务人士直接处理(给人们打电话,给不满意的客户提供折扣等)。商界人士有很多方法知道< / strong>如何处理它。至少他们不会松开任何订单。哪个是恕我直言最重要的部分。
  2. 了解域驱动设计有界上下文(BC)。有界上下文是域驱动设计的核心模式。这是DDD战略设计部门关注的重点,它是关于处理大型模型和团队的。对我而言订购服务发票服务是两种不同的BC。除非有合理的理由,否则不要让它们成为交易。
  3. 如何同步两个不同的BC?您还需要查看域事件聚合。它们由聚合根生成,通常由事件总线等中间发布异步,并由同一BC或其他BC中的其他聚合使用。关于如何设计聚合的一篇很好的文章是V. Vernon(链接在下面的参考文献中)。在您的场景中,这意味着您将在 OrderService BC中生成 OrderPlaced 事件(我不喜欢名称中的服务,因为它对不同的人意味着不同的事情,但我试着坚持你的例子)它们应该被保留,所以如果 InvoiceService BC不可用或者有错误,这些事件可以在以后消费并且发票在以后创建。
  4. 最终一致性:这是由第3点引起的。您必须知道对您的系统有何影响以及如何处理它。简而言之,这是一种用于分布式计算的一致性模型,用于实现高可用性,非正式地保证,如果没有对给定数据项进行新的更新,最终对该项的所有访问将返回最后更新的值。因此,根据您的情况,这意味着,如果 InvoiceService 崩溃或无法使用,则发票在发票后的某个时间点发出订单时,发票将不会被发出服务将带到网上。这是一个非常重要的概念,因为它可以帮助您处理复杂的模型,而且不会丢失任何对商务人士很重要的订单。
  5. 您已触及查询,这通常也是您必须处理的事情。将读取与写入分开也是非常有益的。这意味着您有一个用于写入的模型(域的状态更改)和另一个用于读取的模型(仅用于不同目的的非规范化数据,如UX视图等)。有一种称为 CQRS 的架构模式但是请注意,如果不了解您的域名,就无法应用它。 CQRS也适用于一个或几个BC,但不适用于整个系统。这真的取决于。我在这里提到这种模式作为进一步的调查,因为从理论上说,当你将读取与写入分开时,你正在进行CQRS。这是一种非常技术性的架构模式,因此您可以在不了解自己正在做的事情的情况下加入您的组织。
  6. 这可能无法解决您的问题,这可能会让您失望。但我更愿意给你一些提示,以便你可以更进一步,因为我一直在那里,并且做一切事务处理通常是在真实系统中处理事物的坏方法。

    <强>参考文献: