为什么我们需要实体对象?

我真的需要看到一些诚实,深思熟虑的辩论,目前公认的 < em > 企业应用程序 设计范式的优点。

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

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

我目前的设计理念是:

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

(注意: 我也用 JavaEE 构建了企业级应用程序,请用 Java 人员替换我的.NET 示例)

我不反 OO。我为不同的目的编写了许多类,只是没有实体。我承认,我编写的大部分类都是静态助手类。

我不是在做玩具。我指的是跨多台机器部署的大型、高容量事务性应用程序。Web 应用程序、 Windows 服务、 Web 服务、 b2b 交互等等。

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

我得出了一个经过实战检验的结论: 实体对象正在妨碍我们,如果没有它们,我们的生活将会轻松得多。

考虑这个简单的例子: 您得到一个关于应用程序中某个页面不正常工作的支持调用,可能其中一个字段没有像应该的那样被持久化。对于我的模型,分配给发现问题的开发人员打开 正好三份文件。一个 ASPX、一个 ASPX.CS 和一个带有存储过程的 SQL 文件。这个问题可能是存储过程调用缺少的参数,需要几分钟才能解决。但是对于任何实体模型,您都必然会启动调试器,开始逐步执行代码,最终可能会在 VisualStudio 中打开15-20个文件。当您降到堆栈底部时,您忘记了从哪里开始。我们一次只能记住这么多东西。软件是难以置信的复杂,没有添加任何不必要的层。

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

现在我们来讨论一下可伸缩性。

开发人员是否意识到,每次编写或修改与数据库交互的任何代码时,他们都需要对对数据库的确切影响进行彻底的分析?而且不仅仅是开发副本,我指的是生产的模拟,所以您可以看到,您现在需要为对象添加的额外列刚刚使当前查询计划失效,而一个在1秒内运行的报告现在将花费2分钟,仅仅因为您向选择列表添加了一个列?结果是,您现在需要的索引非常大,以至于 DBA 必须修改您的文件的物理布局?

如果您让人们使用抽象离物理数据存储太远,他们将对需要伸缩的应用程序造成严重破坏。

我不是狂热分子。如果我错了,我可以被说服,也许我是错的,因为有这样一个强大的推动 Linq 到 Sql,ADO.NET EF,Hibernate,Java EE 等。请仔细想想你的回答,如果我错过了什么,我真的想知道它是什么,为什么我应该改变我的想法。

[编辑]

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

我可能应该更清楚地表明,我说的是企业应用程序。我真的不能评论,比如说,在某人的桌面上运行的游戏,或者移动应用程序。

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

问问在这个行业工作了很长时间并且从事过长期应用程序工作的人: 在数据库存在的时候,有多少应用程序和 UI 层来了又去?当有4或5个不同的持久层生成 SQL 来获取数据时,调优和重构数据库有多难?你什么都改变不了!ORM 或任何生成 SQL锁定你的数据库的代码。

15596 次浏览

一个原因是——将域模型与数据库模型分离。

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

我觉得你的问题很有趣。
通常我需要实体对象来封装应用程序的业务逻辑。将这种逻辑推送到数据层将是非常复杂和不充分的。
如何避免这些实体对象? 您有什么解决方案?

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

我还想在 Dan 的回答中添加这样的内容,即分离两种模型可以使您的应用程序能够在不同的数据库服务器甚至数据库模型上运行。

@ jdecuyper,我经常对自己说的一句格言是: “如果你的业务逻辑不在你的数据库中,它只是一个建议”。我记得 Paul Nielson 在他的一本书里说过。应用程序层和 UI 来来去去,但数据通常存在很长时间。

如何避免实体对象?主要是存储程序。我还坦率地承认,无论您是否愿意,业务逻辑都倾向于遍历应用程序中的所有层。一定数量的耦合是固有的和不可避免的。

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

