Domain Pollution Resolution 域污染解除
Domain Pollution Resolution
域污染解除
0. Domain 名词解释
首先说明一下 Domain 在本文中的意思。
<<Domain Driven Design>> 一书,令 Domain 这个词很火。引起了广泛争论:哪些Logic 应该放在 Business Service Layer, 哪些应该放在 Domain Object里面。这类争论纷纷扬扬,最后通常都上升到哲学高度,世界观高度,认知心理学高度。如果不幸发展到极端情况,双方很可能开始相互质疑对方的智商和对世界的基本理解能力。
这个层次的Domain 纷争,不是本文所关心的话题。
我一向认为,设计能够满足如下的条件,就可以称为是一个好的设计:能够使用多态性,代替Hard-Coded if else switch 等逻辑分支;新需求来的时候,不需要在旧代码里面添加if else switch等逻辑分支,而只需要加入一个新的Class;尽量避免 Package, Class 级别的交叉引用。
本文所关心的Domain 是一个更高的层次,类似于DSL, Domain Specific Language 里面的Domain的意思。
本文主要讨论如下议题:
HTML是否只应该用来表示结构?
HTML是否应该包含逻辑?
HTML是否应该包含服务器端脚本逻辑?比如,JSP, Velocity, Freemarker.
HTML是否应该包含浏览器端脚本逻辑?比如,Java Script.
Java 代码中是否应该输出HTML标签?比如,Taglib, Tapestry Page Component, etc?
Java 代码中是否应该使用框架特殊的HTML View Model? 比如,XMLC, Wicket, Echo, etc.
HTTP Web Server本来的设计宗旨是无状态,支持大用户量,多连接。目前的server side的page flow, web flow, continuation, 力图使得HTTP Web Server保持用户的流程状态。这种做法是否应该推荐?
SQL是一种可读性很好,应用广泛的一种DSL。如何动态拼装SQL,一直是一个难以解决的问题。
是否应该在Java 代码中嵌入SQL?
是否应该在Java 代码中使用 Criteria API?
是否应该在SQL Template中加入动态语言脚本逻辑?比如 iBatis 使用XML if otherwise, OR Bridge中使用velocity?
HQL, OQL在SQL中引入了OO语言的特性,似乎操作的是对象数据库,而不是关系数据库。这种做法是否应该推荐?
Hibernate 动态期间篡改用户代码的做法,是否应该推荐?
JDO静态期间篡改拥护代码的做法,是否应该推荐?
Dynamic Proxy, CGLib等动态篡改用户代码的做法,是否应该推荐?
Reflection绕开了 Java 的类型检查机制,是否应该推荐?
Thread Local是一种隐式契约,相当于绕开了显式契约(方法签名method signature),这种做法是否应该推荐?
这些属于开发模式、代码风格方面的问题,正如所谓Code Smell的感觉一样,也避免不了主观的成分。所以,下面的陈述并非论断,而只是“在我看来”。
1. HTML Template Layer
HTML应该尽量只用来表示结构,排除所有逻辑。
JSP, Velocity, Freemarker等服务器端脚本逻辑,属于Java Code或者Script Code对HTML的污染。
不仅应该排除JSP, Velocity, Freemarker等服务器端脚本逻辑,而且应该排除浏览器端脚本逻辑,比如Java Script.
当然,这里不是说,不用Java Script,而是说,不要把Java Script放在HTML里面。把Java Script放在一个单独的.js文件里面,在HTML中引入,并使用CSS把HTML Element 和 Java Script Event Handler 联系起来。
这方面的资料有:
Unobtrusive Javascript
http://www.onlinetools.org/articles/unobtrusivejavascript/
http://www.kryogenix.org/code/browser/aqlists/
http://www.bobbyvandersluis.com/articles/goodpractices.php
如何排除HTML中的Server Side Logic呢?
Ajax!
Ajax fans一定反应迅速,给出答案。
没错。Ajax可以做到。Ajax的主要问题在于使用和掌握难度。
除了Ajax呢?
有几个选择,XMLC, Jivan, fastm, Wicket, Echo等。(and Tapestry?)
fastm是我做的一个模板层,具体内容可以在此下载。
https://fastm.dev.java.net/files/documents/1911/25042/fastm1.0c.zip
fastm采用 XML Comment 作为文档结构标记。注意,是结构标记,而不是逻辑标记。不含有for if else。
可见,fastm并不是毫无污染,只是没有逻辑污染。fastm只在HTML中添加了自定义的结构标签,也勉强算没有违背HTML只用来表示结构的原意。
Ajax, XMLC, Jivan 的HTML很干净,而且对HTML 结构的控制能力很强,能够用代码动态控制布局。fastm也能够动态控制布局。Site mesh, tiles taglib, jsp, velocity, freemarker等做不到。
fastm 里面的例子中,包括一个分页的例子,和一个动态组装SQL的例子
2. Java Web Layer
Taglib, Tapestry Page Component, Echo, Wicket等在Java 代码中输出HTML标签,这也是一种,而且要求在Java 代码中使用框架特殊的HTML View Model,比如Table, List, Label, Formbean 等View Object。使用了这些框架,Java 代码的编译就需要依赖于这些框架了。
这相当于HTML标签对Java 代码的污染。
JSP, Velocity, Freemarker都不存在这种情况。Java 代码只是提供POJO,然后 HTML里面的Server Side Script使用这些POJO.
fastm不支持逻辑,所以这个方面弱一些。fastm要求Java Code把if, else 等逻辑分支变成一个Map 结构。由于Map属于java的基本类,所以,fastm也不需要在Java code中使用任何特殊的框架相关的view model。
3. Web Layer - Stateless or Stateful
与其在服务器端支持 page flow, web flow, continuation,那不如在浏览器端支持状态。这种场合下,我觉得,正是使用Ajax的最佳场所。
我倾向于这样的设计,服务器端尽量无状态,如果确实需要状态,那么尽量在浏览器段保持状态。
lightweb是我做的一个Web框架。
https://lightweb.dev.java.net/files/documents/4371/25044/lightweb0.9b.zip
主要的特点是,URL-Centric, 鼓励无状态的Service Oriented设计。支持三种Service模型,
Action (like WebWork Action),
Controller (like Spring MVC Controller),
Channel (like Struts Action Dispatcher),
lightweb支持多级模块寻找,使用几条匹配规则,代替庞大的Site Map 配置文件。
lightweb查找View的时候,不是根据Site Map 文件里面的那种小型状态机的配置(success -> showIt.jsp; fail-> this page), 而是直接资源定位。lightweb继承了fastm的观点,把template看作资源,而不是一段运行脚本。
Lightweb设计为IoC友好。在各个环节都暴露出插件接口。程序员可以自己选择IoC策略。可以采用一般的做法,用一个IoC container集中管理,比如,Spring IoC, Pico, Nano等;也可以分散到各子模块,进行管理;也可以分散到每个Action Validator里面管理。
4. ORM Layer
Hibernate 动态期间篡改用户代码,JDO静态期间篡改用户代码。
令我想起黑客,木马,夹带,偷梁换柱等。这属于语义上的Pollution。就是说,从原有的Source看起来,Domain Object并没有那样的行为,运行起来却有那样的行为。
iBatis采用reflection,确实干净。但是,性能、功能上又不够强大。某些功能的实现,确实需要代码生成。
lightor是我做的一个ORM框架。
https://lightor.dev.java.net/files/documents/4370/25043/lightor0.5a.zip
lightor也需要代码生成。区别在于,lightor不修改任何代码,只是产生新的Mapper代码。Domain Object运行的时候,还是你原来的Domain Object。这就避免了语义的污染。
lightor生成的Mapper代码,可以编译期类型检查,可以阅读,跟踪,调试。这些生成的代码,也都遵循本文的原则。Java 代码里面不含有SQL片断,SQL都存在于单独的资源文件中。
(另外,不采用CGLib,也是为了实现的简单)
lightor直接使用Native SQL。Lightor的目的不是屏蔽关系数据库,而是恰好相反。lightor的目的是,帮助程序员更清楚地认识关系数据库,SQL,JDBC。lightor和JDBC不冲突,可以一起使用。
lightor努力的第一个主要方向就是效率。大数据量查询和处理的效率。
据我所知,目前只有lightor才支持大数据量批量处理,因为只有lightor不惜降低ORM的身份和封装层次,能够接受ResultSet作为参数。
lightor的缓存策略比Hibernate更前进了一步,把query cache 和 ID Cache集成在一起。以便得到更好的控制。并且,把缓存API暴露给程序员,以便智能的控制某个特定的缓存。
5. SQL
HQL, OQL在SQL中引入了OO语言的特性,似乎操作的是对象数据库,而不是关系数据库。
我感觉,这是一种语法层次上的Domain Pollution。OO语法对SQL语法的污染。
正如感觉LinkQ是数据库查询语法对OO语法的污染。
Java 代码中使用 Criteria API,也属于数据库查询领域对OO领域的污染。
SQL是一种可读性很好,应用广泛的一种DSL。
我的看法是,尽量把完整的SQL放到单独的资源文件中,可以直接Copy到SQL Client就可以运行。特殊优化过的Native SQL,也是同样的地位。
HQL, OQL阻碍了用户对SQL进行特殊优化。
如何动态拼装SQL,一直是一个难以解决的问题。
直接在Java 代码中嵌入SQL,肯定不行。前面说了,Java里面最好不要有SQL片断,最好放到另外的资源文件中。
在Java 代码中使用 Criteria API? 前面也否定了。
在SQL Template中加入动态语言脚本逻辑?比如 iBatis 使用XML if otherwise, OR Bridge中使用velocity?
这种方法还是不错。不过仍然在SQL里面引入了脚本逻辑。
我也没有好的做法。我采用fastm来处理。在SQL里面引入结构标记。这也相当于污染。因为SQL和HTML不同。SQL不是描述结构的,而是一门DSL。
这种做法的一个好处是,整段SQL还是可以Copy到SQL Client里面,稍微修改一番,就可以直接运行。
6. A Demo Forum
farum是我做的一个简单的forum demo. 使用了fastm, lightor, lightweb, 也尽量体现我前面提出的原则。
https://farum.dev.java.net/files/documents/4372/25045/farum0.5a.zip
我觉得,使用一门语言,应该尽量发挥它的特长,而不是针对它的短处,修修补补。
Java作为静态类型编译语言的好处,就是编译期类型检查。那么如何发挥这个优势?前面说的Mapper源代码生成,就是一种尝试。
另外,其它的类似的胶水粘合部分,比如,IoC, Validation等,都可以进行这方面的尝试。目前,farum就是采用这样的做法,自己写的Validator Source进行validation, value setting, service implementation injection等工作。
farum没有使用Dynamic Proxy, CGLib等动态篡改用户代码的做法。
尽量不使用Reflection,因为Reflection绕开了 Java 的类型检查机制。与其使用reflection,真不如使用动态脚本语言。
当然,reflection是非常难以避免的。farum也少许使用了reflection。
Thread Local是一种隐式契约,相当于绕开了显式契约(方法签名method signature)。WebWork的一些用法,Spring Framework的Open Session In View,等都是采用了Thread Local。
farum没有采用Thread Local,而是采用了另一种方法实现了Open Session In view。而且如果缓存命中,甚至不需要从connection pool中获取Connection。
总结
<<Web开发构想>>一文中,我提出了,理想中的Web开发架构是这样的:
开发速度快,运行速度快,结构清晰优雅。
具体到每一层。
Web框架层主要追求 开发速度快。
O/R层主要追求 运行速度快。
页面资源层和页面模板层主要追求 结构清晰优雅。
下面就是我对这些理想的初步实现。
还远远达不到完美的程度,而只是朝这个方向努力。
Template Layer
fastm
https://fastm.dev.java.net/files/documents/1911/25042/fastm1.0c.zip
Web Layer
lightweb
https://lightweb.dev.java.net/files/documents/4371/25044/lightweb0.9b.zip
ORM Layer
lightor
https://lightor.dev.java.net/files/documents/4370/25043/lightor0.5a.zip
A forum demo using fastm + lightor + lightweb
farum
https://farum.dev.java.net/files/documents/4372/25045/farum0.5a.zip 1 楼 nihongye 2005-12-04 我认为java最好把什么都污染了.这样只要关心java提供的一条龙服务.
从表现层一直到持久层. 2 楼 firebody 2005-12-05 nihongye 写道我认为java最好把什么都污染了.这样只要关心java提供的一条龙服务.
从表现层一直到持久层.
哈哈,我也是一样的观点,我现在关注在Server Side以Java Object Model 封装JS Object Model,绘制JS 脚本,通过JS 脚本绘制HTML DOM和event script 。 3 楼 nkoffee 2005-12-05 firebody 写道nihongye 写道我认为java最好把什么都污染了.这样只要关心java提供的一条龙服务.
从表现层一直到持久层.
哈哈,我也是一样的观点,我现在关注在Server Side以Java Object Model 封装JS Object Model,绘制JS 脚本,通过JS 脚本绘制HTML DOM和event script 。
是啊,换种角度思考,太多的强调“seperation of concerns”是不是本身就是一种错误。 4 楼 ajoo 2005-12-05 nihongye 写道我认为java最好把什么都污染了.这样只要关心java提供的一条龙服务.
从表现层一直到持久层.
不同的语言有不同的适应层面。能用java包打一切当然好。只可惜这个目标不现实。凡事顺其自然,强行用一个不擅长某项工作的语言来做事情,效果不会好。 5 楼 robbin 2005-12-05 to buaawhl:
JavaEye的文档目前正在建设中,我已经开始整理精华文章,把这篇收录进来
http://doc.iteye.com/pages/viewpage.action?pageId=212
你看看怎么样? 6 楼 buaawhl 2005-12-05 robbin 写道to buaawhl:
JavaEye的文档目前正在建设中,我已经开始整理精华文章,把这篇收录进来
http://doc.iteye.com/pages/viewpage.action?pageId=212
你看看怎么样?
多谢Robbin. :-) 而且还对文档进行了排版整理,:oops: thanks a lot.
另外,看到URL已经变成了action,基于WebWork 的新版Javaeye 快要面世了吧? 7 楼 robbin 2005-12-05 buaawhl 写道robbin 写道to buaawhl:
JavaEye的文档目前正在建设中,我已经开始整理精华文章,把这篇收录进来
http://doc.iteye.com/pages/viewpage.action?pageId=212
你看看怎么样?
多谢Robbin. :-) 而且还对文档进行了排版整理,:oops: thanks a lot.
另外,看到URL已经变成了action,基于WebWork 的新版Javaeye 快要面世了吧?
是这样子的,目前用的是confluence(不是自己开发的),一个很好的Java做的wiki,网站就准备用confluence做网站了,confluence很强大,自定义theme以后,样子也不错:
http://builder.adaptavist.com/display/ADAPTAVIST/Home
主要是意识要自己要做也做不到这么好,而且confluence自己定制也比较方便,所以就用他做网站了,而forum和blog则还是计划自己另外开发。
因为还没有正式发布,所以还没有全面开放高级会员注册,你要是觉得不错,有兴趣自己写wiki,就站内短信我(账号,密码,Email),我开账号;要是懒得自己写,好文章我也会帮助整理发布上去。 8 楼 kubite 2005-12-08 呵呵,听说Robbin要整理精华帖啊,大力支持啊,我原先是在各个版块里看到比较好的文章就下载下来然后再编译成CHM格式电子书,发现还挺爽的,在这个论坛我学到了好多东西,特别是关于领域设计方面的,特别是你那个好象是"总结领域模型"的帖子,让我看了大呼过瘾,虽然我现在在用Delphi做项目,但我一直在关注Java技术的发展,并把在Java里学到的东西看看在Delphi里能不能实现,比如我就写了一个简单的Delphi持久化框架,虽然功能不能和Hibernate比,但基本的比如一对多,多对多,缓存基本都实现了,而且现在运行的还可以,不能实现很好的一个主要的原因是Delphi里还不支持反射的功能,所以不能通过XML文件进行配置
最后希望论坛越办越好,下面有时间开帖和大家讨论领域设计的问题 9 楼 buaawhl 2005-12-09 kuky 写道to buaawhl:
你有一天突然 Martin Fowler 是的候,你你的做法是多麽的吃力不好。
Domain Pollution , 在我看是有趣但很滑稽的。
what are you talking about?
给我找找,我哪里说过,Martin folwer错了?
这里面列出来的东西,和 Martin folwer的任何事情 有什么关系?
另外,我也注明,后面的东西只是“在我看来”。
出于礼貌问题,对于你的“在你看来”,我也需要礼貌的回答一句,您可以尽量保持您的“在你看来”,您有这个权利。
只是您这句“滑稽而有趣”的莫名奇妙的不知所云的话,暴露了您的 无知 或者 态度问题。
1. 说明您没有看懂这个帖子。也许不屑于看懂。
2. 说明您也根本没看懂 Martin Folwer 的作品。
-------
另。这个帖子里面,我没有明确说明。因为在其他的帖子里面,我已经提到过了。而且我也不希望利用名人影响。
lightor 的 mapper 概念(row mapper, column mapper)就是 PEAA中描述的思路。
template层的fastm也主要对应PEAA里面描述的 template存在的if else branch 问题。 10 楼 ajoo 2005-12-09 俺对domain XXX的东西都比较无知,但俺对无知的东西基本上不插话。
不过提醒布娃娃一下,无知的指责到哪都有,当作耳旁风好了。
对了,这个标题怎么读怎么别扭,语法象是日文。 11 楼 buaawhl 2005-12-11 kuky 写道哎呀!好像又重了,我人不太,打到人意,道歉,多多包涵
可能我跟所有人的思想都不一, 所以被作是知的人待了, ok, 那我先明我的立, 如果所有人和我的思想都不一就不要管我的了, 我是UP的反者, 又是XP的反者, 世界上不止只有种方法, 我是EJB, spring, hibernate和Junit的反者, 我很佩服 Martin Fowler 的思想有先知的洞察力, 只是他做了很多事致很多人片面化了他的思想. DSL也不是真正完善的解的方法.
上我也只是根自己的, 可能你有unix系, 也有大型, unix系的目我以前搞,正在做, 真正用到足大的系的候, 流程到上甚至更多的候, 所有的引擎都重的候, 唯一的能解的西就是你的所的 polluted domain.
:D 这种现象,属于典型的 借题发挥。
实际上你讨论的规则引擎 和 性能问题,和我说的内容,没有什么关系。和Martin Folwer 的 LOP 可能有点关系。但是本文也没有提到。
既然转到了性能主题,下面也讨论一下性能。注明一下,以下的部分,其实和主题贴已经无关了。
我也是性能至上主义者。文中所述的各种Domain Pollution 解除方案,不仅没有牺牲性能,反而对性能有相当的提高。
文中没有推荐使用 规则引擎脚本,而且也反对了Reflection, CGLib等,而赞成Static Code Generation。而这部分Code Generation 正是 O / R 之间的粘合剂 Mapper 部分。Mapper 部分的效率,由于是代码生成,可以把代码展开执行,可以达到最高效率。
现在的Lightor的做法就是这样,生成的Mapper Code都是按照最优化的JDBC调用方法。采用 getInt ( int index) ; 而不是 getInt( String columnName)。
以前我发过Lightor的一个性能测试结果。Lightor能够达到比一般的JDBC ( get by column name)效率还高。
template 也是如此。fastm 不含有script logic, 把所有的logic 都推回到Java Code里面。template 输出的时候,只有结构匹配过程,而没有脚本执行过程。所以性能要高于server side script。并且fastm template体积很小,解析速度很快。
文中已经很明显地体现了,运行速度、负载量,是这套东西的 首要关心问题。其次是追求 结构优雅清晰。再次是追求 开发速度。
我的理想目标虽然不是大型网络游戏系统,但也是高负载大用户量的B2C电子商务系统和内容服务网站。比如,alibaba, ebay, yahoo, amazon 这样的网站。
你可以举个简单的例子,说明一下如何采用 Domain Pollution 解决性能问题?我们也好学习一下,性能问题 是否和 Domain Pollution 有必然联系。
EJB, spring, hibernate,这套东西里面没有用,也不打算采用。只是从hibernate中借鉴了很多实现思路。如果打算采用一些 ioc container, build tool 等, 那么就采用ajoo 开发的那一套。
虽然我不是Test 万能论者,这套东西里面也没有时间写测试,但我推崇单元测试、回归测试,因为我不知道更好的减少软件缺陷的方法。
to ajoo:
我本来是想借用英文的Resolve 的含义(解决,消解),来表达双重意思(解决方案。消除不利因素)。想不到,被 留美华侨 误认为 日文。唉,真是 失败。那你说,咋整才像 中文,或者 英文?
此文的本意,是讲述咱老百姓自己的耳闻目睹的故事。文中所述的都只是 日常工作中 常见的问题,只是借用Domain这个词语来描述大致对应于Layer的概念(HTML domain, Java Domain, SQL domain)。
比起 ajoo提出的FP, CO, IoC container, Build tool 等高阶设计话题,本文所述,属于广泛基础的日常接触的范畴。 12 楼 ajoo 2005-12-12 日文往往都是宾语o谓语的。所以这个“污染解除”很象对日文的直译。
要说咱老百姓喜闻乐见的中国话,可能会是“关于解除域污染的方案,想法”之类的。或者,更正式些:关于解决域污染问题的若干意见。(建议提交人大常委会扩大会议讨论通过) 13 楼 bluemeteor 2005-12-12 nihongye 写道我认为java最好把什么都污染了.这样只要关心java提供的一条龙服务.
从表现层一直到持久层.
java何时能污染数据库呢?ORM或说HQL还远远说不上污染....有个朋友是一个杰出的DBA,在他看来JAVA所有的事务管理都是何必呢?他的思想是有强大的Database开发技术做基础,任何DB I/O是需要一次请求即可实现,狂汗...每次因为这个都要和他吵架..
还有表现层....java不是万能,虽然我希望她是