问题 在多层体系结构中执行批量操作时处理错误和反馈


假设您有一个可以跨多个对象执行某些操作的业务逻辑方法。也许您想要为从列表中选择的每个人拨打一个抽奖号码选择网络服务。在Java中,代码可能如下所示:

Set<Person> selectedPeople = ... // fetch list of people
for ( Person person : selectedPeople ) {
    String lotteryNumber = callLotteryNumberWebService( person );
    // ...
}

注意,彩票号码网络服务可能具有副作用,例如记录该人已经请求了彩票号码(可能对他们的账户收费),因此即使一个人的网络服务呼叫失败,它也可能已经成功。这些信息(彩票号码)需要反馈到更高级别(视图)。

如果这是发生单个操作的情况,则业务逻辑方法可以返回单个值(例如,抽奖号码)或者抛出具有故障的任何细节的异常。但对于批量操作,一些操作可能会成功,一些操作可能会失败。

这似乎是许多应用程序中会出现的一种问题,应该有一种干净的方法来处理它。那么,将这种类型的信息从业务逻辑层反馈到应用程序中的另一层(如视图)的最佳方法是什么,最好是以可以重用于不同类型的数据和操作的通用方式?


4096
2017-08-27 10:01


起源



答案:


如果我理解,你会遇到一些请求可以通过但有些请求失败的情况。我不确定您希望错误返回的位置,但您可以使用以下某个(或变体或组合):

  • 受影响的错误列表和域对象。基本域对象或具有可持久ID的内容可能对重用有用。例如。引用域对象的错误集合。
  • 您可以将(AOP,DI)注入Person对象中的某种错误对象/消息。例如。如果(人。错误){...}
  • 您可以将Person集合包装到带有标题,正文,错误信息的消息中
  • 您的所有域对象都可以包含可通过接口访问的错误集合;或Person支持IHasErrors接口。您可以使这个泛型并使用支持警告和验证以及各种方式的基本Error对象。

如果您使用的是真正的多层(而不是分层)系统,那么您可能拥有基于消息的体系结构,可以轻松容纳某种通用错误/警告/验证机制。 SOA / Ajax系统适合这一点。

如果你有一些具体的问题,很高兴深入研究。


1
2017-09-07 06:00





这个问题强调了异常处理,事务和想法的适当使用之间的重要区别 工作流程“补偿” 当正确陈述时,这是提问者试图得到的:

这似乎是许多应用程序中会出现的一种问题,应该有一种干净的方法来处理它。

这是一个常见问题,首先是您正在尝试的事务方法的一些背景知识:

数据交易最初是在复式会计后建模的 - 单一 信用 和相应的 借方 必须一起记录或根本不记录。随着事务变得越来越大,它们变得越来越难以正确实现,并且更难以处理失败。当您开始跨系统边界传递单个事务的想法时,您很可能接近错误。它可以完成,但需要复杂且必然更高延迟的事务协调器。在某种程度上,交易是错误的心态,补偿开始变得更有意义。

这是您回过头来看看业务实际做了什么的地方。单个大型交易很可能不是商务人士看到它的方式。通常他们会看到已完成的步骤,并且根据后续结果,可能需要采取不同的补偿措施。这就是工作流程的理念所在 赔偿金 进来。 这是对这些概念的一个介绍 

例如,如果您从亚马逊订购图书,他们可能不会在购物车中“锁定”记录,甚至在确认订单时使用严格的交易来确定图书是否仍在库存中。无论如何,他们会把它卖给你,并在他们可以的时候发货。如果他们在几周内没有设法将其存入库存,他们可能会向您发送一封电子邮件,告诉您他们正在努力满足您的需求,您可以继续等待他们将其存入库存,或者您可以取消您的订单。这称为补偿,在许多实际业务流程中都是必需的。

最后,还有 没有例外 关于这一点。期望这可能发生并使用正常的控制流程。你不应该在这里使用你的语言的异常处理功能(何时抛出异常的良好规则)。您也不应该依赖特定于工具(WCF?)的机制来查看或处理服务实现中发生的异常。沟通故障应该是数据合同的正常部分(故障合同)。

不幸的是,通过“干净的方式来处理它”没有设置的标志会神奇地处理它,你必须继续分解问题并处理所有产生的碎片。希望这些概念能够将您与其他人在处理此问题时所做的事情联系起来。

概要:

  • 您的问题超出了交易的概念 - >查看工作流程补偿。

祝你好运 -


10
2017-09-01 18:56



很好的答案! - James Anderson


答案:


如果我理解,你会遇到一些请求可以通过但有些请求失败的情况。我不确定您希望错误返回的位置,但您可以使用以下某个(或变体或组合):

  • 受影响的错误列表和域对象。基本域对象或具有可持久ID的内容可能对重用有用。例如。引用域对象的错误集合。
  • 您可以将(AOP,DI)注入Person对象中的某种错误对象/消息。例如。如果(人。错误){...}
  • 您可以将Person集合包装到带有标题,正文,错误信息的消息中
  • 您的所有域对象都可以包含可通过接口访问的错误集合;或Person支持IHasErrors接口。您可以使这个泛型并使用支持警告和验证以及各种方式的基本Error对象。

如果您使用的是真正的多层(而不是分层)系统,那么您可能拥有基于消息的体系结构,可以轻松容纳某种通用错误/警告/验证机制。 SOA / Ajax系统适合这一点。

如果你有一些具体的问题,很高兴深入研究。


1
2017-09-07 06:00





