我从未遇到过写得好的业务层。有什么建议吗?

12

我观察到一些优秀的代码片段,用于定义规则、验证、业务对象(实体)等方面,但是我必须承认,我从未见过一个完整的、优秀且写得好的业务层。

我明白我不喜欢什么,但不知道什么是好的。

有人能指出一些好的面向对象的业务层(或优秀的业务对象),或告诉我如何评判业务层以及何谓优秀?

谢谢

9个回答

14
我从未遇到过良好编写的业务层。
这是Alex Papadimoulis的看法:
如果你仔细想想,软件应用程序中几乎每一行代码都是业务逻辑:
- Customers数据库表,包含CustomerNumber(CHAR-13)、ApprovedDate(DATETIME)和SalesRepName(VARCHAR-35)列:业务逻辑。如果不是,那么它只会是Table032,有Column01、Column02和Column03。 - 给首次客户打九折的子例程:绝对是业务逻辑。而且希望不是软编码。 - 将过期发票突出显示为红色的代码也是业务逻辑。Internet Explorer肯定不会寻找字符串“未支付”和“超过30天”,然后说,“嘿,这样做的话,#990000的背景看起来很好看!”
那么如何将所有这些业务逻辑封装在单一的代码层中呢?当然是通过可怕的架构和糟糕的代码!
通过暗示系统架构应包括专门用于业务逻辑的层,许多开发人员采用各种可怕的聪明技巧来实现这个目标。结果总是灾难性的。

说实话,如果需要几个业务层才能实现某些功能,我认为这仍然可以是一种优雅的方法。这些层之间应该有一致性,这将使其成为一个好的层次结构。 - Scott McKenzie
哇,我简直不敢相信这个完全错误的引用收到了多少票。现在如果你在数据层中没有使用ORM框架,那么你可能做错了 - 对于数据层来说,它实际上可以是Table032、Column01、Column02 - 这并不是业务逻辑。第二点是业务逻辑,很好。第三点是100%的表示层,而不是业务逻辑。你过期的发票应该有某种状态标志或IsPastDue属性,表示层应该正确地呈现为红色或其他颜色。再次强调,表示层不是业务逻辑。 - BenSwayne

6
我想这是因为通常来说,业务逻辑是任意和糟糕的。垃圾进,垃圾出。
此外,大多数真正好的业务层可能是专有的。;-)

我必须同意。有人知道任何好的、非专有的例子吗? - Scott McKenzie
+1. 我知道至少两个好的例子,但由于我受到保密协议的约束,无法透露出处。 - Milan Babuškov

5
优秀的业务层应该在充分进行领域分析后进行设计。如果您能够捕捉业务语义并将其与任何实现隔离开来,无论是数据存储还是任何特定应用程序(包括演示),那么逻辑应该经过良好的分解并且可以在不同的上下文中重复使用。
就像优秀的数据库模式设计应该捕捉业务语义并将自己与任何应用程序隔离开来一样,业务层也应该做到这一点。即使数据库模式和业务层描述相同的实体和概念,两者也应该在不同的上下文中可用——除非模式不反映当前业务,否则数据库模式不应更改。只要通过一个中间层进行抽象,业务层就可以使用任何存储模式。例如,ADO.NET Entity框架允许您设计一个概念模式,它映射到业务层,并具有与存储模式的单独映射,可以在不重新编译业务对象层或概念层的情况下进行更改。
如果业务方面的人员可以查看使用业务层编写的代码,并对正在进行的工作有大致的了解,那么这可能是正确设计对象的一个很好的指示——您已经成功地在问题域中传达了解决方案,而没有用解决方案域的工件使其变得模糊不清。

我已经开始研究领域驱动设计和创建有意义的实体的方法似乎非常关键;我认为这被称为“普遍流畅性”。这个想法听起来很不错,但我对实现还不确定。非常好的回答,谢谢。 - Scott McKenzie

4
我总是感到陷入了困境。理想情况下,您的业务逻辑不应该关心与数据库或UI相关的问题。
但实际上,像主键和外键之类的东西仍然会带来问题。即使像Entity Framework这样的工具也无法完全消除这种情况。将作为POST数据传递的ID转换为相应的对象并将其传递给业务层再传递给数据层进行剥离可能极其低效。
即使是NoSQL数据库也存在问题。它们往往返回完整的对象模型,但通常会返回多余的信息,并且会因为在假设对象模型不会更改的情况下导致问题。而主键在NoSQL数据库中同样存在。
此外,还有代码重用的问题。数据层通常会返回完全填充的对象,包括特定表格或表格中的每一列。然而,业务逻辑通常只关心其中的有限子集。这导致特定的数据传输对象可以只携带相关数据。当然,你需要在表示之间进行转换,所以你要创建一个映射器类。然后,当你保存时,你需要将这些较小的对象转换回完整的数据库表示或执行部分更新(即另一个SQL命令)。
所以我看到很多业务层类接受直接映射到数据库表格的对象(数据传输对象)。我也看到很多业务层接受原始的UI值(演示对象)。在计算过程中向数据库调用所需数据也不是不寻常的事情。试图提前获取它可能是低效的(考虑一下if语句如何影响获取的数据),而懒加载的值会导致很多魔法或意外的调用数据库。
最近,我一直在尝试先编写“核心”代码。这是执行实际业务逻辑的代码。但是,很多时候当我查看别人的代码时,我会问一个问题:“但是,它在哪里执行[业务规则]?”因为业务逻辑被关于获取数据、转换数据等问题的担忧所淹没了。因此,现在我首先实现逻辑,在弄清楚需要哪些数据时,将其作为参数添加或将其添加到参数对象中。使得其余代码适应这个新接口通常归于某种中介类。
就像我所说的,编写业务层时必须考虑很多因素,包括性能。上述方法在我没有版本控制或数据库模式权限的情况下非常有用。我只有对要求的理解并在黑暗的房间里工作。
使用依赖注入可以帮助你预先设计良好的架构。尝试思考如何在不使用数据库或其他服务的情况下测试代码。这也适用于可以在多个上下文中运行的小型可重复使用的类。
结论: 我的结论是,真正完美的业务层是不存在的。即使在同一个应用程序中,有时候只有一种方法能够在90%的情况下工作。我们所能做的最好办法就是尝试编写最简单有效的代码。很长一段时间里,我一直避免使用DTOs,并将ADO.NET数据行封装为对象,以便更新可以立即记录在底层的数据表中。但这是个巨大的错误,因为我无法复制对象,而约束条件会导致异常在奇怪的时间被抛出。我这样做只是为了避免显式地设置参数值。

2

1
很抱歉,但Fowler的建议太过理论化和玄学,已经失去了其适用性。 - dacracot
我必须在这里同意dacracot的观点。Fowler被高估了。 - Draemon
Fowler的DSL书相当不错。它在构建业务层方面的适用性有限。它更倾向于创建语言,以定制业务逻辑,供业务分析师编写。DDD书对这个问题更加适用。即使是Fowler的Analysis Patterns或POEAA也更加适用。 - Travis Parks

2

如果你是微软的开发者,学习和使用CSLA.Net对我很有帮助。虽然我从未实现过"纯"的CSLA应用程序,但我已经使用了架构中提出的许多思想。

最好的方法是不断寻找那个神奇的解决方案,并使用最适合解决问题的思路。保持简单。


2

我发现的一个问题是,即使您拥有设计良好的业务层,也很难阻止业务逻辑泄漏,而开发工具往往会鼓励这种情况。例如,一旦您向ASP.NET WebForm添加验证控件,就已经让业务逻辑泄漏到视图中了。验证应该在业务层中进行,并且只显示其结果在视图中。一旦您向数据库添加约束,那么您的数据库中也存在业务逻辑。尽管如此,DBA类型通常会强烈反对这一点。


0

我也不是。我们在应用程序中不创建业务层,而是使用MVC-ARS。业务逻辑嵌入到(S)状态机和(A)操作中。


你能再详细描述一下吗? - Scott McKenzie
我们实现了两层,一层是Web服务器,另一层是数据库服务器。每个层都遵循MVC模式。然而,在数据库中,该模式被称为ARS以保持词汇的一致性。表示非常类似于视图,状态机非常类似于控制器,但是... - dacracot
它们在模型和操作方面有所不同。模型等于服务器之间的表示。操作更容易理解为处理数据的SQL语句。Web服务器中的控制器更像是路由器,将操作推送到其他组件。清楚吗? - dacracot

-1
可能是因为实际上我们从未能够完全将业务逻辑与“过程”、输入、输出、接口分离,这最终让人们很难处理抽象的概念,更不用说将其与现实联系起来了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接