141

我真的需要看到一些关于当前接受的企业应用程序设计范式优点的诚实、深思熟虑的辩论。

我不相信实体对象应该存在。

我所说的实体对象是指我们倾向于为我们的应用程序构建的典型事物,例如“Person”、“Account”、“Order”等。

我目前的设计理念是这样的:

  • 所有数据库访问都必须通过存储过程完成。
  • 每当您需要数据时,调用存储过程并遍历 SqlDataReader 或 DataTable 中的行

(注意:我还使用 Java EE 构建了企业应用程序,Java 人员请用等价物代替我的 .NET 示例)

我不反对OO。我为不同的目的编写了很多类,而不是实体。我承认我写的大部分类都是静态辅助类。

我不是在制造玩具。我说的是跨多台机器部署的大型、大容量事务应用程序。Web 应用程序、Windows 服务、Web 服务、b2b 交互,应有尽有。

我用过 OR 映射器。我已经写了几篇了。我使用了 Java EE 堆栈、CSLA 和其他一些等价物。我不仅使用过它们,而且还在生产环境中积极开发和维护这些应用程序。

我得出了久经考验的结论,实体对象正在阻碍我们,我们的生活将如此得多。

考虑这个简单的例子:你接到一个关于你的应用程序中某个页面不能正常工作的支持电话,也许其中一个字段没有像它应该的那样被持久化。使用我的模型,分配来查找问题的开发人员恰好打开了 3 个文件。一个 ASPX、一个 ASPX.CS 和一个带有存储过程的 SQL 文件。该问题可能是存储过程调用中缺少参数,需要几分钟才能解决。但是对于任何实体模型,您总是会启动调试器,开始单步执行代码,最终您可能会在 Visual Studio 中打开 15-20 个文件。当你走到栈底时,你已经忘记了从哪里开始。我们一次只能在脑海中保留这么多东西。软件非常复杂,无需添加任何不必要的层。

开发复杂性和故障排除只是我抱怨的一方面。

现在让我们谈谈可扩展性。

开发人员是否意识到,每次编写或修改与数据库交互的任何代码时,他们都需要彻底分析对数据库的确切影响?不仅仅是开发副本,我的意思是模拟生产,所以您可以看到您现在为您的对象需要的附加列刚刚使当前查询计划无效,并且在 1 秒内运行的报告现在需要 2 分钟,只是因为您在选择列表中添加了一列?事实证明,您现在需要的索引太大以至于 DBA 将不得不修改文件的物理布局?

如果你让人们通过抽象离物理数据存储太远,他们将对需要扩展的应用程序造成严重破坏。

我不是狂热者。如果我错了,我可以确信,也许我错了,因为对 Linq to Sql、ADO.NET EF、Hibernate、Java EE 等的推动力如此之大。请仔细考虑你的回答,如果我遗漏了什么我真的很想知道它是什么,以及为什么我应该改变我的想法。

[编辑]

看起来这个问题突然又活跃起来了,所以现在我们有了新的评论功能,我已经直接评论了几个答案。感谢您的回复,我认为这是一个健康的讨论。

我可能应该更清楚我在谈论企业应用程序。我真的无法评论,比如说,在某人的桌面上运行的游戏或移动应用程序。

为了回应几个类似的答案,我必须把一件事放在首位:正交性和关注点分离经常被引用为使用实体/ORM 的理由。对我来说,存储过程是我能想到的关注点分离的最好例子。如果您不允许通过存储过程以外的所有其他方式访问数据库,那么理论上您可以重新设计整个数据模型并且不会破坏任何代码,只要您维护存储过程的输入和输出即可。它们是合同编程的完美示例(只要您避免“选择 *”并记录结果集)。

问一个在这个行业工作了很长时间并使用过长期应用程序的人:在数据库存在的情况下,有多少应用程序和 UI 层来了又去?当有 4 或 5 个不同的持久层生成 SQL 以获取数据时,调整和重构数据库有多难?你什么都改变不了!ORM 或任何生成 SQL 的代码都将您的数据库锁定在石头上

4

41 回答 41

59

我认为这取决于应用程序的“逻辑”有多复杂,以及您在哪里实现了它。如果你所有的逻辑都在存储过程中,而你的应用程序所做的只是调用这些过程并显示结果,那么开发实体对象确实是浪费时间。但是对于对象之间具有丰富交互的应用程序,并且数据库只是一种持久性机制,拥有这些对象可能是有价值的。

所以,我想说没有一刀切的答案。开发人员确实需要意识到,有时,过于 OO 会导致比解决的问题更多的问题。

于 2008-08-20T20:56:51.520 回答
27

Theory 认为高度内聚、松散耦合的实现是前进的方向。

所以我想你是在质疑这种方法,即分离关注点。

我的 aspx.cs 文件是否应该与数据库交互、调用存储过程并理解 IDataReader?

在团队环境中,尤其是在处理应用程序的 aspx 部分的技术人员较少的情况下,我不需要这些人能够“接触”这些东西。

将我的域与数据库分离可以保护我免受数据库结构变化的影响,这肯定是件好事吗?当然,数据库的有效性是绝对重要的,所以让最擅长这些东西的人在一个地方处理这些东西,尽可能少地影响系统的其余部分。

除非我误解了您的方法,否则数据库中的一项结构更改可能会对您的应用程序表面产生很大的影响。我看到这种关注点分离使我和我的团队能够最大限度地减少这种情况。此外,团队的任何新成员都应该更好地理解这种方法。

此外,您的方法似乎提倡将应用程序的业务逻辑驻留在数据库中?这对我来说感觉不对,SQL 真的很擅长查询数据,而不是,恕我直言,表达业务逻辑。

有趣的想法,虽然感觉离aspx中的SQL只有一步之遥,从我糟糕的旧非结构化asp时代开始,让我充满恐惧。

于 2008-08-20T21:03:50.317 回答
26

一个原因 - 将您的域模型与数据库模型分开。

我所做的是使用测试驱动开发,所以我首先编写我的 UI 和模型层,然后模拟数据层,所以 UI 和模型是围绕特定领域的对象构建的,然后我将这些对象映射到我正在使用的任何技术数据层。让数据库结构决定应用程序的设计是个坏主意。在可能的情况下,先编写应用程序并让它影响数据库的结构,而不是反过来。

于 2008-08-20T19:43:19.777 回答
21

对我来说,归结为我不希望我的应用程序关心数据的存储方式。我可能会因为这样说而受到打击……但是您的应用程序不是您的数据,数据是应用程序的工件。我希望我的应用程序考虑客户、订单和项目,而不是像 DataSets、DataTables 和 DataRows 这样的技术......因为谁知道它们会存在多久。

我同意总有一定数量的耦合,但我更喜欢这种耦合向上而不是向下。我可以更容易地调整一棵树的四肢和叶子,而不是改变它的树干。

我倾向于为报告保留存储过程,因为查询确实比应用程序的一般数据访问更糟糕。

我也倾向于在该场景的早期进行适当的单元测试,就像一列没有被持久化可能不是问题。

于 2008-08-20T21:13:39.450 回答
16

埃里克,你死定了。对于任何真正可扩展/易于维护/健壮的应用程序,唯一真正的答案是放弃所有垃圾并坚持基础。

我的职业生涯遵循了类似的轨迹,并得出了相同的结论。当然,我们被认为是异端,看起来很有趣。但我的东西很好用。

应该怀疑地看待每一行代码。

于 2008-08-22T04:49:15.760 回答
10

我想用一个类似于你提出的例子来回答。

在我的公司中,我必须为产品构建一个简单的 CRUD 部分,我构建了我所有的实体和一个单独的 DAL。后来另一位开发人员不得不更改相关表,他甚至重命名了几个字段。我必须更改以更新表单的唯一文件是该表的 DAL。

(在我看来)实体给项目带来的是:

正交性:一层中的更改可能不会影响其他层(当然,如果您对数据库进行巨大更改,它会波及所有层,但大多数小的更改不会)。

可测试性:您可以在不接触数据库的情况下测试您的逻辑。这会提高测试的性能(允许您更频繁地运行它们)。

关注点分离:在大型产品中,您可以将数据库分配给 DBA,他可以优化数据库。将模型分配给具有设计所需知识的业务专家。将个人表单分配给在网络表单等方面更有经验的开发人员。

最后我想补充一点,大多数 ORM 映射器都支持存储过程,因为这就是您正在使用的。

干杯。

于 2008-08-20T21:25:25.537 回答
8

我认为你可能在这个话题上“咬得比你能嚼得更多”。Ted Neward 称其为“计算机科学的越南”并不是轻率。

我可以绝对向您保证的一件事是,它将改变任何人对此事的看法,正如无数其他博客、论坛、播客等经常证明的那样。

就一个有争议的话题进行公开讨论和辩论当然是可以的,只是这个话题已经做了很多次,以至于双方“双方”都同意不同意并开始编写软件。

如果您想进一步阅读这两个方面的内容,请参阅 Ted 博客、Ayende Rahein、Jimmy Nilson、Scott Bellware、Alt.Net、Stephen Forte、Eric Evans 等上的文章。

于 2008-09-12T04:10:30.873 回答
7

@Dan,对不起,这不是我要找的那种东西。我知道这个理论。你的说法“是一个非常糟糕的主意”没有一个真实的例子支持。我们正在尝试用更少的时间、更少的人、更少的错误来开发软件,并且我们希望能够轻松地进行更改。根据我的经验,您的多层模型在上述所有类别中都是负面的。尤其是关于让数据模型成为你做的最后一件事。从第一天开始,物理数据模型就必须是一个重要的考虑因素。

于 2008-08-20T20:46:59.937 回答
4

实体对象可以方便在应用层进行缓存。祝你好运缓存数据读取器。

于 2008-08-22T04:51:23.897 回答
4

We should also talk about the notion what entities really are. When I read through this discussion, I get the impression that most people here are looking at entities in the sense of an Anemic Domain Model. A lot of people are considering the Anemic Domain Model as an antipattern!

There is value in rich domain models. That is what Domain Driven Design is all about. I personally believe that OO is a way to conquer complexity. This means not only technical complexity (like data-access, ui-binding, security ...) but also complexity in the business domain!

If we can apply OO techniques to analyze, model, design and implement our business problems, this is a tremendous advantage for maintainability and extensibility of non-trivial applications!

There are differences between your entities and your tables. Entities should represent your model, tables just represent the data-aspect of your model!

It is true that data lives longer than apps, but consider this quote from David Laribee: Models are forever ... data is a happy side effect.

Some more links on this topic:

于 2008-11-05T10:25:54.857 回答
4

我发现你的问题真的很有趣。
通常我需要实体对象来封装应用程序的业务逻辑。将这个逻辑推入数据层会非常复杂和不充分。
你会怎么做才能避免这些实体对象?你有什么解决方案?

于 2008-08-20T20:44:15.107 回答
4

除了抽象和松耦合之外,实体对象还有其他很好的理由。我最喜欢的一件事是使用 DataReader 或 DataTable 无法获得的强类型。另一个原因是,如果做得好,适当的实体类可以使代码更易于维护,方法是使用一流的结构来表示任何查看代码的人都可能理解的领域特定术语,而不是使用一堆带有字段名称的字符串用于索引 DataRow。存储过程与 ORM 的使用实际上是正交的,因为许多映射框架让您能够映射到存储过程。

我不会认为 sprocs + datareaders 可以替代一个好的 ORM。使用存储过程,您仍然受到过程类型签名的约束并紧密耦合,该类型签名使用与调用代码不同的类型系统。存储过程可以进行修改以适应其他选项和架构更改。在架构可能发生更改的情况下,存储过程的替代方法是使用视图——您可以将对象映射到视图,然后在更改视图时将视图重新映射到基础表。

如果您的经验主要包括 Java EE 和 CSLA,我可以理解您对 ORM 的厌恶。您可能想看看 LINQ to SQL,它是一个非常轻量级的框架,主要是与数据库表的一对一映射,但通常只需要较小的扩展即可将它们变成成熟的业务对象。LINQ to SQL 还可以将输入和输出对象映射到存储过程的参数和结果。

ADO.NET Entity 框架具有额外的优势,即可以将您的数据库表视为相互继承的实体类,或将多个表中的列聚合到一个实体中。如果您需要更改架构,您可以更改从概念模型到存储架构的映射,而无需更改实际的应用程序代码。同样,这里可以使用存储过程。

我认为企业中更多的 IT 项目失败是因为代码的不可维护性或开发人员的生产力低下(例如,可能发生在 sproc-writing 和 app-writing 之间的上下文切换)而不是应用程序的可伸缩性问题。

于 2008-09-12T03:36:20.783 回答
4

真的很有趣的问题。老实说,我无法证明为什么实体是好的。但我可以分享我的看法,为什么我喜欢他们。像这样的代码

void exportOrder(Order order, String fileName){...};

不关心订单来自哪里——来自数据库、来自网络请求、来自单元测试等。它使这个方法更明确地声明它到底需要什么,而不是采用 DataRow 并记录它期望拥有哪些列以及它们应该具有哪些类型是。如果您以某种方式将其实现为存储过程,则同样适用 - 您仍然需要将记录 ID 推送给它,而它不需要存在于 DB 中。

此方法的实现将基于 Order 抽象来完成,而不是基于它在 DB 中的精确呈现方式。我实现的大多数此类操作实际上并不依赖于这些数据的存储方式。我确实了解某些操作需要与数据库结构耦合以提高性能和可扩展性,就我的经验而言,它们并没有太多。根据我的经验,通常知道 Person 有 .getFirstName() 返回 String 和 .getAddress() 返回 Address 以及 address 有 .getZipCode() 等就足够了 - 并且不关心哪些表被用于存储该数据.

如果您必须处理您所描述的此类问题,例如当额外的分栏报告性能时,那么对于您的任务,数据库是一个关键部分,您确实应该尽可能接近它。虽然实体可以提供一些方便的抽象,但它们也可以隐藏一些重要的细节。

可伸缩性是这里有趣的一点——大多数需要巨大可伸缩性的网站(如 facebook、livejournal、flickr)倾向于使用 DB-ascetic 方法,当尽可能少地使用 DB 并且通过缓存解决可伸缩性问题时,尤其是通过 RAM 使用。http://highscalability.com/上有一些有趣的文章。

于 2008-11-11T10:42:07.693 回答
3

我还想补充Dan 的回答,即分离两个模型可以使您的应用程序能够在不同的数据库服务器甚至数据库模型上运行。

于 2008-08-20T20:49:14.127 回答
3

You might find this post on comp.object interesting.

I'm not claiming to agree or disagree but it's interesting and (I think) relevant to this topic.

于 2008-09-19T23:13:40.480 回答
3

如果您需要通过负载平衡多个 Web 服务器来扩展您的应用程序怎么办?您可以在所有 Web 服务器上安装完整的应用程序,但更好的解决方案是让 Web 服务器与应用程序服务器通信。

但是如果没有任何实体对象,他们就没有太多可谈的了。

我并不是说如果它是一个简单的、内部的、寿命短的应用程序,你不应该编写单体应用程序。但是一旦它变得适度复杂,或者它应该持续很长时间,你真的需要考虑一个好的设计。

这样可以节省维护它的时间。

通过将应用程序逻辑与表示逻辑和数据访问分开,并在它们之间传递 DTO,您可以将它们解耦。允许他们独立改变。

于 2008-08-20T21:04:41.483 回答
3

A question: How do you handle disconnected applications if all your business logic is trapped in the database?

In the type of Enterprise application I'm interested in, we have to deal with multiple sites, some of them must be able to function in a disconnected state.
If your business logic is encapsulated in a Domain layer that is simple to incorporate into various application types -say, as a dll- then I can build applications that are aware of the business rules and are able, when necessary, to apply them locally.

In keeping the Domain layer in stored procedures on the database you have to stick with a single type of application that needs a permanent line-of-sight to the database.

It's ok for a certain class of environments, but it certainly doesn't cover the whole spectrum of Enterprise applications.

于 2008-12-29T03:48:26.577 回答
2

@jdecuyper,我经常对自己重复的一个格言是“如果您的业务逻辑不在您的数据库中,那么它只是一个建议”。我认为保罗尼尔森在他的一本书中说过。应用层和 UI 来来去去,但数据通常存在很长时间。

如何避免实体对象?存储过程居多。我也坦率地承认,无论您是否愿意,业务逻辑都倾向于贯穿应用程序的所有层。一定程度的耦合是固有的,也是不可避免的。

于 2008-08-20T20:54:15.190 回答
2

我最近一直在思考同样的事情;有一段时间我是 CSLA 的重度用户,我喜欢说“您的所有业务逻辑(或至少尽可能多地)都封装在业务实体中”的纯粹性。

我已经看到业务实体模型在数据库设计与您处理数据的方式不同的情况下提供了很多价值,这在很多业务软件中都是如此。

例如,“客户”的概念可能包含客户表中的一条主记录,结合客户下的所有订单,以及客户的所有员工及其联系信息,以及客户表中的一些属性。可以从查找表中确定客户及其子项。从开发的角度来看,能够将客户作为一个单一的实体来工作真的很好,因为从业务的角度来看,客户的概念包含所有这些东西,并且这些关系可能会或可能不会在数据库中强制执行。

虽然我很欣赏“如果您的业务规则不在您的数据库中,这只是一个建议”这句话,但我也认为您不应该将数据库设计为强制执行业务规则,而应该将其设计为高效、快速和规范化.

也就是说,正如上面其他人所指出的,没有“完美的设计”,工具必须适合工作。但是使用业务实体确实有助于维护和提高生产力,因为您知道去哪里修改业务逻辑,并且对象可以以直观的方式对现实世界的概念进行建模。

于 2008-08-20T21:18:19.610 回答
2

埃里克,

没有人阻止您选择您希望的框架/方法。如果你打算走“数据驱动/存储过程驱动”的道路,那么一定要去做!尤其是如果它真的、真的可以帮助您按规范和按时交付应用程序。

需要注意的是(与您的问题相反),您的所有业务规则都应该在存储过程中,并且您的应用程序只不过是一个瘦客户端。

话虽如此,如果您在 OOP 中执行应用程序,则适用相同的规则:保持一致。遵循 OOP 的原则,其中包括创建实体对象来表示您的域模型。

这里唯一真正的规则是一致性这个词。没有人阻止您以数据库为中心。没有人阻止您执行老式结构化(又名功能/程序)程序。见鬼,没有人阻止任何人编写 COBOL 风格的代码。但是,如果应用程序希望获得任何程度的成功,就必须非常非常一致地走这条路。

于 2008-09-12T02:45:15.960 回答
2

我真的不确定您认为“企业应用程序”是什么。但是我得到的印象是您将其定义为内部应用程序,其中 RDBMS 将一成不变,并且系统不必与任何其他系统(无论是内部还是外部)互操作。

但是,如果您有一个包含 100 个表的数据库,这相当于每个表的 4 个存储过程,仅用于基本的 CRUD 操作,即 400 个需要维护且不是强类型的存储过程,因此容易出现拼写错误,也不能进行单元测试. 当你得到一个新的 CTO,他是一名开源布道者并且想要将 RDBMS 从 SQL Server 更改为 MySql 时会发生什么?

今天的很多软件,无论是企业应用程序还是产品都在使用 SOA,并且对公开 Web 服务有一些要求,至少我现在和一直参与的软件是这样的。使用您的方法,您最终会暴露一个序列化的 DataTable 或 DataRows。现在,如果客户端被保证是 .NET 并且在内部网络上,这可能被认为是可以接受的。但是当客户端未知时,您应该努力设计一个直观的 API,并且在大多数情况下您不希望公开完整数据库模式。我当然不想向 Java 开发人员解释什么是 DataTable 以及如何使用它。还有带宽和负载大小以及序列化DataTables的考虑,DataSets非常重。

软件设计没有灵丹妙药,它实际上取决于优先级在哪里,对我来说,它是单元可测试代码和松散耦合的组件,可以很容易地被任何客户端使用。

只是我的 2 美分

于 2008-11-11T09:29:06.657 回答
2

我想为OO和RDB之间的距离问题提供另一个角度:历史。

任何软件都有一个现实模型,它在某种程度上是对现实的抽象。没有计算机程序可以捕捉到现实的所有复杂性,而编写程序只是为了解决现实中的一系列问题。因此,任何软件模型都是现实的简化。有时,软件模型迫使现实自我缩减。就像您希望汽车租赁公司为您预订任何汽车,只要它是蓝色且有合金的,但操作员无法满足您的要求,因为您的请求不适合计算机。

RDB 来自一个非常古老的将信息放入表格的传统,称为会计。会计是在纸上完成的,然后是穿孔卡片,然后是计算机。但会计已经是现实的缩影。会计已经迫使人们长期遵循它的系统,以至于它已成为公认的现实。这就是为什么制作会计计算机软件相对容易的原因,在计算机出现之前很久,会计就已经有了它的信息模型。

鉴于良好会计系统的重要性,以及您从任何业务经理那里获得的认可,这些系统已经变得非常先进。数据库基础现在非常稳固,没有人会犹豫将重要数据保存在如此值得信赖的地方。

我猜想,当人们发现现实的其他方面比会计更难建模(这已经是一个模型)时,OO 一定会出现。OO 已经成为一个非常成功的想法,但是 OO 数据的持久性相对不发达。RDB/会计很容易获胜,但 OO 是一个更大的领域(基本上所有不是会计的)。

我们中的许多人都想使用 OO,但我们仍然希望安全地存储我们的数据。有什么比以与受人尊敬的会计系统相同的方式存储我们的数据更安全的呢?这是一个诱人的前景,但我们都遇到了同样的陷阱。与 RDB 行业的巨大努力相比,很少有人会费心去考虑 OO 持久性,因为 RDB 行业受益于会计的传统和地位。

Prevayler 和 db4o 是一些建议,我敢肯定还有其他一些我没有听说过,但似乎没有一个像休眠一样得到了一半的新闻。

对于多用户应用程序,尤其是 Web 应用程序,将对象存储在良好的旧文件中似乎并没有被认真对待。

在我每天努力弥合 OO 和 RDB 之间的鸿沟时,我尽可能多地使用 OO,但尽量减少继承。我不经常使用SP。我只会在看起来像会计的方面使用高级查询的东西。

当鸿沟永久关闭时,我会很高兴。我认为当 Oracle 推出“Oracle Object Instance Base”之类的东西时,解决方案就会出现。要真正流行起来,它必须有一个令人放心的名字。

于 2008-12-03T08:24:58.083 回答
1

目前没有太多时间,但就在我的脑海中......

实体模型允许您为数据库(和其他可能的系统)提供一致的接口,甚至超出了存储过程接口的功能。通过使用企业范围的业务模型,您可以确保所有应用程序一致地影响数据,这是非常重要的事情。否则你会得到糟糕的数据,这简直是邪恶的。

如果您只有一个应用程序,那么您就没有真正的“企业”系统,无论该应用程序或您的数据有多大。在这种情况下,您可以使用类似于您所说的方法。如果您决定在未来扩展您的系统,请注意将需要做的工作。

不过,您应该记住以下几点(IMO):

  1. 生成的 SQL 代码不好(有例外)。抱歉,我知道很多人认为它可以节省大量时间,但我从来没有发现一个系统可以生成比我编写的代码更高效的代码,而且代码通常很糟糕。您还经常最终生成大量从未使用过的 SQL 代码。这里的例外是非常简单的模式,比如查找表。不过很多人都被它迷住了。
  2. 实体 <> 表(甚至是逻辑数据模型实体)。数据模型通常具有应尽可能靠近数据库执行的数据规则,其中可能包括有关表行如何相互关联的规则或其他类似规则,这些规则对于声明性 RI 来说过于复杂。这些应该在存储过程中处理。如果你所有的存储过程都是简单的 CRUD 过程,你就不能这样做。最重要的是,CRUD 模型通常会产生性能问题,因为它不会最小化通过网络到数据库的往返行程。这通常是企业应用程序中的最大瓶颈。
于 2008-10-21T15:13:55.527 回答
1

域逻辑与数据存储逻辑分离的应用程序适用于任何类型的数据源(数据库或其他)或 UI(Web 或 windows(或 linux 等))应用程序。

您几乎被困在数据库中,如果您所在的公司对您使用的当前数据库系统感到满意,这还不错。然而,由于数据库随着时间的推移而发展,可能会有一个新的数据库系统,它非常整洁和新,您的公司想要使用它。如果他们想切换到数据访问的 Web 服务方法(例如面向服务的架构有时会这样做)怎么办。您可能必须在各处移植存储过程。

此外,领域逻辑抽象出 UI,这在具有不断发展的 UI 的大型复杂系统中更为重要(尤其是当他们不断寻找更多客户时)。

此外,虽然我同意存储过程与域逻辑的问题没有明确的答案。我属于领域逻辑阵营(我认为他们会随着时间的推移而获胜),因为我相信精细的存储过程比精细的域逻辑更难维护。但这是另一回事

于 2009-03-03T17:21:11.960 回答
1

有时,您的应用程序和数据层并没有那么紧密耦合。例如,您可能有一个电话计费应用程序。您稍后创建一个单独的应用程序来监控电话使用情况,以 a) 更好地向您做广告 b) 优化您的电话计划。

这些应用程序有不同的关注点和数据要求(即使数据来自同一个数据库),它们会驱动不同的设计。如果您让数据库驱动代码,您的代码库最终可能会变得一团糟(在任一应用程序中)和维护的噩梦。

于 2008-12-29T07:01:38.497 回答
0

某些逻辑(例如与集合相关的操作)往往在存储过程中得到更好的表示。然而,有时,具有许多分支和条件的算法最好用编程代码来表示。用于解析支持脚本操作的运行时函数的命令的语法不能在存储过程中实现。

我看到的存储过程的一个缺点是您倾向于为应用程序中的新列表或网格获取一个新的存储过程。或者更糟糕的是,一个存储过程来统治它们,10 个参数和案例语句来进一步定义它们。此外,存储过程变得庞大,甚至更难以调试。

综上所述,我同意你的观点,因为你选址的原因,ORM 可能会多次妨碍你。最后,它归结为你如何统治技术。

于 2010-06-15T12:56:49.553 回答
0

我对支持存储过程的“将数据库锁定”的论点感到困惑。我可以将我的 ActiveRecord 模型从 MySQL 转移到 Postgres 到 SQLite,非常感谢。除非我想全部重写它们,否则我无法使用任何基于 proc 存储的东西来做到这一点。

我假设您的意思是您将数据库模式锁定在石头上。这个论点更有趣。在某种程度上,我认为这是从具有最少单元测试和代码覆盖率的应用程序的角度来争论的——在应用程序中,您不会因为担心会破坏“某些东西”而更改代码。

不过,我对基于存储过程的系统的经验很少。我很好奇,在大型应用程序中,您如何管理所有数据关系?在一个页面上,我展示了一个带有图片的产品。在另一个页面上,我展示了一个产品和创建它的用户。在另一页上,我展示了一个产品和关于它的评论。在另一页上,我需要展示没有图片的产品以及关于它的规格表......等等。我有一个包含很多关系的数据模型。我假设您不会为每个组合编写存储过程?DRY 原则是我担心的原则。我在重新左连接(有效地重新编码)我的关系的地方写了多少查询?而且,当我们谈论锁定模式时,我需要重写多少存储过程?

于 2008-12-03T06:49:10.583 回答
0

我认为现在企业解决方案中过分强调实体对象。它们不能包含业务层功能,因为它们属于服务层中的Services,或者属于UI特定功能的UI层等。实体对象确实允许设计人员在设计应用程序方面更好地思考,但它们不一定必须在其中包含所有应用程序逻辑。它们可以是遵循某些规则和接口的哑对象,可用于在它们之上构建其他层并充当层之间的数据载体。

于 2008-12-15T19:17:16.327 回答
0

老实说,我认为如果您可以摆脱表格中的数据,那就去吧!但是一旦事情变得棘手,你就会明智地学习如何组织事情以获得一些简单性。

我还没有阅读所有答案,但共同点很棘手:

  • 代码重复 Buggy,代码不稳定
  • 巨大的类加载了静态
  • 逻辑无处不
    在(aspx、静态方法、sql、触发器)
  • 与多个对象交互
    ,共享共同特征将被证明是困难的

就域与数据而言。我认为数据永远是赢家,功能对客户来说才是最重要的。它必须工作。如果您违反了按时交付工作的原则,我支持重构。您总是可以回去重构。

还有一个关于调试器、复杂域的简短说明。我似乎有很多人因为碰到接口而感到害怕,不理解在非常高级的 OOP/多态代码中可能出现的所有杂技。我完全理解,有时你会迷路和被吓倒。这就是他们制作工具的原因。我更害怕有 1000 个文件的解决方案,而不是有 1000 行的巨大方法。我已经看到两者都信不信由你。

如果您愿意编写测试,那么还有一个快乐的媒介,您不会太担心调试器和通过代码进行步进。如果您获得了好的工具并找到了平衡点,您将解决上述所有问题,并使事情变得足够简单以便解决。

于 2009-04-26T05:52:28.857 回答
0

我最近偶然发现了这个问题。意识到这个问题已经很老了,而且有很多答案,我知道我的回答很多人甚至一次都看不到。不过,我想在这里留下我的评论。

我会从三个方面来看待这个问题。但在此之前,我必须声明:10 个中有 8 个来自命令式/OO 设计世界(C/C++、JAVA、C# 等)的程序员不知道如何编写优化、高效的 SQL 代码。根据我的经验,很少有人能够同时做好应用程序开发SQL 开发。

话虽如此,我想从三个方面来看待这个问题。

第一:关注点分离不是按照程序,而是按照组织层次

坦率地说,这个世界上有很多种“企业”,每一种都有自己的组织层次,因历史和哲学而异。在我合作过的一家特定公司中,程序员无法修改或开发数据库。他们可以读取和使用数据库API(即SQL server中的存储过程),但不能直接读取数据库,也不能编写任何查询或存储过程。

任何数据库请求(数据或功能)都必须委托给另一个角色:数据架构师。她/他将负责数据库的开发,可能还有维护。(虽然,维护部分应该是DB Admin的工作。)在这种环境下,存储过程只是消耗性的;就连PROD环境下的存储过程的源代码也会被加密,不允许程序员看到SP的源代码。

但是,在其他一些组织中,程序员希望进行所有方面的开发,包括接口、中间件和数据存储。这是大多数情况。

在这两种情况下(尽管第一种情况相当极端但真实),它会影响您如何看待作者的问题。在第一种情况下,我会说作者会同意数据架构师的角色,但组织中的任何非数据库程序员都会非常鄙视。然而,在第二种情况下,由于我之前对许多开发人员不知道如何编写好的 SQL 代码(而且通常也不喜欢处理它)的免责声明,因此选择更简单的方法是很自然的:ORM。

第二:数据库的作用:存储不同解释的纯数据,还是预定义信息方案的提供者

定义:“数据”是原始的,而“信息”是解释的。

在许多现实世界的情况下,数据库仅被视为纯粹的数据存储。它可能包含数据逻辑(例如关系数据的完整性),但它不包含业务逻辑(例如,任何公式应用于数据,不是因为数据的性质,而是因为这就是该特定业务部分的工作方式)。

在上述我合作过的组织中,在一个数据库中,它存储了客户的各种财务信息。起初,只有一个公式可以计算出与客户财务状况相关的指标,并且该公式连同基于该公式的客户状态被存储在数据库的存储过程中。然而,随着政府在过去几年中不断改变规则,已经创建了更多的公式来适应政府。

因此出现了问题:组织中的每个分支机构都有自己独特的编程部门(每个分支机构之间的组织间业务很少),使用相同的财务数据集,但具有不同的公式和操作。

在这种情况下,将公式存储在数据库中的原始模型带来了维护和办公室政治的地狱。起初,数据架构师会创建类型化的存储过程来适应变化,但很快该组织就开始遇到这种模型的问题。总部已经决定,每个分支机构都将维护自己的一套公式,除了该分支机构之外,任何人都不应该知道拥有的公式。在这种情况下,数据架构师知道所有公式,这与总部的政策不符。更改公式的快节奏也为每个分支之间的测试带来了效率问题,因为每个公式调整都必须经过数据架构师。

在这种情况下,组织面临一个相当深刻的问题:数据库应该为解释的信息提供服务,还是应该只为没有任何意义的数据提供服务?

这是进入第三个方面的好方法。

第三:意识形态战争:单一VS多用途单体VS模块化

上述示例清楚地证明了数据以多用途方式使用。数据虽然对于每个分支都保持不变,但在不同的场景中有不同的解释和使用。这是我的看法。

您的数据库是否存储和提供本质上具有多用途且性能不是大问题的数据?

如果是,那么我会说,数据库应该简化为只提供数据,任何与数据完整性无关的逻辑都应该存储在其他地方。这更像是一种模块化方法:其他人可以插入他们想要的任何操作和解释,但不能插入 SQL。

如果问题的任何部分是否定的(即,它是单一目的,或者性能是一个大问题),并且假设没有办公室政治妨碍,那么我会说,将大部分内容放入数据库的单一方法很好。喜欢与否,这成为一种意识形态选择。

我的印象是作者在编写和编辑问题时支持整体方法的观点。我确实会逐案考虑,但总的来说,我是这样的:

  • 简单的 CRUD,仅此而已:ORM
  • 基于数据的公式和工作流:中间件(如 CSLA),不在数据库中(除非性能是一个问题)
  • 报告:肯定在数据库中(出于性能原因)

以上是我的 2 美分。

于 2012-05-22T18:15:37.360 回答
0

我认为您只是习惯于编写特定类型的应用程序并解决特定类型的问题。您似乎从“数据库优先”的角度对此进行了攻击。有很多开发人员将数据保存到数据库中,但性能并不是首要任务。在很多情况下,对持久层进行抽象可以极大地简化代码,并且性能成本不是问题。

无论你在做什么,它都不是 OOP。这没有错,只是不是 OOP,将您的解决方案应用于所有其他问题是没有意义的。

于 2008-08-20T21:03:20.707 回答
0

有趣的问题。几个想法:

  1. 如果您的所有业务逻辑都在您的数据库中,您将如何进行单元测试?
  2. 更改您的数据库结构,特别是影响应用程序中多个页面的更改,是否会成为整个应用程序更改的主要麻烦?
于 2008-08-20T21:26:43.337 回答
0

好问题!

我更喜欢的一种方法是创建一个迭代器/生成器对象,该对象发出与特定上下文相关的对象实例。通常这个对象包装了一些底层数据库访问的东西,但我在使用它时不需要知道这一点。

例如,

AnswerIterator 对象生成 AnswerIterator.Answer 对象。在底层,它遍历一条 SQL 语句以获取所有答案,并通过另一条 SQL 语句获取所有相关评论。但是在使用迭代器时,我只使用具有此上下文最小属性的 Answer 对象。使用一点骨架代码,这几乎是微不足道的。

我发现当我有一个庞大的数据集要处理时,这很有效,如果做得好,它会给我提供相对容易测试的小而短暂的对象。

它基本上是数据库访问内容的薄薄的一层,但它仍然给了我在需要时抽象它的灵活性。

于 2008-09-12T02:36:47.103 回答
0

我的应用程序中的对象倾向于与数据库一对一地关联,但我发现使用 Linq To Sql 而不是 sprocs 可以更轻松地编写复杂的查询,尤其是能够使用延迟执行来构建它们。例如来自Images.User.Ratings where 中的r 等。这使我无需尝试在sql 中制定几个连接语句,并且使用Skip & Take 进行分页也简化了代码,而不必嵌入row_number 和“over”代码。

于 2008-09-12T03:04:29.347 回答
0

我一直在猜测,由 SQL 驱动的关系数据库是否与这些使用 ActiveRecord 范例的框架没有交叉用途。一个基本问题是 AR(以及良好的 OO 设计,就此而言)驱使我们分解逻辑;并且 SQL 根本不适合语句分解。

我想知道对数据库使用 isam 持久性模型是否不是一个更好的主意;与 OO 的阻抗匹配更好;对数据作为表格的基本思想更加一致;更符合传统的面向对象持久性的工件。一个很好的例子是 FK 及其关联可以更加明确。

RoR 有一个作为数据库蛞蝓的代表,我怀疑这个问题是很大一部分原因。

有没有人尝试将 isam 数据库用于 ActiveRecord 实现?

于 2008-11-06T20:44:52.707 回答
0

为什么要停留在实体对象上?如果您在企业级应用程序中看不到实体对象的价值,那么只需使用纯功能/过程语言进行数据访问并将其连接到 UI。为什么不直接去掉所有的面向对象的“绒毛”?

于 2008-10-21T16:24:11.283 回答
0

我看不出实体对象与可伸缩性有什么关系,您可能在谈论使用 ORM 工具,在这种情况下我同意您的看法。

我对可扩展性非常感兴趣。实体对象永远不会妨碍您构建高度可扩展的应用程序,但您必须以正确的方式进行,换句话说,您需要一个手写的 DAL,而不是使用某些 ORM 生成的 DAL。实际上,这就是我不喜欢 ORM 的原因,没有什么比手写 DAL 更好的了,我也不使用 LINQ,因为我在很多地方读到它有很大的开销。我调整我的应用程序中的每个查询并创建所需的索引,我不让一些 ORM 为我生成代码。

我不同意实体对象使代码更难维护的观点,实际上这种架构的全部目的是使维护和修改代码更容易,这就是我在实践中看到的,我写了意大利面条代码很长一段时间(没有使用 3 层或 n 层架构)所以我知道我在说什么。

缓存也需要实体对象,我想知道如果您不使用实体对象,您如何在应用程序中缓存数据,您使用数据集还是数据表?

于 2009-04-09T01:05:48.213 回答
0

好吧,我要感谢你的精彩讨论。我正在研究 Stephen Walther 的 ASP.NET MVC Framework Unleashed,我喜欢它作为一种哲学练习,但我对他的方法所需要的大量管道代码感到有些震惊。现在这不是使用 ORM 所固有的——Rails 以将你从这些家务事中解放出来而自豪,但我真的在纠结是否值得编写和维护一个可供应用程序和将 Record 类映射到数据库的 EntityRecord 类。

他对好处的解释是,您最终会得到一个可测试的应用程序,其中测试运行得很快,但坦率地说,我宁愿牺牲一些测试速度来执行实际在应用程序中的代码。我认为,当您花一天的时间辛苦工作并复制属性以使您的测试能够快速运行时,测试尾巴已经开始摇晃程序员的狗——他们宁愿追兔子或在前面打盹的火。

第二个引用的好处是您可以使用您的应用程序并在不同的数据库上运行它。是的,好吧,也许如果你正在编写类似销售人员的东西以进行转售或其他东西,这可能是一个目标,但对于 90% 或更多的应用程序来说,那又如何?我想起了“生活真美好”中的邻居,他给了乔治一罐钱,说:“我把这个存起来,以备离婚之需,以防我有丈夫。” 在你需要它之​​前不要写它。

另一方面,我确实反对存储过程。这不一定是它们的使用所固有的,而更像是我工作过的一些脑死亡商店的一个特征:他们有时会在我想编写的代码中放置一个 DBA。我喜欢认为我不是牛仔,但另一方面,我不喜欢召集联合国委员会在表格中添加字段。

于 2009-09-05T19:03:05.977 回答
0

一个问题:如果您的数据源是 Web 服务怎么办?我通过 Web 服务仅使用分布式数据编写应用程序。我是否希望使用与我的数据源是 RDBMS 不同的范例来编写它?

我不是问如果你从 RDBMS 切换到 Web 服务你会怎么做(因为在内部商店中,这不太可能),我问的是当数据从一开始就来自 Web 服务时你会怎么做?

您的编程模型是否与 RDBMS 完全不同?如果是,则需要考虑可维护性。如果他们跳入的每个应用程序都使用不同的范例进行编程,我的开发人员将度过一段糟糕的时光。

于 2009-09-24T23:28:48.763 回答
0

以适用于许多实际情况的非常通用的方式解决大问题的软件必然会带来性能成本。处理所有通用性需要代码,而代码运行需要时间。

此外,通过抽象层下降总是会发现有些东西花费很少,有些东西花费很多,而这些差异被抽象隐藏了。抽象为开发人员提供的与底层事物的隔离总是导致开发人员随意引入比必要的更昂贵的操作。

关于这个问题还有什么可以说的,从仅从性能的角度和仅从扩展性能的角度来看,避免因与自己的数据库的实际情况额外隔离而造成的双重打击将在性能方面得到回报。

我目前的工作日都在与这些问题引起的性能问题作斗争,它们是可怕的怪物。

于 2014-03-03T22:46:16.860 回答
0

每种方法都有优势。必须根据具体情况判断它们对特定问题的适用性。

正如其他人所指出的,我全心全意地相信实体(以及因此面向对象)设计简化了原本复杂的业务逻辑。但在我看来,基于实体的设计的最大优势是通过明确定义的输入和输出实现模块化,这更容易在数据库之外面向对象的模型中实现。我会在下面详细说明。


我是 Linux 用户。Unix 哲学中的一点是,开发人员应该“编写程序来处理文本流,因为这是一个通用接口”。Linux 也是如此,因为它非常以文本为中心。您可以将不相关的流程链接在一起以实现新的东西,例如grep ^col /var/log/bim-sync | sed 's/.*alt:\([0-9]\{1,\}\).*/\1/g | xargs -I replstr bim-transcoder replstr. 这些程序彼此完全不知道,但可以很容易地结合在一起以实现新的目的。这是可能的原因是因为您(“胶水”的作者)知道每个进程的输入和输出格式。

现在,我不相信文本流在任何地方都适用。文本流很常见,但不是通用的。我的开发理念是“编写具有明确输入和输出的程序”。我在这里谈论的输入/输出不一定是标准输入/输出,也不一定是文本 - 它可能是命令行程序的参数,通过网络套接字发送的原始字节,在内存层之间传递的内存数据结构用旧的输入-处理-输出“黑匣子”来思考软件,让您可以像 Unix 中的命令行实用程序一样编写应用程序——独立地用一层薄薄的胶水将它们连接在一起。

例如,假设您正在为澳大利亚新南威尔士州的出生、死亡和婚姻编写软件。当孩子的出生登记进入时,操作员输入详细信息,扫描已签名的表格并点击提交按钮。点击提交按钮发出一个RegisterBirth命令。该软件验证命令的详细信息(出生日期和时间、医院等),并发出BirthRegistered事件。这个事件包括很多分娩的细节,比如接生的医生,是自然分娩还是剖腹产,是不是剖腹产,是不是紧急情况,亲生父母是谁等. 很多不同的代码可以“插入”这个事件。例如,一段代码可以发出一个简单的insert into person...SQL 语句,而另一段代码可以发出一系列Neo4j 密码命令来存储新生婴儿及其与亲生父母的关系。第二段代码将允许对分层“家谱”数据进行极快的查询。无论您使用的是邻接列表还是嵌套集,这样的查询在 SQL 数据库中都会更慢(也更复杂) 。还有一段代码可以更新当月紧急剖腹产数量的统计数据,由于历史原因,该数据存储在一个 XML 文件中。

模块化并不止于抽象持久性机制。例如,您可以编写一个FastCGI “粘合层”来启用您的应用程序:一个“输入”HTTP 请求被您的 Web 服务器接受,它向您的“粘合层”发出一个“输出”FastCGI 请求。您的 FastCGI“粘合层”接受此作为输入并将其转换为适合您的应用程序的输出形式。您的应用程序接受输入命令并发出事件或错误,这些事件或错误可以被其他“粘合层”(例如上面给出的 SQL 和 Neo4j 示例)拾取。

模块化几乎可以在任何方向上继续。你可以有一个命令行界面,或者一个 GUI 界面。您可以创建一个全面的自动化测试套件。您可以打开您的应用程序以供第三方编写脚本。这里的很多概念都与域驱动设计命令查询职责分离事件溯源有关,这三个相互关联的模式我发现它们非常强大。

当使用基于实体的方法时,有许多相关的架构。例如,有Jeffrey Palermo的Onion Architecture和 Alistair Cockburn 的Ports and Adapters(或 Hexagonal Architecute)。所有这些架构的共同点是通过定义的输入和输出实现模块化和抽象,无论这些输入/输出边界是否在单个程序内,或者它们是否跨越多个进程甚至网络。

基于实体的方法提供了模块化和灵活性。但是,这种方法也有缺点,其中三个很重要:

  1. 一是前期投资高。这意味着这种方法对于规模较小的项目没有意义。

  2. 其次,您必须编写的胶水代码量可能会很大。编写胶水代码可能很乏味,但也可能是有益的。例如,假设您的应用程序作为存储后端与 PostgreSQL 松散集成。当公司董事决定应用程序应该支持 Microsoft SQL Server 时,如果在截止日期之前达到目标且低于预算,这是非常令人满意的(并建立团队士气)。

  3. 第三,我的经验告诉我,基于实体的方法可能比简单的 SQL 解决方案更糟糕,这取决于实施它的人员的专业知识。例如,如果实体优先方法充满了 getter 和 setter,它们只不过是数据库表的内存表示,您可以确定问题没有被考虑过。可以理解的是,这些案例让开发人员想知道“我们为什么不直接编写 SQL?”


参考:

于 2016-06-18T13:30:51.557 回答