这个问题强调了异常处理,事务和想法的适当使用之间的重要区别 工作流程“补偿” 当正确陈述时,这是提问者试图得到的:

这似乎是许多应用程序中会出现的一种问题,应该有一种干净的方法来处理它。

这是一个常见问题,首先是您正在尝试的事务方法的一些背景知识:

数据交易最初是在复式会计后建模的 - 单一 信用 和相应的 借方 必须一起记录或根本不记录。随着事务变得越来越大,它们变得越来越难以正确实现,并且更难以处理失败。当您开始跨系统边界传递单个事务的想法时,您很可能接近错误。它可以完成,但需要复杂且必然更高延迟的事务协调器。在某种程度上,交易是错误的心态,补偿开始变得更有意义。

这是您回过头来看看业务实际做了什么的地方。单个大型交易很可能不是商务人士看到它的方式。通常他们会看到已完成的步骤,并且根据后续结果,可能需要采取不同的补偿措施。这就是工作流程的理念所在 赔偿金 进来。 这是对这些概念的一个介绍 

例如,如果您从亚马逊订购图书,他们可能不会在购物车中“锁定”记录,甚至在确认订单时使用严格的交易来确定图书是否仍在库存中。无论如何,他们会把它卖给你,并在他们可以的时候发货。如果他们在几周内没有设法将其存入库存,他们可能会向您发送一封电子邮件,告诉您他们正在努力满足您的需求,您可以继续等待他们将其存入库存,或者您可以取消您的订单。这称为补偿,在许多实际业务流程中都是必需的。

最后,还有 没有例外 关于这一点。期望这可能发生并使用正常的控制流程。你不应该在这里使用你的语言的异常处理功能(何时抛出异常的良好规则)。您也不应该依赖特定于工具(WCF?)的机制来查看或处理服务实现中发生的异常。沟通故障应该是数据合同的正常部分(故障合同)。

不幸的是,通过“干净的方式来处理它”没有设置的标志会神奇地处理它,你必须继续分解问题并处理所有产生的碎片。希望这些概念能够将您与其他人在处理此问题时所做的事情联系起来。

概要:

  • 您的问题超出了交易的概念 - >查看工作流程补偿。

祝你好运 -


10
2017-09-01 18:56



很好的答案! - James Anderson


我更愿意返回一组标识对象的自定义错误对象,这些对象受错误,错误代码和描述的影响。通过这种方式,可以尝试对用户进行补救或进一步显示错误。


1
2017-08-27 10:06





如果您正在考虑这些条款,我认为您真的过度使用例外!

返回表示失败的值是完全可以的,而不是抛出异常。通常情况会更好。当您无法在抽象级别恢复时,最好使用例外,但不应将它们用作控制流的主要方法,否则您的程序将变得非常难以阅读。

Web服务不返回异常,它返回返回代码和消息。我会存储一些有用的表示,它表示返回的信息,并返回视图的列表或任何将要查看它的列表。


1
2017-08-31 17:16





我会调查一下 的DTO 为了这种任务。 DTO还可以包括关于持久性是否成功的信息以及其他类型的“元数据”。


0
2017-08-31 13:43





我可能会返回一个类型的结果图 Map<Person,Future<String>> 从我的 getLotteryNumbers<Collection<Person>> 服务。

然后我会遍历地图并使用 Future.get() 获得彩票号码或抛出的异常。

在我的一些服务中,我喜欢将所有调用实现为单项调用,然后在我的服务中使用逻辑来批量处理它们并将它们作为一个组进行处理。批处理使用a实现 LinkedBlockingQueue 和一个投票线程。

在这种情况下,我返回一个 Future<Thing> 等待批处理结果可用 CountdownLatch

在实践中查看Java Concurrency,了解这些组件如何协同工作 http://jcip.net/ 


0
2017-08-31 10:34





另一种方法,特别是对于高吞吐量系统,将使用基于队列的设计,其中处理实体将对对象执行操作,然后基于结果将对象放入不同的队列中以供其他实体进行额外处理,然后继续。这将减少由于需要额外处理而产生的瓶颈,例如:处理缺货产品的订单


0
2017-09-02 09:27





理想情况下,对您的Web服务的调用应该是这样的。

List<Person> selectedPeople = ... //fetch list of people
callLotteryNumberWebService(selectedPeople.ToArray );

为每个人拨打网络服务费用很高。在服务器端,您需要遍历列表并执行操作。 服务器端代码可以抛出2个异常: BulkOperationFailedException - 如果由于db down或配置文件丢失而导致致命错误。无法进一步处理。 BulkOperationException - 它包含一个与人有关的异常数组。您可以保留一些id以唯一地引用每个对象。 你的代码将是这样的:

List<Person> selectedPeople = ... // fetch list of people 

try{
    callLotteryNumberWebService(selectedPeople.ToArray);
}catch  (BulkOperationFailedException e) {
    SOP("Some config file missing/db down.No person records processed")
}catch(BulkOperationException e)  {
    UserDefinedExceptions us =  e.getExceptions()
    foreach(exception ein us)   {
        // read unique id to find which person object failed
    }
}

construct msg based on which personobject succeeded and which failed

当没有抛出异常时,操作被认为是成功的。您可以为失败设置自定义错误代码,而不是使用UserDefined异常。 在服务器端构造BulkOperationException很棘手。其次,您需要将从服务器端抛出的错误归类为BulkOperationFailedException和BulkOperationException。这就是我在我的一个项目中处理的方式


0
2017-09-07 08:43