架构分层和工作单元模式

Jon*_*Jon 3 architecture unit-of-work repository-pattern

我试图找出建筑/分层和工作单元的良好实践.

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

MVC服务/域层存储库层EF6

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

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

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

1)CustomerService 2)InvoiceService 3)OrdersService

每个存储库都有自己的实例(即没有通用存储库),允许我们准确编辑我们想要的内容并创建返回我们想要的查询,而不是获取非常大的对象或进行大量的单独调用(并避免必须通过)在很多领域包括来自域的州.

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

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

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

Tom*_*uλa 5

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

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

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

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

这可能无法解决您的问题,这可能会让您失望.但是我更愿意给你一些提示,以便你可以更进一步,因为我一直在那里做事务交易通常是处理真实系统中的事情的坏方法.

参考文献: