我在 IntelliGrape 工作,这是一家专门使用 Groovy & Grails 进行开发的公司。本文是我们 Grails 项目遵循的最佳实践的基本清单,收集自邮件列表、Stack Overflow、博文,播客和 IntelliGrape 的内部讨论。它们分为控制器、服务、Domain、视图、TagLib、测试和其他。
这里的建议是专门针对 Grails 2.0 的,尽管其中的大多数是多个版本都适用的。
控制器
- 不允许控制器充当其他角色。控制器的角色就是接受传入的请求、检查权限等、问 Domain 或 Service 要结果、将结果用所需的格式(如 HTML、JSON 或 XML)返回给请求者。要让控制器保持苗条。别让它执行业务逻辑、查询或更新。
- 如果一个控制器代表了一个单一的 Domain Class,使用标准的命名公式“
Controller”为控制器命名。 - 避免代码重复 - 通用的操作应该提取成闭包或者方法。更多信息参见这篇博文。
- 将复杂的数据绑定划分为一个 Command 对象。可以让 Command 对象丰富些(就像富 Domain Class)。创建一个有层次的 Comand 对象在某些情况下也是有用的。
服务
- 服务是复杂业务逻辑或粗粒度代码的最佳候选。如果有必要,服务 API 可以很容易的暴露成一个 RESTful/SOAP Web 服务。
- 服务缺省为事务性的,但是如果服务的方法没有更新持久化存储,可以设置为非事务性的。
视图
- 尽量保持视图的简单 - 在这一层,抵制放置业务或数据库逻辑的诱惑。
- 使用布局(Layout)保证应用的所有页面或者页面子集有一个统一的外观。
- 让你的视图保持 DRY(别重复你自己)。将重复内容放在模板中。
- 对通用的 UI 元素,使用自定义的 TagLib。
Domain
- 最好将模型 Domain 特定的逻辑放在自己的 Domain 中。符合“单个 Domain 加少数依赖”的任何代码都应该被放到自己的 Domain Class 里。但仅限于那个 Domain 相关的逻辑,处理多个 Domain 的更复杂业务逻辑属于服务。
- 为了重用公共局部查询或者分解复杂逻辑,使用命名查询,并根据需要把它们组合起来,就像常见的 jQuery 函数链式调用一般。
- 别在 Domain 文件夹下混入其他的通用工具类或数值对象,它们应该呆在 src/groovy 目录下。如果这些类需要支持验证,可以用 @Validateable 对其进行注解。
- 使用有意义的构造函数实例化 Domain 对象,避免任何不必要的状态并只构造有效的对象。
TagLib
- 保持各标签的精简。一个标签可以调用其他标签,如果需要,一个标签可以分解成可重用的子标签。
- TagLib 可以被认为是 MVC 架构中视图层的一部分,但深入 Domain 内部组装或者格式化数据显示也是可以接受的。依旧遵循(即不是完全禁止)最小化与 Domain 直接交互的原则。
- 它应该包含更多的逻辑,而非渲染;虽然有少许渲染还是不错滴。
- 为了更好的组织结构,可以使用多个自定义的 TagLib。
测试
- 较之集成测试,更偏爱单元测试。不仅因为它们运行 / 调试更快,而且能更好地强制松耦合。服务的测试比较特殊,使用集成测试会更好些。
- 在单元测试中,使用 save(validate:false) 保存没有完全装入的对象。
Config.groovy
- 将所有环境特定的配置放在 Config.groovy,如 serverURL、每个环境下各异的常量等等。
- 将个人配置内容(比如本地数据库用户名或密码等等)放在
Config.groovy 文件中,并加入到版本控制系统的忽略列表中,以便每个团队成员能够根据自己的特定需求覆盖配置。 - 虽有些争议,但我们建议设置 grails.gorm.failOnError = true,这样只要保存对象时 Domain 校验失败就会抛出一个异常。由此,你就不再需要检查是否保存成功。
- 在 Grails 2.0 中,缺省“grails.hibernate.cache.queries = true”,毋须添加 cache:true 就会自动缓存查询。把它设为 false,只在真正有助于提高性能时才缓存。
杂项
- 理解并坚持 Grails 的惯例,因为 Grails 就是惯例驱动的。使用这些惯例会让你的开发者生活更轻松。
- 在不同的包中组织 Grails 的制品,别用com.businessname.appname.domain和com.businessname.appname.controller。否则在 FooController 中,我们将最终需要导入 Foo 类。 既然 Grails 已经将这些制品放在了不同目录下,它们就不需要再被分离了。可以参考这篇博文。
- fixtures 插件可以在开发时用来初始化数据。
- 将你应用的可重用部分开发成 Grails 插件。这些插件可以独立测试,还可以消除你的应用的复杂性。如果你认为其他人会需要这些插件,你还可以把插件发布到公共的插件库中。
- 更新脚手架模板以生成你的项目特定的视图和控制器。
- 青睐动态脚手架,而不使用静态脚手架,除非前者不满足你的需求。例如,如果仅需要修改“save” action,你可以仅重载“save” action,在运行时动态生成其他脚手架代码。
- 总在 DataSource.groovy 中设置数据库的重连属性是很好的习惯。
- 总保证自己应用中含有一个外部的配置文件(那怕是一个空文件),这样,在产品环境中需要覆盖任何配置时都无需重新生成一个新的 WAR 文件。
- 如果你想对你所使用的插件进行小的改动,例如改变 quartz 监控插件的 list.gsp 以符合应用主题,那么不要因为这点小改动而把插件源代码包含到项目里,你可以遵循相同的目录结构或包重载这些文件。原因是应用的优先级比插件高。
- Domain 中所有自定义的校验可以放在一个共享的校验文件中,以便其他 Domain 重用这些约束。参见这里的示例。
- 在应用中安装任何的插件,最好在 BuildConfig.groovy 文件中声明,而不是使用 install-plugin 命令。请看这个线索了解详情。
我还漏了什么吗?你们的项目或组织中遵循哪些 Grails 开发的最佳实践?可以通过评论分享出来,以便将来丰富本文的内容。
关于作者
Amit Jain是一位软件工程师,具有 4 年多的 Java 和 Grails 开发经验。他是一位敏捷的爱好者和认证的 Scrum Master。在最近 3 年中,他就职于 IntelliGrape,这是一家专门使用 Groovy & Grails 进行开发、 敏捷外包及项目的公司。参见 IntelliGrape 网站。
查看英文原文: Grails Best Practices
感谢胡键对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。
评论