This commit is contained in:
by931
2022-09-06 22:30:37 +08:00
parent 66970f3e38
commit 3d6528675a
796 changed files with 3382 additions and 3382 deletions

View File

@@ -560,11 +560,11 @@ FROM dbo.Forums_Categories INNER JOIN
</code></pre>
<p>如上所示,创建视图的 SQL 语句封装了对论坛主题数、回复数等数据的统计业务逻辑。</p>
<p>显然,遵循职责分离的原则,数据设计模型主要包含三部分的职责:业务逻辑、数据访问及数据。映射为对象模型,就是与数据表一一对应并持有数据的持久化对象,封装了 SQL 数据访问逻辑的数据访问对象Data Access ObjectDAO以及满足业务用例需求的服务对象。三者之间的关系如下图所示</p>
<p><img src="assets/f832ef40-7fd5-11e9-ace0-ad297907c3be" alt="enter image description here" /></p>
<p><img src="assets/f832ef40-7fd5-11e9-ace0-ad297907c3be" alt="png" /></p>
<h4>数据访问对象</h4>
<p>数据访问对象属于 J2EE 核心模式中的一种,引入它的目的是封装数据访问及操作的逻辑,并分离持久化逻辑与业务逻辑,使得数据源可以独立于业务逻辑而变化。</p>
<p>《J2EE 核心模式》认为:“数据访问对象负责管理与数据源的连接,并通过此连接获取、存储数据。”一个典型的数据访问对象模式如下图所示:</p>
<p><img src="assets/6ea8b5b0-7fd6-11e9-8b24-8b44cf4ff051" alt="enter image description here" /></p>
<p><img src="assets/6ea8b5b0-7fd6-11e9-8b24-8b44cf4ff051" alt="png" /></p>
<p>图中的 Data 是一个传输对象,如果将该 Data 定义为表数据对象,它可以处理表中所有的行,如 RecordSet或者由 ADO.NET 中的 IDataReader 提供类似数据库游标的访问能力就相当于运用了《企业应用架构模式》中的表数据入口Table Data Gateway模式。如果 Data 是这里提及的代表领域概念的持久化对象,则需要引入 ResultSet 到 Data 之间的映射器这时就可以运用数据映射器Data Mapper模式。如下所示</p>
<pre><code class="language-java">public class Part {
private String name;
@@ -606,7 +606,7 @@ public class PartMapper {
</code></pre>
<h4>持久化对象</h4>
<p>在数据设计模型中,持久化对象可以作为数据的持有者传递给服务、数据访问对象甚至是 UI 控件。早期的开发框架流行为持有数据的对象定义一个通用的数据结构,同时为 UI 控件提供绑定该数据结构的能力。如 ADO.NET 框架就定义了 DataSet、DataTable 等数据结构ASP.NET Web Form 则提供绑定这些数据结构的能力。例如,我们要显示商品的类别,在 Web 前端就定义了属于 System.Web.UI.Page 类型的 Web 页面 CategoriesPage它与数据访问对象以及持久化对象的交互如下图所示</p>
<p><img src="assets/2c47ccf0-7fd7-11e9-91b9-2513956f5ea9" alt="enter image description here" /></p>
<p><img src="assets/2c47ccf0-7fd7-11e9-91b9-2513956f5ea9" alt="png" /></p>
<p>图中的 DataTable 通过 CategoriesDAO 直接返回,它实际上是 ADO.NET 框架定义的通用类型。在一些 .NET 开发实践中,还可以定义强类型的 DataSet 或 DataTable方法是定义一个代表业务概念的类例如 Categories让它派生自 DataTable 类。</p>
<p>随着轻量级容器的流行越来越多的开发人员认识到持久化对象强依赖于框架带来的危害POJOPlain Old Java Object和 POCOPlain Old CLR Object得到了大家的认可和重视。Martin Fowler 甚至将其称之为持久化透明Persistence IgnorancePI的对象用以形容这样的持久化对象与具体的持久化实现机制之间的隔离。理想状态下的持久化对象不应该依赖于除开发语言平台之外的任何框架。</p>
<p>在《领域驱动设计与模式实战》一书中Jimmy Nilsson 总结了如下特征,他认为这些特征违背了持久化透明的原则:</p>
@@ -666,7 +666,7 @@ object Report extends SQLSyntaxSupport[Report] {
</code></pre>
<p>类 Report 并没有继承任何类,但却利用 Scala 的隐式参数依赖了框架定义的 DBSession然后通过 Report 的伴生对象去继承名为 SQLSyntaxSupport[T] 的特性,以及组合调用了 withSQL 对象。显然,活动记录在满足了快速编码与代码重用的同时,也带来了与数据访问框架的紧耦合。</p>
<p>当持久化对象被运用到 CQRS 模式中时,查询端通过查询外观直接访问一个薄的数据层,如下图右端所示:</p>
<p><img src="assets/c439a880-7fd7-11e9-a061-51370b206b66" alt="enter image description here" /></p>
<p><img src="assets/c439a880-7fd7-11e9-a061-51370b206b66" alt="png" /></p>
<p>这个薄的数据层通过数据访问对象结合 SQL 语句直接访问数据库,返回一个表数据记录 ResultSet然后直接将其转换为 POJO 形式的数据传输对象DTO对象。这是因为查询端仅涉及到数据库的查询因此并不需要持久化对象至于添加、删除与修改则属于命令端采用的是领域模型而非数据模型。</p>
<h4>服务对象</h4>
<p>由于持久化对象和数据访问对象都不包含业务逻辑服务就成为了业务逻辑的唯一栖身之地。这时持久化对象是数据的提供者实现服务时就会非常自然地选择事务脚本Transaction Script模式。</p>
@@ -731,7 +731,7 @@ object Report extends SQLSyntaxSupport[Report] {
}
</code></pre>
<p>在采用事务脚本时同样需要考虑职责的分配每个类应该围绕一个主题将相关的事务脚本组织在一起。为了更好地应对事务脚本的变化可以考虑让一个事务脚本对应一个类并通过识别事务脚本的共同特征引入命令Command模式。例如推荐朋友事务脚本和推荐音乐事务脚本</p>
<p><img src="assets/f43067e0-7fd7-11e9-ace0-ad297907c3be" alt="enter image description here" /></p>
<p><img src="assets/f43067e0-7fd7-11e9-ace0-ad297907c3be" alt="png" /></p>
<p>当然无论对于事务脚本做出怎样的设计改进只要不曾改变事务脚本的过程设计本质一旦业务逻辑变得更加复杂时就会变得捉襟见肘。Martin Fowler 就认为:</p>
<blockquote>
<p>“当事物一旦变得那么复杂时,就很难使用事务脚本保持一个一致的设计。”</p>