2010-04-20

我们丢失了Model层

Views: 24367 | 18 Comments

我在百度参与的一个最重要的项目, 项目设计的第一步就是数据库设计. 是的, 因为是重构, 所以没有人觉得这样做的问题所在. 在找出业务对象之前, 在分析出实体-关系之前, 在整理出功能列表之前, ... 在所有这些之前, 我们做了两件事: 一件是非常抽象的业务无关的系统(程序)的层次划分, 另一件是过于细枝末节的数据库表设计.

再加上我们使用了一种所谓的ActiveRecord的东西, 于是, 虽然大部分功能都已实现, 系统正常上线运行, 又过了一段时间, 我们突然意识到, 我们自认为掌握了MVC, 我们自认为理解了Web系统开发, 但是, 我们丢失了Model层!

我们所谓的Model, 其实就是数据库对和PHP对象的映射. 所有业务逻辑, 事务, 都放在Controller里.

造成这个结果, 我认为有几个重要的因素:

1. 缺少业务分析

在我们开始进行这个项目, 其实我们是在做一个从来没有人做过的系统. 并不是这个系统多么的先进, 而是我们面对的业务是独一无二的, 即使我们愿意花一百万, 也没有一个现成的软件可以满足我们的需求.

我们有的, 是一个2002年时留下的旧系统. 这个系统最初的目的, 其实就是几个数据库表加上一点PHP代码, 方面日常操作.

这就表示, 我们在做一个没有明确需求的系统, 而是要做一个满足未来随时提出新需求的系统, 这样的难度可想而知.

2. 缺少总体设计

正因为需求不明确, 所以, 根本无法进行总体设计.

3. 过早进行数据库设计

这是一个重要的硬伤, 在我们没有抽象出任何业务对象之前, 我们就根据旧系统的理解, 画出了一个包含几十个表的图形. 而且字段都没有确定, 都是后期不断增加的.

4. 开发框架误导了MVC概念

我们使用了一个较流行的PHP开发框架, 该框架借鉴了ROR的ActiveRecord(AR)概念, 并且有自己的MVC理解. 它把AR当做Model, 并推荐放在一个名为models的目录下面. 于是, 我们在自动生成表对应的AR之后, 便望文生义想当然地认为已经拥有了Model层. 其实, AR只不过是DAO(数据访问层), 并不是Model层.

最后, 我们的业务几乎全放在了Controller里: DB事务, DB查询, 将AR转成POPO(Plain Old PHP Object)...

我同意这种看法: keeping controllers as skinny as possible.

5. 开发框架是代码框架, 不是业务框架!

很多人没有正确地理解代码框架, 以为选了一个开发框架就完事了. 于是, 在 ZendFramewok, Yii, Symfony, CodeIgniter... 等等框架上扫来扫去. 这个很好, 那一个也很不错, 特别是每一个都能快速的开发出一个Blog系统. 对! 全是Blog系统的Demo! 但我们要开发的系统是个人博客系统吗? 是只有一两个表的增删改查的程序吗? 我们要开发的系统, 是在特定的时机, 在特定的页面, 当用户进行特定的操作后, 用特定的方式, 修改特定的字段.

这些框架都只是代码级别, 离业务还有十万八千里呢. 只要把业务抽象出来了, 用最流行的那几个框架中的任意一个都不会错.

不重视业务分析和理解, 却对同质化的代码框架纠缠不清, 简直和初学者没什么两样.

Related posts:

  1. C#封装log4net
  2. 高性能并发Web服务器实现核心内幕
  3. 关系数据库应用设计基础
  4. 基于列的数据库
  5. 开发跨浏览器JavaScript—《Ajax基础教程》笔记
Posted by ideawu at 2010-04-20 10:11:43 Tags:

