代码之家  ›  专栏  ›  技术社区  ›  Jon

体系结构分层和工作单元模式

  •  3
  • Jon  · 技术社区  · 11 年前

    我正在努力找出架构/分层和工作单元的良好实践。

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

    MVC模式 服务/域层 库层 第6页

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

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

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

    1) 客户服务 2) 发票服务 3) 订单服务

    每个存储库都有自己的实现(即没有通用存储库),允许我们精确地编辑所需的内容并创建返回所需内容的查询,而不是获取非常大的对象或进行大量单独的调用(并避免从域传递大量include语句)。

    这种结构总体上运行良好。我们发现服务层类有时会非常大(最终会破坏SRP)。认为我们应该最终将它们拆分为子服务。如果是CustomerService,则可能会变成CustomerQueryService和CustomerAdminService。我们还发现,这种结构有助于存储库,因为我们的一些查询最终会非常大,将数据转换为正确的格式,而不会提取比所需更多的数据。

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

    我不确定如何实现这一点,或者这是不是一个坏主意。我应该如何建立一个允许多服务和存储库的工作单元模式(convcens和SRP的分离)?

    1 回复  |  直到 11 年前
        1
  •  5
  •   Tomasz Jaskuλa    11 年前

    你的问题中有很多概念不仅与技术部分有关。我一直试图从技术上解决它,但从长远来看,这总是失败的。重要的是 商业专家 不得不说。这里是 领域驱动设计 如果应用正确,会发光。作为开发人员,我们试图假设业务是如何运作的,而没有真正询问他们如何解决他们的问题。这导致我们 一切都是事务性的 东西 2个 , 环状和整体运行时怪物 我的第一个问题是: “为什么订单必须与发票服务进行交易?” 。询问业务专家。 “您想因为开票系统中的错误而取消5000万订单吗?” 这对我来说似乎是非常错误的。问问商界人士,他们希望如何处理这些失败案例。

    据我所知,即使无法开具发票,商业人士也总是愿意接受订单。但我缺乏你们工作的背景,所以我不能给你们一个真正彻底的答案。也许有些角落里的箱子我不知道。

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

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

    这可能无法解决您的问题,而这可能会对您不利。但我更愿意给你一些提示,这样你就可以走得更远,因为我去过那里 做任何事务性的事情通常都是一种糟糕的处理方式 在真实系统中。

    参考文献: