文 章 编 号 :1007-757X(2012)1-0036-04 领 域 驱 动 模 型 的 WEB 软 件 系 统 设 计 研 究 摘 要 : J2EE 3 JDK1.7 Tomcat WEB 关 键 词 : 中 图 分 类 号 :TP311 文 献 标 志 码 :A 0 引 言 Web 软 件 系 统 的 分 层 结 构 典 型 的 J2EE 软 件 系 统 开 发 方 法 分 为 三 层 结 构, 即 表 现 层 中 间 层 和 数 据 服 务 层 三 层 体 系 中 将 业 务 规 则 数 据 访 问 及 合 法 性 校 验 等 工 作 放 在 中 间 层 处 理 客 户 端 不 直 接 与 数 据 库 交 互, 而 是 通 过 组 件 与 中 间 层 建 立 连 接, 再 由 中 间 层 与 数 据 库 交 互 中 间 层 为 了 将 控 制 层 与 业 务 逻 辑 层 分 离, 又 细 分 为 Web 层 Service 层 DAO(Data Aecess Otject) 层 和 PO 层 Web 层 就 是 MVC(Model View Control) 模 式 里 面 的 C (controller), 负 责 控 制 业 务 逻 辑 层 与 表 现 层 的 交 互, 调 用 业 务 逻 辑 层, 并 将 业 务 数 据 返 回 给 表 现 层 作 组 织 表 现 Service 层 ( 就 是 业 务 逻 辑 Business Logic 层 ), 负 责 实 现 业 务 逻 辑 业 务 逻 辑 层 以 DAO 层 为 基 础, 通 过 对 DAO 组 件 的 正 面 模 式 包 装, 完 成 系 统 所 要 求 的 业 务 逻 辑 DAO 层, 负 责 与 持 久 化 对 象 交 互 该 层 封 装 了 数 据 的 增 删 查 改 (CRUD) 的 操 作 PO 即 持 久 化 对 象, 通 过 实 体 关 系 映 射 工 具 将 关 系 型 数 据 库 的 数 据 映 射 成 对 象, 很 方 便 地 实 现 以 面 向 对 象 方 式 操 作 数 据 库 数 据 服 务 层 用 来 存 放 数 据 自 2002 年 struts,spring,hibernate 框 架 成 熟 以 来, 以 SSH 为 核 心 的 3 层 架 构 开 发 模 式 一 直 是 J2EE 系 统 开 发 的 主 流 开 发 模 式 中 间 层 采 用 的 是 流 行 的 Spring+Hibernate,Web 控 制 层 用 Struts Spring 的 作 用 贯 穿 了 整 个 中 间 层, 将 Web 层 Service 层 DAO 层 及 PO 无 缝 整 合 而 随 着 时 间 的 推 移, 这 种 开 发 模 式 最 终 遇 到 了 它 自 身 瓶 颈 导 致 这 个 瓶 颈 的 根 本 原 因 是 当 时 java 语 言 本 身 的 缺 陷 及 servlet 本 身 需 要 完 善 等 等 由 于 这 3 个 框 架 也 是 在 这 此 两 者 的 不 完 善 的 基 础 上 发 展 而 来, 导 致 了 J2EE 域 模 型 的 开 发 结 构 一 直 为 贫 血 模 型 1.2 基 于 领 域 驱 动 设 计 的 WEB 软 件 系 统 研 究 领 域 驱 动 设 计 (Domain-drived design) 是 领 域 模 型 的 出 色 专 家 Eric Evans 在 其 著 作 领 域 驱 动 设 计 里 提 出 的 概 念 [1] 在 DDD 的 范 畴 内, 通 常 把 领 域 模 型 分 为 贫 血 的 领 域 模 [2] 型 和 充 血 的 领 域 模 型 [3] 贫 血 模 型 就 是 域 对 象 ( 领 域 对 象 ) 除 了 get 和 set 方 法 或 者 包 含 少 量 CRUD 方 法, 整 个 对 象 充 当 的 就 是 一 个 数 据 容 器, 所 有 的 业 务 逻 辑 都 不 包 含 在 内, 而 是 放 在 Business Logic 层 或 者 Service 层, 这 是 Java Web 程 36 序 采 用 的 最 常 用 开 发 模 型 优 点 是 系 统 的 层 次 结 构 清 晰, 各 层 之 间 单 向 依 赖 缺 点 是 不 够 面 向 对 象, 领 域 对 象 只 是 作 为 保 存 状 态 或 者 传 递 状 态, 所 有 的 业 务 都 在 Service 中 处 理, 当 业 务 越 来 越 复 杂 时,Service 会 变 得 越 来 越 庞 大, 让 系 统 慢 慢 陷 入 泥 潭 最 终 难 以 理 解 和 维 护 还 有 很 重 要 的 一 点 是, 当 添 加 一 个 新 的 UI 时, 很 多 业 务 逻 辑 得 重 新 写 例 如 当 提 供 Web Service 的 接 口 时, 原 来 为 Web 界 面 提 供 的 Service 就 很 难 重 用, 导 致 重 复 的 业 务 逻 辑 如 何 保 持 业 务 逻 辑 的 一 致 是 很 大 的 挑 战 充 血 模 型 与 贫 血 模 型 相 反 域 对 象 要 继 承 关 键 业 务 逻 辑, 业 务 逻 辑 在 多 个 域 对 象 之 间 分 配, 而 Business Logic 只 是 简 单 封 装 以 及 控 制 事 务 权 限 及 完 成 一 些 不 适 合 放 在 域 模 型 中 的 业 务 逻 辑, 它 是 非 常 薄 的 一 层, 它 指 挥 多 个 模 型 对 象 来 完 成 业 务 功 能 优 点 是 面 向 对 象,Business Logic 符 合 单 一 职 责, 不 像 在 贫 血 模 型 里 那 样 包 含 所 有 的 业 务 逻 辑 太 过 沉 重 当 业 务 变 得 复 杂 时, 充 血 领 域 模 型 显 出 巨 大 的 优 势 当 需 要 多 个 UI 接 口 时, 域 对 象 可 以 重 用, 并 且 业 务 逻 辑 只 在 领 域 层 中 出 现, 这 使 得 很 容 易 对 多 个 UI 接 口 保 持 业 务 逻 辑 一 致, 如 图 1 所 示 : 图 1 J2EE 多 层 结 构 与 充 血 模 型 比 较 通 过 图 1 左 半 部 分 可 以 看 到, 贫 血 模 型 方 式 造 成 J2EE 开 发 层 次 之 间 调 用 混 乱, 修 改 和 拓 展 非 常 不 方 便, 而 在 右 半 部 分 的 充 血 模 式 开 发 方 式 下, 界 面 ( 边 界 ) 对 象 就 是 域 对 象, 作 者 简 介 : 王 非 (1961-), 北 师 大 珠 海 分 校 信 息 技 术 学 院, 教 授, 硕 士, 研 究 方 向 :WEB 信 息 系 统, 广 东 珠 海,519085
没 有 多 余 的 Contruol 或 Action 了 原 来 Domain 层 被 服 务 层 Service layer 遮 挡, 在 右 边 图 中, 则 Domain 层 直 接 暴 露 给 前 台 了, 没 有 被 遮 挡, 裸 露 了 这 样 一 步 到 位 实 现 领 域 模 型 到 界 面 的 过 渡, 可 以 将 设 计 的 核 心 模 型 和 客 户 要 求 的 界 面 需 求 做 到 完 整 的 统 一 面 向 对 象 大 师 Martin Fowler 不 止 一 次 的 在 他 的 博 客 和 著 作 企 业 应 用 架 构 模 式 [4] 中 倡 导 过 上 述 概 念 在 设 计 中 的 巨 大 威 力 Eric Evans 也 在 其 著 作 中 提 供 了 不 少 宝 贵 经 验 和 方 法 充 血 模 型 的 缺 点 是 如 何 划 分 业 务 逻 辑, 什 么 样 的 逻 辑 放 在 domain object 中, 什 么 样 的 逻 辑 放 在 service 层 中, 这 是 不 确 定 的, 对 开 发 人 员 的 素 质 要 求 较 高 2 基 于 充 血 模 型 的 Web 系 统 开 发 框 架 Xtreme 为 实 现 以 充 血 模 型 结 构 为 基 础 的 新 型 Web 系 统 开 发, 本 文 提 出 了 通 过 运 用 最 新 JDK1.7 及 Tomcat 的 相 应 改 造, 来 设 计 所 需 要 使 用 的 框 架 以 完 成 软 件 系 统 的 开 发, 用 实 际 的 代 码 以 及 具 体 对 代 码 的 分 析 来 描 述 开 发 框 架 如 何 运 用 于 开 发 并 提 高 工 作 效 率, 从 而 解 决 传 统 J2EE 架 构 结 构 复 杂, 开 发 繁 琐 的 缺 点 以 下 给 出 Xtreme 设 计 的 主 要 思 路 与 方 法 2.1 控 制 层 与 视 图 层 每 一 个 域 模 型 都 有 对 应 的 一 个 控 制 器 (Controller), 通 常 命 名 方 式 为 ModelNameController 如 果 一 个 类 带 有, 那 么 框 架 会 默 认 视 为 控 制 器 Java 代 码 import com.yangtai.main.context; import com.yangtai.main.controller; } 那 么 在 这 个 类 中 定 义 的 所 有 public 方 法 都 是 一 个 Action public void index(context context) { context.out("user.html"); } public void show(user user, java.io.printwriter out) { out.pri ntln(" 姓 名 :" + user.name); out.println(" 年 龄 :"+user.age); } } 可 以 通 过 "ModelName/ 方 法 名 " 这 样 的 url 来 访 问 这 个 Action, 例 如 :="user/show" 就 表 示 由 UserCotroller 中 的 showr 方 法 来 处 理 表 单 如 果 在 url 中 未 指 明 方 法 名, 比 如 http://localhost:8080/user, 那 么 就 会 调 用 默 认 的 index 方 法 http://localhost:8080/user 与 http://localhost:8080/user/index 是 等 价 的 如 果 不 喜 欢 index 这 个 默 认 的 方 法 名, 而 是 喜 欢 main 这 样 的 方 法 名, 可 以 把 改 成 (defaultaction="main") 这 里 特 别 说 明 以 下 两 点, 第 一 点 是 Action 方 法 声 明 中 的 参 数, 每 个 Controller 类 中 都 有 index 方 法, 但 是 方 法 的 参 数 可 以 是 不 同 的 列 出 来 对 比 一 下 : index(printwriter out) 37 index(string name, Context context) index(context context) 对 比 传 统 的 Servlet 下 面 这 两 个 方 法 : dopost(httpservletrequest req, HttpServletResponse resp) doget(httpservletrequest req, HttpServletResponse resp) 参 数 是 确 定 的, 是 由 Sun 在 规 范 中 事 先 定 好 的, 你 只 能 去 覆 盖 dopost doget 而 Xtreame 开 发 框 架 中 Action 的 参 数 类 型 个 数 顺 序 都 是 灵 活 的, 是 由 正 在 写 程 序 的 人 自 己 定 的 第 二 点 是 Action 对 Http 请 求 参 数 的 自 动 封 装 与 show(user user, java.io.printwriter out) 中 User 类 有 点 类 似 JavaBeans, 但 又 不 大 一 样 如 果 你 给 一 个 类 加 上 @Form 这 个 Annotation( 注 释 ), 然 后 把 这 个 类 作 为 Action 方 法 参 数 的 类 型, 那 么 编 译 器 就 会 推 断 出 想 把 Http 请 求 参 数 值 自 动 装 配 到 这 个 类 的 实 例 变 量 中 接 着 编 译 器 就 会 在 这 样 的 类 中 继 续 寻 找 所 有 带 有 @Setter 的 public 方 法, 然 后 解 析 方 法 参 数, 编 译 器 同 样 就 会 推 断 出 想 要 把 Http 请 求 参 数 值 自 动 装 配 到 这 个 方 法 的 参 数 中 Java 代 码 如 下 : import com.yangtai.main.form; import com.yangtai.main.setter; @Form public class User { String name; int age; @Setter public void init(string name, int age) { this.name = name; this.age = age; } } import com.yangtai.main.context; import com.yangtai.main.controller; public void index(context context) { } public void show(user user, java.io.printwriter out) { out.pri ntln(" 姓 名 :"+user.name); out.println(" 年 龄 :"+user.age); } } 2.2 域 模 型 的 设 计 业 务 领 域 建 模 同 样 是 一 个 比 平 台 架 构 更 复 杂 更 需 要 精 心 研 究 的 内 容 在 一 些 文 献 中 有 详 细 的 讨 论 [4-7], 本 文 的 重 点 在 于 说 明 开 发 充 血 模 型 系 统 的 架 构 xtreme 如 何 支 持 系 统 开 发, 因 而 这 里 简 单 说 明 域 模 型 的 设 计 域 模 型 设 计 的 步 骤 是 首 先 从 业 务 描 述 中 提 取 名 词, 从 提 取 出 来 的 名 词 中 总 结 业 务 实 体, 区 分 名 词 中 的 属 性 角 色 实 体 实 例, 形 成 问 题 域 中 操 作 实 体 的 集 合, 在 业 务 实 体 集 合 中 抽 象 业 务 模 型, 建 立 问 题 域 的 概 念, 最 后 用 UML 提 供 的 方 法 和 图 例 进 行 领 域 模 型 设 计 确 定 模 型 之 间 的 关 系 域 模 型 需 要 包 含 的 内 容 有 数 据 建 模 ( 基 本 数 据 类 型 定 义 数 据 验 证 和 关 联 定 义 等 ) 及 确 定 与 数 据 库 相 关 的 操 作
下 面 通 过 代 码 简 单 说 明 : 基 本 数 据 类 型, 可 以 通 过 添 加 属 性 来 自 定 义 类 : public class Person { private Date lastvisit } 当 类 被 标 注 为 时, 开 发 框 架 在 运 行 时 就 已 经 为 该 类 注 入 了 CRUD 方 法 以 及 查 询 方 法 数 据 验 证, 两 种 方 法, 一 种 是 通 过 定 义 注 解, 可 用 如 下 面 代 码 : public class User { @Length(min=, max=) @NotNull private Date lastvisit } 另 一 种 方 法 是 验 证 调 用, 可 以 在 任 何 实 体 中 调 用 validate 方 法 验 证 domain:: if(user.validate()) { // do something with user } else { user.errors.allerrors.each { println it } } domain 的 errors 属 性 是 一 个 类 似 Spring Errors 接 口 实 例. Errors 提 供 用 于 导 航 验 证 错 误 以 及 取 回 原 始 值 的 方 法 本 质 上 有 两 个 验 证 阶 段, 第 一 个 阶 段 是 data binding, 当 把 请 求 参 数 绑 定 到 实 体 上 发 生 验 证 的 第 二 阶 段 发 生 在 当 调 用 validate 或 save 时 这 时, 将 会 验 证 在 constraints 定 义 的 约 束 值 比 如, 默 认 的 持 久 方 法 save 会 在 执 行 之 前 调 用 validate 因 此, 允 许 像 下 面 这 样 编 码 : if(user.save()) { return user } else { user.errors.allerrors.each { println it } } 域 模 型 的 关 联 定 义 确 定 domain 类 之 间 的 相 互 作 用, 有 one-to-one one-to-many many-to-many 另 外 域 模 型 的 设 计 还 包 括 有 与 数 据 库 相 关 的 操 作 等 其 他 很 多 内 容, 由 于 篇 幅 的 关 系 不 再 赘 述 图 2 系 统 需 求 分 析 最 后 用 UML 提 供 的 方 法 和 图 例 进 行 领 域 模 型 设 计 确 定 模 型 之 间 的 关 系, 如 图 3 所 示 : 如 图 4 所 示 : 图 3 域 模 型 UML 图 3 设 计 实 例 用 一 个 简 单 的 网 店 系 统 作 为 实 例 说 明 Xtreame 的 具 体 应 用 3.1 系 统 建 模 由 于 是 一 简 单 的 网 店, 从 业 务 描 述 中 可 提 取 名 词 有 : 商 品, 商 品 分 类, 客 户, 店 员, 购 物 车, 商 品 类 别 通 过 提 取 出 来 的 名 词 中 总 结 业 务 实 体, 区 分 名 词 中 的 属 性 角 色 实 体 实 例, 形 成 问 题 域 中 操 作 实 体 的 集 合 再 从 业 务 实 体 集 合 中 抽 象 业 务 模 型, 建 立 问 题 域 的 概 念, 如 图 2 所 示 : 38 图 4 对 应 贫 血 模 型 的 UML 图 由 UML 图 我 们 可 以 看 出 Xtreme 开 发 方 法 的 UML 图 的 类 结 构 相 当 简 约, 对 比 一 下 传 统 开 发 方 法 同 样 功 能 的 UML 图, 不 难 发 现, 开 发 的 任 务 量 的 确 减 轻 不 少 3.2 系 统 实 现
下 面 给 出 域 模 型 及 控 制 器 的 实 现 细 节 Person 类 Java 代 码 public class Person { private Date lastvisit private String password private String email } Product 类 Java 代 码 public class Product{ private Date addtime private ProductCategory category private Admin admin boolean isnew() { id == null } } ProductCategory 类 Customer 等 类 的 代 码 省 略 当 类 被 标 注 为 时, 开 发 框 架 在 运 行 时 就 已 经 为 该 类 注 入 了 CRUD 方 法 以 及 查 询 方 法, 可 以 在 后 面 的 Controller 中 方 便 地 用 到 这 些 方 法, 是 实 体 类 必 备 的 和 数 据 库 的 相 关 操 作, 开 发 框 架 已 经 做 好, 只 需 要 关 注 业 务 逻 辑 控 制 器 实 现 如 下 : ProductController 类 Java 代 码 class ProductController { public void add(context context,product product) { if(product.save()){ context.out("product.html"); } } public void edit (Context context,long productid) { Product product = Product.get(productId); if(product.save()){ context.out("product.html "); } } 上 述 很 少 的 类, 很 少 的 代 码, 就 完 成 了 原 来 需 要 经 过 建 立 Dao,Service,Action,ActionForm 等 许 多 繁 琐 的 步 骤, 才 能 实 现 的 软 件 开 发 任 务 4 结 束 语 软 件 开 发 模 型 将 采 用 领 域 驱 动 设 计 的 充 血 模 型 结 构 慢 慢 取 代 传 统 的 贫 血 模 型 结 构 网 站 系 统 开 发 也 慢 慢 慢 向 动 态 语 言 发 展 随 着 充 血 的 领 域 驱 动 设 计 的 理 论 不 断 实 践 及 完 善, 相 信 未 来 的 软 件 开 发 速 度 将 比 现 在 提 高 数 倍, 充 血 模 型 越 是 真 实 的 反 映 领 域 本 质, 软 件 开 发 速 度 就 会 越 来 越 快, 因 为 很 多 功 能 在 领 域 模 型 里 面 已 经 完 成 了, 需 要 的 时 候 只 要 复 用 就 可 以 了 这 样 自 然 就 能 提 高 软 件 系 统 的 可 扩 展 性 和 可 维 护 性, 快 速 应 对 软 件 开 发 中 的 变 化 充 血 模 型 应 用 于 实 际 的 系 统 开 发 是 完 全 可 行 的 参 考 文 献 : [1] Eric Evans. [M]. :,2006. [2] Martin Fowler. [3] http://www.martinfowler.com/bliki/anemicdomainmode l.html [4] robbin. http://robbin.javaeye.com/blog/17579?page=1#comment s. [ J].,2007. [5] Landre E, Wesenberg H, Olmheim J. Agile enterprise software development using domain-driven design and test first[c], The 22th ACM SIGPLAN conference on object oriented programming systems and ppplications. Quebec,2007:983-993. [6] Wesenberg H, Olmheim J, Landre E. Using domain-driven design to evaluate commercial of the shelf software[c]. The 21th ACM SIGPLAN conference on object oriented programming systems and ppplications. Oregon, 2006:809-814. [7]. [J]. 2008,7(2):37-39.. ( 2011.09.02) ( 上 接 第 35 页 ) 货 及 方 便 药 品 召 回, 避 免 类 似 的 药 品 名 称 剂 量 与 剂 型 之 间 发 生 混 淆, 强 化 药 品 管 理, 确 保 药 品 供 给 及 时 准 备 实 现 血 液 制 品 的 非 接 触 式 识 别, 减 少 血 液 污 染, 实 现 多 目 标 识 别, 提 高 数 据 采 集 效 率 [3] 4 结 语 健 康 物 联 是 一 种 全 新 的 概 念, 随 着 我 们 国 民 对 生 命 健 康 需 求 的 不 断 提 高, 随 着 以 健 康 为 中 心 的 新 健 康 观 不 断 被 人 们 所 接 受, 随 着 健 康 服 务 业 务 和 健 康 信 息 化 技 术 的 快 速 发 展, 健 康 物 联 网 必 将 得 到 更 加 广 阔 的 发 展 与 应 用 参 考 文 献 : [1] ITU ITU 2005 [2] J. 2010 1 :1-3 [3], : J. 2010 5 [4], J. 2010 8 :23-27 [5] Baker C R Armijo K Belka S et al. Wireless Sensor Networks for Home Health Care[C]//21st International Conference on Advanced Information Networking and Applications Workshops Niagara Falls Ontario May 21-23 2007. New York IEEE 2007 2 832-837. [6] Moh M Walker Z Hamada T et al. A prototype on RFID andsensor networks for elder healthcare progress report[c]//sigcommworkshop on Experimental Approaches to Wireless Network Designand Analysis Philadelphia Pennsylvania August 22 2005. NewYork ACM 2005 70-75. ( 2011.09.26) 39