18 Responses to "我们丢失了Model层"

  • 框架只是按原作者的想法划定了一些隔离层。我们完全可以自己在这些层上增加更多的层来提高代码的复用性。博主的应用,controller根本不必直接面向AR或DAO,完全可以增加一套业务层来封装掉这些底层操作。 Reply
  • [...] 我们丢失了 Model 层 [...] Reply
  • 我现在遇到了完全一样的状况,写了2个月才发现自己片面地理解了Model,以为Model只是用来做数据表的增删改查,于是所有的业务逻辑都放到了Controller,现在每个Controller都是几千行。。。

    意识到问题的严重,通过搜索来到这里。请教博主后来是怎样重构的?做成Component放到components目录下,似乎可行,只是我还不太理解正常情况下components是做什么的。。直接继承AR类放到model目录下?这好像才是“正确”的方式。。 Reply
  • Mm.. 听 weihello 的看法, 也非常有收获. 其实, 系统简单时, 再在DAO之上建一个Service层, 不免太繁琐; 当系统复杂时, 仅仅有DAO是不够的 – 我认为AR类就是DAO.

    说一句感叹, 有些系统不会有稳定的那一天, 只有它们消亡的那一天, 因为这些系统只为特定的业务而存在, 不可或缺, 但又不可复制. Reply
  • 有人也认为,高负载的应用(日PV 10亿级别),应当业务上很成熟,即便如此,我认为也是需要有个Interface来隔离视图和业务的,不然任一层次的变化都会很痛苦的。 Reply
  • 随便谈谈
    将领域相关业务做到AR里,叫所谓的充血模型,有人认为这叫“纯粹的领域模型”,实际上,纯粹的领域模型小型应用还罢,稍微复杂一点的应用,AR(所谓的Domain object)会最终被不断的侵入,还有不断的臃肿。 比如说,一些业务上的原因,一个AR需要了解过多的其他AR;再比如,引入一些业务层次的缓存,也需要侵入AR,复杂一点的逻辑,还需要侵入AR框架才能够实现。

    假如,你有2个层次,一个是所谓的DAO,这里实现存取逻辑、数据层次的缓存、数据切分等等功能;另外一个是Service层次,实现逻辑、业务层次的缓存、领域对象的合并处理等等。那么彼此就容易相安无事。从代码层次也比较好维护。


    上述一切的出发点是:是基于MVC+层次体系结构的方式去开发项目。
    MVC的Model不等于分层体系结构的业务Model层(有人叫Service),MVC的M相对于应用而言,还是属于视图层次的内容。一般而言,仅仅是特定视图的逻辑代码而已。比如你这个视图层的Model换成其他的客户端技术,就没意义了。

    所以,你需要一个独立的业务Model层(也就是所谓的Service),这样才能够使得你的视图变化,业务层代码不发生过大的变化—了不起我再来个facade service。

    无所谓高负载还是低负载,只要你的视图要变化,你的Service层还是要存在,用传统DB也罢,用时下流行的KV也罢,你都脱离不了视图层是在频繁变化的现实。


    就你现在的情况,我个人建议是,引入Helper类,将AR做成贫血模型,随着不断的重构,最后发现Helper类其实就是所谓的Service. 关于粒度的问题,我认为是根据领域的从属关系来确定,而不是人为的简单切分。 到了某一天系统相对稳定了,为了某个特定原因切分,还可以考虑。 Reply
  • 很高兴能得到 Qiang 的回复. Qiang, 你在回复中提到”例如Post::findLatestPosts()函数,你可以替换这个函数的内部使用(用AR或用SQL)”, 我想再向你请教一个问题, 因为这是我最近和同事争论的焦点.

    文中提到的”Post”类, 是一个AR类吗? 因为我的观点是使用composite方式在AR类之后创建新的Model类, 但我的一位同事认为, 应该直接在AR类里实现这些功能. 我希望能得到你在这个问题上的看法.

    另外, 我们应用了Yii框架的系统由于业务的关系, 非常庞大而复杂, 但也不是高负载应用. Yii框架我觉得有一个缺点, 就是对Model层的指导和考虑太少. 正如我的博文中提到的, 我们把AR类当作Model层, 是导致controller过于fat的原因. 现在, 我们有意再创建AR类之外的Model, 但是, 我们遇到了一个非常棘手的问题, 就是谁也说不定应该怎么组织这些代码! 直接继承AR类放到model目录下? 做成Component放到components目录下? 或者再建一个lib目录?

    而对于一些不是很类(class)的方法也不知道放在哪. 于是, 我们创建了一个Uitl类, 放在components目录下, 里面是一些常规数据处理的静态方法.

    所以, 对于Yii, 我希望能对Model层增加更多的抽象和指导. Reply
  • MVC的本质是代码重用和责任分离。举个例子,如果你的系统有前台,后台,Web API,和其它tier,你会怎么架构你的系统?

    如果你把业务逻辑写到controller里,那么它就没法被其它tier重用了。相对于model而言,controller和view的重用性要低很多,尤其是controller。这也是推荐thin controller的原因,因为越thin,表明非重用代码越少。当然,为了实现thin controller,你可能需要做很多重构工作。view可以有部分重用性,比如layout, widget/component/partial view等等。但view一般都是和tier紧密相关的,所以很难跨tier重用。model是能实现跨tier重用的,因为所有的tier最终都要和可持续数据层打交道。所以很多人推崇fat model。不过model也不能太fat了,因为越fat,说明它被引用的可能性越大(成万金油了),以至于对它的修改很容易引起各种副作用。这种情况下需要对model进行重构和拆分。

    AR是最自然的基于DB table的model。对大多数应用而言,使用AR就足够了。对于大规模多tier的应用,你可以考虑在AR基础上进一步写各种model,封装基于各种领域的业务逻辑(主要是为了防止model过于fat,不好维护的弊病)。

    对于高负载应用,更重要的是各种缓存技术的综合使用,系统的配置,数据库的优化。用直接SQL替代ORM是最后才需要做的。即便这样,维护一个model层还是很有必要的。例如Post::findLatestPosts()函数,你可以替换这个函数的内部使用(用AR或用SQL),而不用担心是否会影响使用这个函数的代码。这就是代码责任分离的好处了。 Reply
  • 回复weihello: 同意你的观点, PHP用ORM确实弊大于利. 至于高负载应用, 一般也不会分什么Model层了, 会有更多的解决方案, 是MVC之外的. Reply
  • 非开发框架之罪,而是架构之罪。
    问题可能比你想象的还要严重,小站来说,问题不大。对于百度而言不同了,应用很多,迟早一点会利用服务去形成更高层的应用,缺了这个服务层,意味着以后这个应用又需要大规模的重构了。

    而且,
    1.对于高负载应用,个人觉得使用rich model就是个错误:失去了利用层次扩展的许多灵活性…..
    2. 对于php使用类ORM,个人认为更得不尝失,毕竟PHP的灵活性和Ruby语言还是无法比拟的,况且脚本语言的array已经足够强大了。 Reply

« [1][2] » 1/2

Leave a Comment