代码改变世界

NH in Action ch3.2 Implementing the domain model

2009-12-21 10:23  jiva  阅读(265)  评论(0编辑  收藏  举报

NH in Action ch3.2 Implementing the domain model

     这一部分重点介绍一下当实现一个Domain Model的时候需要注意的地方。比如说:如何将对业务的关注和分层(比如说事务设置持久化)分离开来?什么样的持久化是需要的?是自动的呢还是透明的(automated or transparent)?

    我们首先从任何实现都必须处理的关注点分离(the separation of concerns, 个人觉得理解成抽象也挺好)开始。Domain Model Implementation 是一个集中的、组织好的组件(central, organizing component), 使系统功能中重用度最高的部分。由于这个原因,我们应该努力让非业务方面的内容(business aspects)渗透(掺杂)到我们的Domain Model implementation中。

3.2.1 Addressing leakage of concerns

      Domain Model implementation是如此重要的一部分代码以至于它不应该依赖于其他的.net APIs。比如说:Domain Model中的Code不应该执行输入输出或者通过ADO.net API来执行数据库操作,只有这样才能够使得Domain Model能够几乎在任何地方重用。最重要的是,能够在脱离Application Server或者其他的托管环境(managed environment)更方便的进行单元测试。

      个人认为关于重用性这一点:第一是Domain Model能够在一个Application中的各个部门都能够重用,而且是完整的重用。比如说有个校验唯一性的约束,提成了一个函数或者啥的,可以放在服务器端重用,还有个非空的约束,也提成了一个函数或者啥的,仍在了客户端重用,这两部分应该是一个完整的模型。这算是一种。第二个重用可能就比如说换了底层框架或者从CS迁移到BS等,在Application 架构的基础上发生变化的时候,Domain Model 不需要重新翻译或者迁移。另外作者在这里说Domain Model必须保证一定的纯度,这个算是一个严格的要求吧,个人觉得这个东西要是做到的话很难,特别是很多人同时维护一个系统,处于偷懒、取巧、笨拙等各种原因,即使给你一张白纸,过不了一个月,也会变成满天星星。如果负责人或者开发经理有洁癖,不断地重构,估计系统会比较好些。

      另外作者说单元测试能够在这种环境下更好的进行也是我非常期待的。目前我们的系统就是用了和DataSet差不多的东西,和服务器端的代码紧密的结合在一起,很不容易进行测试,虽然可以跑黑盒的自动化,但是感觉还是不爽。我们都曾有个BUG最少的梦。

     我们说Domain Model 应该只能同Modeling the Business domain 紧密联系。但是在系统中还存在其他的关注点(concerns),比如说持久化,事务管理,认证,不应当把那些处理横截关注点(cross-cutting concerns)的代码放到领域模型中来。当出现这种情况时,我们说这个叫做 leakage of concerns.

     这个地方不理解呀。比如说银行转账的那个例子,事务特性显然是这种业务天生内含的,如果和事务处理分开,如何做呢?标记: 留个疑问。

     DataSet不存在这个问题(leakage of concerns), 它不能被当做领域模型的主要原因是因为它不能包含业务规则的处理。如果从这点看,GSP还真是领域模型的数据库。更多的讨论会在持久化上展开,DataSet和NH都关注(能够处理)的部分,但是NH提供了DataSet没有的东西: transparent persistence(透明的持久化???)

3.2.2 Transparent and automated persistece

    我们可以通过DataSet 提取修改的LOG,这样就能够将这些修改持久化起来。NH提供了另一种复杂、强大的方式: it can automatically persist your changes in a way that is transparent to your domain model。

     Transparent的意思是在领域模型的持久化的类(还有一部分不需要持久化的)和持久化的逻辑之间的一个完全的彻底的关注点分离(separtaion of concerns),持久化类完全意识不到或者不依赖于持久化机制。

    比如说Item Class, 我们没有任何对NH的代码级别的依赖。

深入对Transparent的解释:

1. NH不要求持久化类必须继承自某个基类或者实现某个接口。没有任何特殊的类用来实现属性或者关联,因此, transparent persistence 增强了代码的可读性。

2. 持久化类可以在持久化的Context之外被重复使用。比如说单元测试或者UI。

3. 在一个具有transparent persistence的系统中, Objects意识不到数据存储的存在,也不需要对他们正在持久化或者获取有所察觉。持久化被一个通用的外部的持久化管理接口托管。

      Transparent persistence 很好的将持久化类和持久化解决方案解耦开来。

       我们认为transparency 是必要的。Transparent persistence 应该是任何ORM solution的主要目标之一。但是不存在完全的彻底的transparent的自动化持久解决方案, 包括NH。

       现在应该清楚为啥持久化机制应该对领域模型具有微小的影响和为啥transparent and automated persistence 是必须的了吧。DataSet是不适合的, 那么那种编程模型是你应该采用的呢?你应该采用一种被社区广泛接受的disciplined consistent 的编程模型。接下来介绍。

3.2.3 Writing POCOs

    和DataSet这种Heavy model相反的一种模型是Plain Old CLR Object(Poco).如果不考虑持久化的,这种类应该是在编写业务逻辑时最应该使用的类,为了持久化,我们放弃了Object的很多优点呀。

    当使用NH的时候, 实体实现为POCOs, NH对这些POCO类几乎没有任何强加的要求,大多数POCO都是NH兼容的。A POCO 声明了定义行为的业务方法,代表状态的属性,有些属性也代表和其他POCO直接的关联。

下图展示了一个POCO类。

clip_image002

NH不要求持久化类是Serializable的(1标识的),但是当使用.net remoting的时候,可序列化通常是需要的。

NH需要每个持久化类都有一个无参数的构造函数。(如2标识)。至少是Protected的。

POCO中的属性实现了业务实体中的特性。(如3所示)。

POCO也定义了一些业务方法.(如4所示)。如果没有这些业务方法的话,使用Object还不如使用DataSet来的方便。

3.2.4 Implementing POCO Associations

      在POCO类中使用属性来代表对象之间的关联(associations), 通过这些属性可以调用到另外的对象的方法、属性(you user accessor methods to navigate the object graph at runtime)。比如说类Category的第一个关联(association)。如下所示:

clip_image004

  对这个一对多的自关联(one to many self association)实现的脚手架代码(scaffolding code)如下:

public class Category: ISerializable

{

private string name;

private Category parentCategory;

private ISet childCategory = new HashedSet();

private Category(){}

}

      这里使用了parentCategory 和 childCategories来在上面的关联关系中进行双向导航(bidirectional navigation of the association)。parentCategory代表关联关系的single-valued end, childCategories代表在关联关系的的many-valued end, 这里的childCategories必须是Collection 类型。可以使用ISet类型定义使用HashedSet类型初始化。 HashedSet是NH提供的类库:Iesi.Collections中的。使用同IList。

      NH要求collection-type属性使用interface来定义。比如说,应该使用ISet而不是HashedSet.运行时,NH会将适合的实例使用NH自己的类来Wrap.so, Good practice to collection interfaces rather than concrete implementations.

上面的private的字段需要实现成properties.如下:

public string Name{

get{return name;}

set{name = value;}

}

public ISet ChildCategories{

get{return childCategories;}

set{childCategories = value;}

}

public Category ParentCategory{

get{return parentCategory;}

set{parentCategory = value;}

}

添加一个child category 到parent Category 大体如下:

Category aParent = new Category();

Category aChild = new Category();

aChild.ParentCategory = aParent;

aParent.ChildCategories.Add(aChild);

上面的代码包含两个操作,第一将aChild的ParentCategory指向aParent, 然后在aParent的ChildCategories中添加aChild。作者将这种Group of Operation抽象成一个方法,如下:

public void AddChildCategory(Category childCategory)

{

if(childCategory.ParentCategory != null)

childCategory.Parent.Category.ChildCategories.

Remove(childCategory);

childCategory.ParentCategory = this;

childCategories.Add(ChildCategory);

}

   当然为了避免前面的操作的错误出现,作者还建议将ChildCategories设置为private.so,上面的代码也有问题。

   到这里,作者举这个例子让我很困惑,作者想说明啥?NH对Collection的Type Constraints?

   作者把想说的话放到了灰底的块中了,作者应该是想强调在POCO中不需要考虑NH会对这种关联关系怎么处理。如果是GSP中,只需要指定ParentID那么就可以GetChildRecords或者GetMasterRecords,这种关联关系是由GSP或者说DataSet中默认维护的,但是NH却不会去维护这种关系。数据结构和算法总是紧密联系的,但这里POCO确实和NH松耦合的。

Managed relationships in NHibernate

NH 不会管理持久化的关联关系,如果你想操作对象之间的关联关系,那么,虽然你的持久层是基于NH的,但是你仍然需要像不存在NH一样Code. 假设一个关系式双向的,那么两边的关系都必须考虑到,无论如何,这是你在编写没有NH的对象的时候必须的。

其实作者这里写一个DataSet的对比一下就容易理解了。

接下来作者做了个many-to-many association的例子。Category and the item,和上面的差不多。

3.2.5 Adding logic to properites

略/

 

    总的来说,这一部分主要介绍了POCO,和DataSet相比,DataSet算是定义好的一种数据结构,这种数据结构限制了算法的编写方式或者展示方式,使得处理业务问题时无法充分利用面向对象的优势,而NH则解耦了。这是NH对DataSet的最大的优势吧。