因此,我认为没有万能的答案。开发人员确实需要意识到,有时候,试图过于面向对象会导致更多的问题,而不是解决问题。

我认为您只是习惯于编写特定类型的应用程序,并解决特定类型的问题。您似乎是从“数据库优先”的角度来解决这个问题的。有很多开发人员将数据保存到 DB 中,但是性能并不是最重要的。在很多情况下,在持久层上放置一个抽象会极大地简化代码,而且性能成本是不成问题的。

不管你在做什么,都不是 OOP。这没有错,只是不符合面向对象程序设计(OOP) ,并且将您的解决方案应用于所有其他问题是没有意义的。

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

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

Cs 文件是否应该与数据库交互、调用 sproc 并理解 IDataReader?

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

将我的域从数据库中分离出来可以保护我不受数据库结构变化的影响,这肯定是件好事吧?当然,数据库的功效是绝对重要的,所以让那些最擅长这些东西的人在一个地方处理这些东西,尽可能地减少对系统其他部分的影响。

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

此外,您的方法似乎主张应用程序的业务逻辑驻留在数据库中?我觉得这是错误的,SQL 真的很擅长查询数据,而不是表达业务逻辑。

尽管这个想法很有趣,但是它感觉离 aspx 中的 SQL 只有一步之遥,这让我感到恐惧。

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

但是如果没有任何实体对象,它们就没有太多可以讨论的内容。

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

这样可以节省维护时间。

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

对于我来说,这可以归结为我不希望我的应用程序关心数据是如何存储的。我可能会因为这样说而被打耳光... ... 但是你的应用程序不是你的数据,数据是应用程序的工件。我希望我的应用程序考虑的是客户、订单和项目,而不是像 DataSet、 DataTable 和 DataRows 这样的技术... ... 因为谁知道这些东西会存在多久。

我同意总是存在一定数量的耦合,但我更喜欢这种耦合向上而不是向下。我可以轻易地改变一棵树的枝叶,而不是改变它的主干。

我倾向于为报告保留 sprocs,因为与应用程序的一般数据访问相比,查询往往会变得更糟糕一些。

我还倾向于认为,在这种情况下,如果在早期进行适当的单元测试,那么一个列没有被持久化可能不会是一个问题。

我最近一直在思考同样的事情; 我曾经是 CSLA 的重度使用者,我喜欢说“你所有的业务逻辑(或者至少是合理可能的)都封装在业务实体中”的纯粹性。

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

例如,“客户”的概念可能由 Customer 表中的主记录组成,结合了客户已下的所有订单,以及客户的所有雇员和他们的联系信息,客户及其子女的一些属性可能由查找表确定。从开发的角度来看,能够将 Customer 作为一个单独的实体进行处理是非常好的,因为从业务的角度来看,Customer 的概念包含所有这些内容,并且这些关系可能在数据库中被强制执行,也可能不被强制执行。

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

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

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

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

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

正交性: 一个层的变化可能不会影响其他层(当然,如果你对数据库做了大的变化,它会波及所有层,但大多数小的变化不会)。

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

关注点分离: 在一个大型产品中,你可以把数据库分配给一个数据库管理员,他可以对数据库进行优化。将模型分配给具有设计模型所必需的知识的业务专家。分配个人表单给更有经验的开发者。.

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

干杯。

有趣的问题,几个想法:

  1. 如果所有的业务逻辑都在数据库中,您将如何进行单元测试?
  2. 更改数据库结构,特别是影响应用程序中几个页面的数据库结构,不会成为整个应用程序更改的主要麻烦吗?

艾瑞克, 你说对了。对于任何真正可伸缩/容易维护/健壮的应用程序,唯一真正的答案是摒弃所有垃圾,坚持基本原则。

我的职业生涯也走过类似的轨迹,得出了同样的结论。当然,我们被认为是异教徒,看起来很滑稽。但我的东西很管用。

应该用怀疑的眼光看待每一行代码。

实体对象可以促进应用程序层的缓存。

问得好!

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

比如说,

AnswerIterator 对象将生成 AnswerIterator。回答问题。在底层,它迭代一个 SQL 语句以获取所有的答案,另一个 SQL 语句以获取所有相关的注释。但是,当使用迭代器时,我只使用了对此上下文具有最小属性的 Response 对象。使用一些框架代码,这几乎变得微不足道。

我发现,当我需要处理一个庞大的数据集时,这种方法可以很好地工作,如果处理得当,它会给我提供相对容易测试的小型、暂时的对象。

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

艾瑞克,

没有人阻止你选择你想要的框架/方法。如果你要走“数据驱动/存储过程驱动”的路径,那么无论如何,去吧!特别是如果它真的能够帮助您按规格和按时交付应用程序的话。

警告是(您的问题的另一面) ,您的所有业务规则都应该在存储过程中,您的应用程序只不过是一个瘦客户机。

也就是说,如果在 OOP 中执行应用程序,同样的规则也适用: 保持一致。遵循 OOP 的原则,那个包括创建实体对象来表示您的域模型。

这里唯一真正的规则是字 一致性。没人能阻止你以 DB 为中心。没有人阻止你做老式的结构化(也就是功能性/程序性)程序。见鬼,没有人阻止任何人做 COBOL 风格的代码。但是一个应用程序必须非常、非常一致地沿着这条道路前进,如果它希望获得任何程度的成功。

我的应用程序中的对象倾向于一对一地与数据库相关,但是我发现使用 Linq To Sql 而不是 sprocs 更容易编写复杂的查询,特别是能够使用延迟执行构建它们。例如图像中的 r。用户。评级等。这样可以节省我在 sql 中编写多个 join 语句的时间,并且使用 Skip & Take 进行分页也可以简化代码,而不必嵌入 row _ number & ‘ over’代码。

除了抽象和松散耦合之外,还有其他很好的理由支持实体对象。我最喜欢的事情之一是强类型,这是 DataReader 或 DataTable 无法实现的。另一个原因是,如果处理得当,适当的实体类可以使代码更易于维护,方法是对特定于领域的术语使用第一类构造,任何查看代码的人都可能理解这些术语,而不是使用一堆字符串,其中包含字段名,用于为 DataRow 建立索引。存储过程与 ORM 的使用实际上是正交的,因为许多映射框架使您能够映射到 sprocs。

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

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

这种 Entity Framework 还有一个额外的优点,那就是可以将数据库表视为彼此继承的实体类,或者将多个表中的列视为聚合到一个实体中的列。如果需要更改模式,可以将映射从概念模型更改为存储模式,而不需要更改实际的应用程序代码。同样,可以在这里使用存储过程。

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

我认为在这个问题上你可能是“贪多嚼不烂”。Ted Neward 称之为“ 计算机科学的越南”并非轻率。

有一件事我可以绝对向你保证,那就是它不会改变任何人对这件事的看法,就像无数其他博客、论坛、播客等经常被证明的那样。

对一个有争议的话题进行公开的讨论和辩论当然是可以的,只是这个问题已经讨论过很多次了,以至于双方都同意不同意,只是继续编写软件。

如果你想进一步阅读双方的文章,可以看看 Ted’s blog 上的文章,Ayende Rahein,Jimmy Nilson,Scott Bellware,Alt。Net,Stephen Forte,Eric Evans 等等。

您可能会对 comp.object 上的 这个文章感兴趣。

我并不是说我同意或者不同意,但是这很有趣,而且(我认为)和这个话题有关。

现在时间不多,但我突然想到..。

实体模型允许您为数据库(和其他可能的系统)提供一致的接口,甚至超出了存储过程接口的能力范围。通过使用企业范围的业务模型,您可以确保所有应用程序都能够始终如一地影响数据,这一点非常重要。否则,您最终会得到错误的数据,这是非常糟糕的。

如果您只有一个应用程序,那么无论应用程序或数据有多大,您都不会真正拥有一个“企业”系统。在这种情况下,您可以使用类似于您所谈论的方法。只是要注意,如果您决定在将来扩展您的系统,那么将需要进行哪些工作。

下面是一些你应该牢记在心的事情(IMO) :

  1. 生成的 SQL 代码很糟糕 对不起,我 我知道很多人认为 这节省了很多时间,但是我 从来没有找到一个系统可以 生成的代码比 我可以写什么,经常 代码太可怕了,你 也经常产生一吨 永远不会被使用的 SQL 代码。 这里的例外非常简单 模式,比如查找表。 很多人都会失去理智 不过。
  2. 实体 < > 表(甚至逻辑数据模型实体)。数据模型通常具有应该尽可能紧密地与数据库实施的数据规则,这些规则可以包括表行之间如何相互关联的规则,或者其他对于声明性 RI 来说过于复杂的类似规则。这些应在存储过程中处理。如果所有的存储过程都是简单的 CRUD 过程,那么就不能这样做。最重要的是,CRUD 模型通常会产生性能问题,因为它没有最小化网络到数据库的往返。这通常是企业应用程序中最大的瓶颈。

为什么停留在实体对象上?如果在企业级应用程序中看不到实体对象的价值,那么只需在一个纯功能/工作语言中进行数据访问,并将其连接到一个 UI。为什么不直接删掉那些“毛茸茸”的东西呢?

我们还应该讨论实体到底是什么的概念。 当我通读这个讨论时,我得到的印象是,这里的大多数人都在关注 贫血领域模型意义上的实体。 很多人都认为贫血领域模型是一种反模式!

丰富的领域模型是有价值的,这就是 领域驱动设计的全部。 我个人认为 OO 是一种克服复杂性的方法。这不仅意味着技术上的复杂性(如数据访问、 ui 绑定、安全性... ...) 还有业务领域的复杂性

如果我们能够应用面向对象技术来分析、建模、设计 执行我们的业务问题,这对于非平凡应用程序的可维护性和可扩展性是一个巨大的优势!

实体和表之间存在差异。实体应该代表您的模型,表格只是代表您的模型的数据方面!

的确,数据比应用程序的寿命更长,但是想想 David Laribee这句话: 模型是永恒的... ... 数据是一个快乐的副作用。

更多相关链接:

我一直在推测 SQL 驱动的关系数据库是否与这些使用 ActiveRecord 范例的框架有一点交叉。一个基本问题是 AR (以及良好的面向对象设计)驱使我们分解逻辑; 而 SQL 根本不适合语句分解。

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

RoR 是一个数据库鼻涕虫,我怀疑这个问题是很大一部分原因。

是否有人尝试过将 isam 数据库用于 ActiveRecord 实现?

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

但是,如果你有一个数据库,其中有100个表,相当于每个表的4个存储过程,只是为了基本的 CRUD 操作,需要维护400个存储过程,这些存储过程不是强类型的,因此容易出现输入错误,也不能进行单元测试。当您得到一个新的 CTO,他是一个开放源代码的传道者,并且希望将 RDBMS 从 SQLServer 更改为 MySql 时会发生什么?

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

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

只有我的两分钱

真是个有趣的问题。老实说,我无法证明为什么实体是好的。但我可以分享我喜欢它们的原因。代码就像

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

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

此方法的实现将基于 Order 抽象,而不是基于它在 DB 中的具体表示方式。我实现的大多数此类操作实际上并不取决于如何存储这些数据。我确实理解,出于性能和可伸缩性的目的,有些操作需要与 DB 结构耦合,只是以我的经验来看,这样的操作并不多。以我的经验来看,知道 Person 已经。GetFirstName ()返回 String,并且。GetAddress ()返回 Address,并且 Address 具有。GetZipCode ()等等-并且不关心存储数据涉及哪些表。

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

可伸缩性是这里很有趣的一点——大多数需要大量可伸缩性的网站(如 facebook,livejournal,flickr)倾向于使用数据库禁欲的方法,当数据库被尽可能少地使用,可伸缩性问题通过缓存解决,特别是通过使用 RAM。http://highscalability.com/上有一些有趣的文章。

我对存储进程的“将数据库锁定在石头中”的参数 赞成感到困惑。我可以把我的 ActiveRecord 模型从 MySQL 转移到 Postgres,非常感谢。我不能使用任何基于 proc 的存储,除非我想重写它们。

我假设您的意思是将数据库 模式锁定为一成不变的。这个论点更有趣。在某种程度上,我认为这是从应用程序最小单元测试和代码覆盖的角度来讨论的——在这些应用程序中,你不会因为害怕破坏某些东西而更改代码

不过,我对基于存储处理器的系统的经验很少。我很好奇,在大型应用程序中,如何管理所有的数据关系?在一个页面上,我展示了一个带有图片的产品。在另一个页面上,我展示了一个产品和创建它的用户。在另一个页面上,我展示了一个产品和关于它的评论。在另一个页面上,我需要显示该产品没有图片加入一个规格表关于它..。等等。我有一个有很多关系的数据模型。我想你不会为每个组合都写一个存储过程吧?我担心的是干燥原理。在重新加入(有效地重新编码)我的关系时,我编写了多少查询?当我们讨论锁定模式时,需要重写多少存储进程?

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

任何软件都有一个现实的模型,这个模型在某种程度上是现实的抽象。没有任何计算机程序能够捕捉现实的所有复杂性,而编写程序只是为了从现实中解决一系列问题。因此,任何软件模型都是对现实的简化。有时候,软件模型迫使现实自我减少。就像你想让汽车租赁公司为你预定任何车,只要是蓝色的,有合金的,但操作员不能满足,因为你的要求不适合在电脑上。

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

考虑到好的会计制度的重要性,以及你从任何业务经理那里得到的认可,这些制度已经变得非常先进。数据库的基础现在非常牢固,没有人会犹豫将重要数据保存在如此值得信赖的地方。

我猜,OO 一定是在人们发现现实的其他方面比会计(已经是一种模型)更难以建模的时候出现的。面向对象已经成为一个非常成功的想法,但是面向对象数据的持久性还相对欠发达。关系数据库/会计已经取得了轻松的胜利,但面向对象是一个更大的领域(基本上所有不是会计的东西)。

我们中的很多人都想使用 OO,但是我们仍然想要安全地存储我们的数据。还有什么比像备受尊敬的会计系统那样存储我们的数据更安全呢?这是一个诱人的前景,但我们都遇到了同样的陷阱。与拥有会计传统和地位的 RDB 行业所付出的巨大努力相比,很少有人愿意去考虑面向对象的持久性。

普雷维勒和 db4o 是一些建议,我相信还有其他我没听说过的,但似乎没有一个得到一半的媒体,比如说,冬眠。

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

在我每天努力消除 OO 和 RDB 之间的鸿沟时,我尽可能多地使用 OO,但尽量将继承保持在最低限度。我不经常用 SPs。我将只在看起来像会计的方面使用高级查询内容。

等这个鸿沟永远消失了,我会很高兴的。我认为,当 Oracle 启动类似“ Oracle 对象实例库”的东西时,解决方案就会出现。要想真正流行起来,它必须有一个令人安心的名字。

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

一个问题: 如果所有业务逻辑都被困在数据库中,如何处理断开连接的应用程序?

在我感兴趣的企业应用程序类型中,我们必须处理多个站点,其中一些站点必须能够在断开连接状态下运行。
如果您的业务逻辑封装在一个域层中,这个域层很容易合并到各种应用程序类型中——比如说,作为一个 dll——那么我可以构建能够识别业务规则并且在必要时能够在本地应用它们的应用程序。

为了在数据库的存储过程中保留 Domain 层,您必须坚持使用单一类型的应用程序,这种应用程序需要一个到数据库的永久视线。

对于某些类型的环境来说,它是可以的,但是它肯定不能覆盖 企业应用程序的全部范围。

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

这些应用程序有不同的关注点和数据需求(甚至数据来自同一个数据库) ,它们将驱动不同的设计。如果让数据库驱动代码,那么您的代码库最终可能会变得一团糟(在任何一个应用程序中) ,并且成为维护的噩梦。

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

你几乎卡在你的数据库,这是不坏的,如果你与一家公司谁是满意的当前数据库系统你使用。但是,由于数据库的不断发展,可能会出现一种新的数据库系统,它非常整洁,而且是您的公司想要使用的新系统。如果他们想切换到数据访问的 Web 服务方法(有时候像面向服务的体系结构那样)会怎样。您可能必须将存储过程移植到所有位置。

此外,领域逻辑将用户界面抽象出来,这在具有不断发展的用户界面的大型复杂系统中可能更为重要(特别是当它们不断搜索更多客户时)。

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

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

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

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

另外,实体对象是缓存所必需的,我想知道如果不使用实体对象,如何在应用程序中缓存数据,是使用数据集还是数据表?

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

我还没有读完所有的答案,但是共同点让人感到棘手:

  • 代码重复错误,代码不稳定
  • 用静态加载的巨大类
    课程
  • 逻辑无处不在
    任何地方(aspx,静态方法,sql, 触发器)
  • 与多个
    对象,共享的共同特征将 很难证明

就域与数据而言。我认为数据总是赢家,功能对客户来说是最重要的。一定要成功。我是重构的支持者,如果你能够打破原则,按时交付一些东西。.您总是可以返回并重构。

也是一个关于调试器的快速词汇,复杂域。我看到很多人害怕,因为他们碰到了接口,不理解在非常高级的 OOP/多态代码中可能存在的所有技巧。我完全理解,有时候你会迷失自我。这就是为什么他们制造工具。.与其说我害怕一个有1000个文件的解决方案,不如说我害怕一个有1000行的庞大方法。信不信由你。

如果你愿意编写测试,你就不用担心调试器和代码。如果你有好的工具,找到一个平衡点,你就可以解决上面所有的问题,并且让事情变得足够简单。

谢谢你们的精彩讨论。我正在阅读斯蒂芬•沃尔特(stephenwalther)的 ASP.NET MVC Framework 《释放》(unleached) ,我很喜欢这本书,把它当作一种哲学练习,但我有点惊讶于他的方法所需要的管道代码的数量。现在,使用 ORM 并不是固有的—— Rails 以将您从这些日常事务中解放出来而自豪,但是我真的很纠结,是否值得编写和维护一个单独的 Record 类,应用程序可以使用这个类,以及一个将 Record 类映射到数据库的 EntityRecord 类。

他所说的好处是,您最终得到了一个可测试的应用程序,其中测试运行速度很快,但坦率地说,我宁愿用一些测试速度来交换执行应用程序中实际存在的代码。我认为,当您花费一整天的时间苦干和复制属性以便您的测试能够快速运行时,测试尾巴已经开始摇晃程序员的狗——他们宁愿追逐兔子或者在火堆前小睡一会儿。

引用的第二个好处是,您可以使用您的应用程序并在不同的数据库上运行它。是的,好吧,也许如果你正在编写一个像 SalesForce 这样的东西来转售或者其他什么的,那可能是一个目标,但是对于90% 或者更多的应用程序来说,那又怎么样呢?这让我想起了《生活多美好》中的邻居,他给了乔治一罐钱,说: “我把这些钱存起来准备离婚,以防万一我找到了丈夫。”需要的时候再写。

另一方面,我确实对存储过程有实际的反对意见。它们的使用并不一定是与生俱来的,但更多的是我工作过的一些脑残商店的特性: 他们有时候会把 DBA 放在我想要编写的代码中。我喜欢认为自己不是一个牛仔,但另一方面,我也不喜欢召集一个联合国委员会来为桌子增加一块场地。

一个问题: 如果您的数据源是一个 Web 服务会怎样?我只通过 Web 服务使用分布式数据编写应用程序。如果我的数据源是一个 RDBMS,我是否应该使用不同的范例来编写它?

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

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

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

我在存储过程中看到的一个弱点是,您往往为应用程序中的新列表或网格获取新的存储过程。或者更糟,一个存储的 proc 来规则它们所有,10个参数和 case 语句来进一步定义它们。此外,存储过程变得巨大,甚至更难调试。

所有这一切说,我同意你的意见,一个 ORM 可能会得到的方式多次为原因,你的位置。归根结底,这取决于你如何统治技术。

我最近偶然发现了这个问题。意识到这个问题已经很老了,而且有很多答案,我明白我的回答很多甚至一次都没有被看过。尽管如此,我还是想在这里留下我的评论。

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

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

第一: 关注的分离。

坦率地说,这个世界上有很多种“企业”,每一种都有自己的组织结构,由于历史和哲学的不同而不同。在我工作过的一家公司,程序员 不能在数据库上修改或开发。它们可以读取和使用数据库 API (即 SQL 服务器中的存储过程) ,但不能直接读取数据库,也不能编写任何查询或存储过程。

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

然而,在其他一些组织中,程序员被期望做所有方面的开发,包括接口、中间件和数据存储。这是多数人的案子。

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

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

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

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

在前面提到的组织中,我曾经在一个数据库中工作过,它存储客户的各种财务信息。首先,只有一个公式来计算关于客户财务健康的指数,这个公式连同基于该公式的客户状态一起存储在数据库的存储过程中。然而,随着政府在过去几年里不断改变规则,为了适应政府的需要,已经创造了更多的方案。

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

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

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

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

第三: 意识形态战争: 单用途与多用途,还是 整体式与模块式

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

您的数据库是否存储和服务具有多种用途的数据,性能是否不是一个大问题?

如果是,那么我会说,数据库应该减少到只服务于数据,任何与数据完整性无关的逻辑应该存储在其他地方。这更像是一种模块化的方法: 其他人可以插入任何他们希望拥有的操作和解释,但不能插入 SQL。

如果问题的任何部分都是负面的(例如,它的单一目的,或性能是一个大问题) ,并假设没有办公室政治的方式,那么我会说,单一的方法把大多数的东西放入数据库是好的。不管你喜欢与否,这都是一种意识形态上的选择。

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

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

上面是我的2美分。

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

而且,从抽象层向下总是会发现有些东西成本较低,有些东西成本较高,这些差异被抽象所隐藏。抽象为开发人员提供了与底层隔离的机会,这种隔离总是导致开发人员随意地引入比必需的更昂贵的操作。

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

我现在每天的工作就是与这些问题引起的性能问题作斗争,这些问题是很难对付的。

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

正如其他人指出的那样,我完全相信实体(因此也是面向对象的)设计简化了原本复杂的业务逻辑。但是在我看来,一个基于实体的设计的最大优势是通过 明确的输入和输出模块化,它更容易实现数据库的 在外面面向对象模型。我将在下面详细说明。


我是 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 文件中。

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

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

在使用基于实体的方法时,有许多相关的体系结构。例如,有 Jeffrey Palermo 的 洋葱建筑和 Alistair Cockburn 的 端口和适配器(或六边形建筑)。所有这些体系结构的共同点是通过定义的输入和输出进行模块化和抽象,而不管这些输入/输出边界是否位于单个程序中,或者它们是否跨越多个流程甚至网络。

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

  1. 首先,初始投资高。这意味着这种方法对于范围较小的项目没有意义。

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

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


参考文献: