我已经阅读了scott millett的专业asp.net设计模式这本书,在他的例子中,他在Validate()方法中验证了他的业务逻辑,如果任何破坏的规则被破坏,他们会被添加到集合中并且服务层调用一个名为GetBrokenRules()的模型上的方法。
现在我还阅读了几本关于DDD的书籍,博客和论坛,它说在DDD中一个对象永远不会进入无效状态。
我看到的有关DDD的所有示例在业务规则被破坏时抛出错误而不是传回一组破坏的规则。我甚至已经下载了scott millett的最新源代码,他现在已经改变了他的代码,现在抛出错误而不是传回破坏的规则列表。我也看到了与其他DDD代码样本相同的方法。
我正在与一个团队成员进行辩论,他们认为抛出错误是资源昂贵的,我们应该不抛出错误但返回像我们目前那样的破坏规则的集合。但是,通过这样做,我们传递的是一个无效的对象,因为它有狡猾的数据,我们只在最后检查它的破坏规则。
我只是想到其他人在这个问题上的意见。一旦业务规则失败,我们是否应该抛出错误?如果是这样,你能否强调这样做的优点和缺点。 我不知道.net中资源昂贵的投掷错误是多少,所以我不能反对这一点,但我想知道这是否也是个人opinoin的问题,而不是编码标准。
麦克
答案 0 :(得分:3)
当实际抛出异常时,异常才会很昂贵。
由于您应该事先验证逻辑,因此异常应该很少。如果情况并非如此,那你做错了。
到目前为止,异常是通知模型处于无效状态的最佳机制。如果申请不要终止,他们需要明确处理。
使用错误集合/返回值不是一个好习惯,因为这些可以(很多时候)会被使用代码忽略。
答案 1 :(得分:1)
非常好的讨论。它唤醒了旧的讨论主题。我已经测试了几种方法,发现混合物可以是一个很好的选择。在验证服务方法或ctor的输入参数时,我使用异常。如果验证知识不是实体责任的一部分,我将Validators与Visitor Pattern一起使用来检查域实体。如果您是Persistence-ignorance-religion(我是)的一部分,您可以使用位于基础架构层中的验证器并验证它是否可以持久化(或者可能通过适配器导出到另一个系统......)。但是,我在我的实体上使用非常少的公共自动属性只是为了使实体难以将实体置于无效状态。我还让实体验证自己的域范围/责任(试图在这里跟踪SRP ......)。
抛出异常作为通知其他部分/层的一般解决方案对于每种情况可能都不是最佳的,但可能对某些情况而言。
/干杯
答案 2 :(得分:0)
@Oded所说的完全有效。
然而,还有另一种处理验证的方法,不会抛出异常而不会传递无效对象。简而言之,您始终通过工厂方法创建对象。如果一切都很好,则返回isntance,否则返回null。当然,这意味着你必须在创建对象时检查null,但这是关于它的。 工厂方法(可以是服务的一部分)可以将工厂可以设置错误的IValidationDictionary(不是BCL接口)作为参数。但我们在这里只讨论用户的输入数据(格式化验证)。这里没有业务规则。如果该对象将在其状态无效的上下文中使用,则它必须抛出异常。
你可以通过在对象上或像CanDoThat()这样的聚合根上使用方法来避免99%。如果object.CanDoThat(context)这样做并避免异常。如果由于并发事物在平均时间内发生变化,则可能会触发异常。
我通常有以下工作流程:
如果不能则有2例:
当然,您可能遇到聚合根级别一切正常的情况,但由于数据库约束,它在存储库级别失败。这意味着存储库抛出一个异常,我处理(因为我发现这种情况发生)。如果用户需要响应,那么我将抛出一个特定的异常来表示将设置View的ModelState的错误。
现在这是一个棘手的情况,因为看起来我正在使用异常来控制代码流,但事实并非如此。虽然我希望db抛出一个违反异常的约束,但它仍然是一个例外情况,因为每次都不会发生这种情况,并且这不是用户可以避免的情况。我正在抛出特定的异常,因为第一个是持久性相关的东西,但我的控制器不知道它所以我必须以它理解的方式传达这种异常状态。
好的,长期以来,但是想法是一些错误,而预期仍然是例外,他们需要被视为这样。但是,如果ipnut数据上存在格式验证错误或输入数据以特定方式无效,则应在第一个反腐败层中处理,控制器本身并不需要例外。控制器不应该让无效输入继续进行。如果它向前发展那么它就是一个错误,一个例外情况。
希望,我不是很困惑:)
答案 3 :(得分:0)
为什么不在破坏规则列表中抛出异常?
与构建规则的成本相比,弄清楚如何处理它们,告诉用户有关错误以及介于两者之间的所有网络流量,异常几乎没有任何成本。