本文地址:http://www.cnblogs.com/egger/archive/2013/03/10/2952849.html   欢迎转载 ,请保留此链接๑•́ ₃•̀๑!  

理解业务组织模式

  作为开发者,要理解所有领域逻辑模式的优缺点,这样才能使用最合适的模式。谨记并非所有应用程序都是一样的,也并非所有应用程序都需要复杂的体系结构来封装系统的业务逻辑。

  业务层在任何企业应用程序中是最重要的层次。作为开发者,重要的是要理解所有领域逻辑模式的优缺点,这样才能使用最合适的模式。

  Fowler的著作 Patterns of Enterprise Application Architecture 中首先提出的4种模式:

    Transaction Script(事务脚本)、Active Record(活动记录)、Anemic Model(贫血模型)及Domain Model(领域模型)。

  理解领域驱动设计(domain-driven design,DDD)以及如何运用它让自己专注于业务逻辑而不是基础设施关注点.学习DDD,这种设计方法有助于更有效地理解正在建模的业务领域并确保牢记业务需求。

Transaction Script

  Transaction Script模式遵循的是过程式开发风格而不是面向对象方法。通常,为每个业务事务创建一个单独的方法,并将它们组合起来放入某种静态管理程序或服务类。每个过程都包含了完成业务事务所需的所有业务逻辑,包括工作流、业务规则和数据库持久化验证检查。
  Transaction Script模式的一个优势是它易于理解,很快就可以让团队新成员上手而不需要具有该模式的预备知识。当出现新需求时,很容易向该类中添加更多方法,而不用担心影响或破坏现有功能。因此该模式非常适于那些逻辑中只包含很少或不包含可能增长的功能集合的小型应用程序,以及有不熟悉面向对象编程概念的初级开发者的团队。
  当应用程序变大而且业务逻辑变得更复杂时,Transaction Script模式的问题就会暴露出来。扩展应用程序时,方法的数目也会变多,从而形成一个充斥着功能交叠的细粒度方法的无用API。尽管可以使用子方法来避免代码重复,如验证和业务规则,但在工作流中的复制不可避免,而且当应用程序规模变大时,代码基很快会变得笨重且不可维护。
  应用程序较小,而且业务逻辑简单,不需要采用完全的面向对象方法,Transaction Script模式可能比较合适。如果应用程序规模会变大,那就可能需要重新考虑业务逻辑结构并寻求更具伸缩性的模式,如Active Record模式,

Active Record

  Active Record模式是一种流行的模式,尤其在底层数据库模型匹配业务模型时它特别有效。通常,数据库中的每张表都对应一个业务对象。业务对象表示表中的一行,并且包含数据、行为以及持久化该对象的工具,此外还有添加新实例和查找对象集合所需的方法。

 ASP.NET 设计模式 ( 组织业务逻辑层) 读书摘记4

  图展示一个博客应用程序中的Post和Comment对象如何与它们对应的数据库表关联起来。该图还说明Post中含有一个Comment对象集合。

  在Active Record模式中,每个业务对象均负责自己的持久化和相关的业务逻辑。

  Active Record模式非常适用于在数据模型和业务模型之间具有一对一映射关系的简单应用程序,如博客或论坛引擎。如果已经有数据库模型或者希望采用“数据优先”的方法来构建应用程序,这也是一个可用的好模式。因为业务对象与数据库中的表具有一对一映射关系,而且均具有相同的创建、读取、更新和删除(CRUD)方法,所以可以使用代码生成工具自动生成业务模型。优秀的代码生成工具还会内置所有的数据库验证逻辑,以确保只有有效的数据才会持久化。
  Active Record模式随着基于数据库的Web应用程序而流行,其中一个典型就是结合了MVC模式和Active Record ORM的Ruby on Rails框架。在.NET领域,构建在NHibernate之上的Castle ActiveRecord项目是最流行的开放源代码Active Record框架之一。

  Castle ActiveRecord(官网)框架是一个基于.NET的ORM框架,它实现了ActiveRecord设计模式。它本身就是基于NHibernate,只是封 装了NHibernate的大部分烦杂细节,对于需要持久化的类,只需继承自ActiveRecordBase类,并对类中的property赋予正确的 Attribute,而无需编写烦杂的mapping file。对于大型系统复杂的数据库逻辑,Castle project建议仍然使用NHibernate作为ORM,而对于数据库数据大批量的迁移、备份等操作,Castle project建议不采用任何ORM机制,而直接使用ADO.NET。

  看到上面的描述,经常使用EF框架的朋友就会感觉很熟悉。.NET中,ADO.NET Entity Framework 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) 解决方案。微软MVC3吸收了Ruby on Rails的特点,使用MVCScaffolding +EF CodeFirs,就会体验Ruby On Rails一样创建代码结构的快感。

  Active Record模式擅长于处理底层数据模型能够很好映射到业务模型的情形,但是当出现不匹配时(有时候称为阻抗失配),该模式将很难应对。这是由于复杂系统的概念业务模型有时与数据模型不同所造成的。如果业务领域非常丰富,有着大量的复杂规则、逻辑和工作流,那么采用Domain Model方法将更加有利。

Domain Model

  在处理复杂业务逻辑时,Domain Model模式非常有用.Domain Model视为表示正在处理的领域的概念层。事物以及事物之间的关系都存在于这个模型中。这些事物包含数据,更重要的是它们还有行为。Domain Model越能密切地表示真实的领域越好,这是因为更容易理解和复制组织中的复杂的业务逻辑、规则和验证过程。
  Domain Model与Active Record模式之间的主要差别在于,Domain Model中存在的业务实体并不知道如何持久化自己,而且没有必要在数据模型和业务模型之间建立一对一的映射关系。

POCO和PI

  术语持久化不知(persistence ignorance,PI)表示普通CLR对象(plain old common runtime object,POCO)业务实体的朴实本质。那么如何将Domain Model的业务对象持久化呢?通常使用Repository模式。采用Domain Model模式时,Repository对象以及数据映射器负责将业务实体及相关实体的对象图映射到数据模型。

 

领域驱动设计 DDD

  DDD(Domain-Driven Design,领域驱动设计)就是一种流行的利用Domain Model模式的设计方法学。
  DDD就是一组帮助人们构建能够反映业务理解并满足业务需求的应用程序的模式和原则。DDD探讨对真实领域建模,首先要全面理解该领域,并将所有的术语、规则和逻辑放入到代码的抽象表示(通常是以领域模型的形式)中。

1. 通用语言

  通用语言(ubiquitous language)的概念是,它应该充当一个公共词汇表,开发者、领域专家及任何其他参与项目的人都使用它来描述该领域。领域专家具有特定领域知识和技能,并且在开发领域模型的过程中与您密切协作,以确保在尝试使用代码表示业务模型之前完全理解该模型。所编写的类、方法和属性名称都应该基于同样的通用语言。这可以让您使用领域专家能够理解的语言来谈论代码。此外,接触该代码的新开发者也应该能够了解该领域。它还让他们能够以相对容易的方式与业务专家谈论复杂业务逻辑的哪怕是最细微的细节。当参与应用程序开发的各方都使用相同的语言时,人们就可以容易地表达问题和解决方法,从而让应用程序更快、更容易地构建。DDD并不是一个框架,但它确实有一组构建块或概念可供整合到解决方案中。

2. 实体

  实体就是以一种抽象的方式包含了真实实体中的数据和行为。任何与实体相关的逻辑都应该包含在它内部。实体属于需要标识符的事物,在其整个生命周期中,该标识符都将保持不变。通常,系统使用某种唯一标识符或自动编号值来为所有无法采用自然方式标识的实体提供标识符。有时候,实体确实具有自然键,如社会保险号或员工号码。并不是领域模型中的所有对象都是唯一的而且需要标识。对于某些对象,数据是最重要的,而不是标识。这些对象就被称为值对象。

3. 值对象

  值对象没有标识,它们之所以重要只是因为它们的特性。值对象通常并不会单独存在,它们通常是(但并非总是)实体的属性。

4. 聚合和聚合根

  大型系统或复杂领域可能有成百上千的实体和值对象,它们有着错综复杂的关系。领域模型需要一种方法来管理这些关联,更重要的是,在逻辑上属于同一分组的实体和值对象需要定义一个接口,让其他实体能够通过该接口与它们交互。如果没有这类构造,那么以后不同分组对象之间的交互将会相互干扰并产生问题。

  聚合概念将逻辑实体和值对象分组。根据DDD的定义,聚合只是"一族出于数据变化目的而被视作一个单元的相关联对象"。聚合根是一个实体,它是这个聚合中唯一能够允许聚合外的对象持有引用的成员。DDD中的聚合概念是为了确保领域模型中的数据完整性。聚合根是一个充当进入聚合的逻辑途径的特殊实体。例如,如果在电子商务商店上下文中获取一张订单,那么可以将其视为聚合根,因为我们只希望通过访问聚合的根来编辑订单项或应用一张凭证。这使得复杂对象图能够保持一致,而且能够遵守业务规则。因此,与其让一个订单对象通过简单的List属性来暴露它发出的凭证集合,不如让它拥有一些带有复杂规则的方法,能够允许将凭证应用到它并且把凭证列表表现为一个用于显示的只读集合。

5. 领域服务

  那些没有真正位于单个实体中或者需要访问资源库的方法都被放到领域服务中。领域服务层还可以包含自己的领域逻辑,而且可以作为领域模型的重要组成部分,像实体和值对象一样。

6. 应用程序服务

  应用程序服务是位于领域模型之上的一个瘦层,负责协调应用程序活动。它并不包含业务逻辑,也没有保存任何实体的状态。但它可以存放业务工作流事务的状态。

7. 资源库

  Repository模式充当业务实体的内存集合或仓库,它完全将底层的数据基础设施抽象出来。该模式可用来将领域模型与任何基础设施关注点分离,使其成为POCO和PI。

8. 分层

  在DDD中,分层是一种重要的概念,因为它有助于加强关注点的分离。下图所示为构成DDD的各个层次和概念的图形化表示。

 

ASP.NET 设计模式 ( 组织业务逻辑层) 读书摘记4

推荐阅读:

Domain-Driven Design: Tackling Complexity in the Heart of Software(Addison-Wesley,2003),Eric Evans著
Applying Domain-Driven Design and Patterns: Using .Net With Examples in C# and .NET (Addison-Wesley,2006),Jimmy Nilsson著

 


示例讲解

下面将通过创建一个解决方案来为银行领域建模,这涉及账号的创建以及账号之间的现金转账。来演示Domain Model模式。(示例源码下载

创建一个的新的解决方案,并向其中添加下面的项目:

ASP.NET 设计模式 ( 组织业务逻辑层) 读书摘记4

ASPPatterns.Chap4.DomainModel.UI.Web为Web应用程序,其余皆为类库项目。在Repository项目上右击,并添加对Model项目的项目引用。在AppService项目上右击,添加对Model和Repository项目的项目引用。最后,在Web项目上右击,添加对AppService项目的项目引用。

下面为项目的解决方案结构和每个对象的责任。

ASP.NET 设计模式 ( 组织业务逻辑层) 读书摘记4

  ASPPatterns.Chap4.DomainModel.Model 
  Domain Model项目将包含应用程序内的所有业务逻辑。领域对象将存放在此处,并与其他对象建立关系,从而表示应用程序正在构建的银行领域。该项目还将以接口的形式为领域对象持久化和检索定义契约,将采用Repository模式来实现所有的持久化管理需求。Model项目不会引用其他任何项目,从而确保:让它与任何基础设施关注点保持隔离,并坚定地只关注业务领域。

  ASPPatterns.Chap4.DomainModel.Repository 
  Repository项目将包含Model项目中定义的资源库接口的具体实现。Repository引用了Model项目,从而从数据库提取并持久化领域对象。Repository项目只关注领域对象持久化和检索的责任。

  ASPPatterns.Chap4.DomainModel.AppService 
  AppService项目将充当应用程序的网关(API,如果愿意的话)。表示层将通过消息(简单的数据传输对象)与AppService通信。AppService层还将定义视图模型,这些是领域模型的展开视图,只用于数据显示。

  ASPPatterns.Chap4.DomainModel.UI.Web 
  UI.Web项目负责应用程序的表示和用户体验需求。这个项目只与AppService交互,并接收专门为用户体验视图创建的强类型视图模型。

 使用下面的数据库脚本创建数据库及相关表:

ASP.NET 设计模式 ( 组织业务逻辑层) 读书摘记4   ASP.NET 设计模式 ( 组织业务逻辑层) 读书摘记4

USE [BankAccout]
GO
/****** Object:  Table [dbo].[Transactions]    Script Date: 05/25/2013 13:52:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Transactions](
    [BankAccountId] [uniqueidentifier] NOT NULL,
    [Deposit] [money] NOT NULL,
    [Withdrawal] [money] NOT NULL,
    [Reference] [nvarchar](50) NOT NULL
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[BankAccounts]    Script Date: 05/25/2013 13:52:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[BankAccounts](
    [BankAccountId] [uniqueidentifier] NOT NULL,
    [Balance] [money] NOT NULL,
    [CustomerRef] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_BankAccouts] PRIMARY KEY CLUSTERED 
(
    [BankAccountId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
DB:BankAccout

相关文章: