企业应用架构模式 Patterns of Enterprise Application Architecture
Domain Layer
Patterns
1. Transaction Script 事务脚本
Organize Business Logic by procedures that carry out what needs to be done in a transaction.
使用过程组织业务逻辑,每个过程完成对某个请求的处理。
2. Domain Model 领域模型
Build an object model of the domain that incorporate both behavior and data.
领域模型应当使用细粒度的对象,但细粒度对象实现远程接口性能不好,使用细粒度对象时应当使用一个Service Layer实现粗粒度的远程接口(尽量避免远程接口)。
3. Table Module 表模块
Provide a single object for all the behavior on a table.
一个对象封装一个表中所有数据的全部或主要业务逻辑。
事务脚本和表模块都可能会使用到一些类,这些类区别于领域模型,它的职责比较模糊,很泛,仅仅是对一些功能、业务逻辑在非常粗的层面上的封装打包。
一段时间来一直争论领域模型的两种:贫血模型、充血模型,书中的领域模型指充血模型,对象应当具有数据和行为,而贫血模型在实现业务逻辑上通常是 Transaction Script方式。
可以把表模块看做是一种事务脚本,它主要基于数据库的表来处理业务逻辑。ADO.NET的DataSet、DataTable、 DataRow比较方便使用表模块方式;基于贫血模型的对象实现业务逻辑,基本就是Transaction Script方式了。
以SRM为例,夏新版本是Transaction Script;海信版本的Web Service是Transaction Script。
Object / Relational Mapping Patterns
1. Active Record
An object that wraps a record data structure in an external resource, such as a row in a database table, and adds some domain logic to that object. 活动记录本质上是一种领域模型,类和数据库中数据结构匹配,数据库存取操作和领域逻辑都放在了对象上。可以将活动记录视作一种粗粒度的领域模型。
2. Row Data Gateway 行数据入口
An object that acts as a Gateway to a single record in a data source. 一个类的实例操作一行数据。贫血模型通常会使用行数据入口方式,完成数据库单个记录到贫血模型对象的映射。另外活动记录也能很自然的使用行数据入口方式。
3. Table Data Gateway 表数据入口
An object that acts as a Gateway to a database table. 一个类的实例操作某个表中所有数据,通常使用SQL完成业务逻辑操作。
4. Data Mapper 数据映射器
Transfers data from a domain object to a database. The Data Mapper is a layer of software that acts as a mediator between the in-memory objects and the database.
如果映射器是硬编码的,最好为每个领域类或领域层使用一个数据映射器,如果使用元数据映射,一个映射器就足够。映射器通常使用于领域模型。
5. Metadata Mapping 元数据映射
Hold details of object-relational mapping in metadata. 两种实现方式:code generation和reflective program。象Hibernate元数据用XML表示,不少使用的框架使用attribute作为元数据。
6. Identity Map 标识映射
Ensure each object only gets loaded once by keeping every loaded object in a map. Lookup objects using the map when referring to them.
7. Identity Field 标识域
Save a database id field in an object to maintain identity between an in-memory object and a database row.
Meaningful key vs meaningless key: 标识域可能是一个GUID,可能是一个自增长的整数。最好尽量从业务分析中提取或者构造一些具有一定规则、意义的field作为标识,例如物料编码、销售 采购订单号、客户供应商代码、工厂代码等等。
Simple key vs compound key, table-unique key vs database-unique key.
Getting a new key: get the database to auto-generate, use a GUID, or generate your own. The most common auto-generation method is that of declaring one field to be an auto-generated field, an alternative approach to auto-generation is a database counter. 使用GUID时平台通常都提供生成GUID的API。The last option is rolling your own.例如使用SQL的max函数找到下一个序列。A better approach is to use a separate key table,在建表中保存生成规则设置以及下一个有效序列。
8. Serialized LOB 序列化LOB
Save a graph of objects as by serializing them into a single large object (LOB) and store the LOB in a database field.
例如公司的组织结构是一个树,不考虑关联等其它因素,可以将这个树序列化后作为一个字段保存。数据库中可以用BLOB(二进制)或CLOB(文本)字段。
9. Foreign Key Mapping 外建映射
10. Dependent Mapping 依赖映射
Have one class perform the mapping for another. 依赖者有且只有一个所有者,又不被其它任何对象引用,这样当加载所有者时就可以同时加载所有的依赖者。
11. Association Table Mapping 关联表映射
Save an association as a table with foreign keys to the tables that are linked by the association.
12. Embedded Value 嵌入值(Aggregate Mapping, Composer)
Map an object into several fields of another object's table. 例如日期范围,可能简历一个DateRange对象,而数据库中保存时可能用两个字段:StartDate、EndDate。
13. Lasy Load 延迟加载
An object that doesn't contain all of the data you need, but knows how to get it.
Lasy initialization(延迟初始化)、Virtual proxy(虚代理)、Value holder(值保持器)、Ghost(重影)。普通的field没有必要使用延迟加载,对一些内聚、关联的单个、集合对象使用延迟加载会比较合适。
14. Concrete Table Inheritance 具体表继承
Represent an inheritance hierarchy of classes with one table per concrete class in the hierarchy. 也可以叫做叶表继承,只有继承关系中的叶子节点类拥有对应的表。
15. Single Table Inheritance 单表继承
继承结构中的所有类都用一个表保存。
16. Class Table Inheritance 类表继承
Represent an inheritance hierarchy of classes with one table for each class.
Web Server Patterns
1. Model View Controller
2. Page Controller 页面控制器
An object that handles a request for a specific page or action on a web site.
As a result Page Controller has one input controller for each logical page of the web site. That controller may be the page itself, as it often is in server page environments, or it may be a separate object that corresponds to that page.
通常模型和表现层的分离比较明显,而视图和控制器的分离比较模糊。ASP.NET的Code Behind方式,每个页面的控制器是一个单独的对象;ASP、ASP.NET的Code Inline方式,控制器就是页面本身。
3. Front Controller 前端控制器
A Front Controller handles all calls for a web site. It's usually structured in two parts: a web handler and a hierarchy of commands. The web server software directs the http request to the handler. The handler pulls just enough information from the URL and request to decide what kind of action to initiate. The handler then delegates the request to a command, which usually isn't a web handler, to carry out the action.
Maverick.Net算是一种前端控制器。其实本质上看Code Behind跟Maverick.Net没什么区别,只是在意识形态上。Code Behind视图和控制器的结合紧密,很容易把它们视为一体,在解决问题时思路跟页面绑定在一起,容易导致将业务逻辑的理解和实现重心转移到页面上,淡化 Model的概念,淡化MVC之间的概念;Maverick.Net对MVC的分离比较清晰,有利于Domain Model的运用。
4. Template View 模板视图
Render information into HTML by embedding markers into an HTML page. ASP、ASP.NET等服务器页面都是模板视图。
5. Transform View 转换视图
A view that process domain data element by element and transforms it into HTML. 运用广泛的是XSLT转换,这要求将Model序列化成XML。
6. Two Step View 两步视图
Turn domain data into HTML in two steps: first by forming some kind of logical page, then rendering the logical page into HTML.
第一步将Model的数据转换成不带任何详细格式信息的逻辑表示,第二步再把逻辑表示转换成最终格式。如果页面采用目前比较热的DIV + CSS布局,一定程度可以减少采用两步视图的理由。
7. Application Controller
A centralized point for handling screen navigation and flow of an application.
类似Action Flow Engine,在业务逻辑非常复杂,而又要求相当的灵活性,这样某些Command的处理可能会相当复杂,使用一个Application Controller有助于逻辑的拆解和重新组织。
Base Patterns
1. Gateway
An object that encapsulates access to an external system or resource.
One of the key uses for a Gateway is to make a good point to apply a Service Stub. Keep a Gateway as simple as you can. Focus on the essential roles of adapting the external service and providing a good point for stubbing.
2. Record Set
An in-memory representation of tabular data. 例如ADO.NET的DataSet。
3. Value Object
A small simple object, like a money or date range, whose equality is not based on identity.
4. Money
Represents a monetary value. 是值对象的一种。
5. Plugin
Link classes during configuration rather than compilation. 插件式运用比较成功的有Eclipse、SharpDevelop。如果采用插件式,详细的掌握理解OSGI规范是必要的,象Eclipse都是OSGI 规范的成功应用。
6. Service Stub
A stand-in implementation of an external service.
项目如果需要依赖第三方系统的服务,开发过程会有很多不可预测的问题,测试也麻烦。使用服务桩就是解决这个问题。The first step is to define access to this service with an interface. Once the interface is defined and an implementation or two has been coded it's simply a matter of using the Plugin pattern to provide the proper implementations of services to your application at runtime.
7. Layer Supertype
A type that acts as the supertype for all types in its layer.
这种模式实际中也经常使用,例如所有的Domain Object可能继承自某个超类型,Service Layer中的类也可能是继承自某个超类型。
8. Special Case
A subclass which provides special behavior for particular cases.
处理一些特殊情况的类,例如Nullable类等。
9. Registry
A well known object that other objects can use to find common objects and services. 注册表模式使用起来比较方便,有的情况下也能很巧妙的解决一些问题,例如Hibernate里面的Dialect。
Distribution Patterns
1. Remote Facade 远程外观
Provides a coarse grained facade on fine grained objects to improve efficiency over a network. As well as providing a coarse-grained interface, several other responsibilities naturally go to the Remote Facade. The methods of a Remote Facade are a natural point to apply security. The Remote Facade methods also are a natural point to apply transactional control. 远程外观中不应当包含任何领域逻辑,如果需要工作流或协作的逻辑,应当放入细粒度对象或者一个事务脚本中,整个应用程序要能够在不使用远程外观的情况下工 作。
远程外观区别于服务层,服务层不需要是远端的不需要只有粗粒度方法,服务层不需要使用数据传输对象,它返回给client一个真正的领域对象即可。
Martin经验:对于中等应用,使用一个远程外观足够,大的系统使用5、6个远程外观。
2. Data Transfer Object 数据传输对象
An object which acts purely as a carrier of data in order to reduce method calls. 通常大家选择贫血模型的一个理由是,领域对象能够很方便的作为DTO使用。
Concurrency Patterns
1. Unit of Work 工作单元
Maintains a list of objects that are affected by a business transaction and coordinates the writing out of changes and resolution of concurrency problems. 类似于缓存,例如一个事物,需要执行多个更新操作,作为工作单元的类先将多个更新缓存起来,在事物提交的时候一起提交多个UPDATE SQL语句,以降低并发冲突。
所以Hibernate采用了Session机制,Session的一个作用就是工作单元。
1. Transaction Script 事务脚本
Organize Business Logic by procedures that carry out what needs to be done in a transaction.
使用过程组织业务逻辑,每个过程完成对某个请求的处理。
2. Domain Model 领域模型
Build an object model of the domain that incorporate both behavior and data.
领域模型应当使用细粒度的对象,但细粒度对象实现远程接口性能不好,使用细粒度对象时应当使用一个Service Layer实现粗粒度的远程接口(尽量避免远程接口)。
3. Table Module 表模块
Provide a single object for all the behavior on a table.
一个对象封装一个表中所有数据的全部或主要业务逻辑。
事务脚本和表模块都可能会使用到一些类,这些类区别于领域模型,它的职责比较模糊,很泛,仅仅是对一些功能、业务逻辑在非常粗的层面上的封装打包。
一段时间来一直争论领域模型的两种:贫血模型、充血模型,书中的领域模型指充血模型,对象应当具有数据和行为,而贫血模型在实现业务逻辑上通常是 Transaction Script方式。
可以把表模块看做是一种事务脚本,它主要基于数据库的表来处理业务逻辑。ADO.NET的DataSet、DataTable、 DataRow比较方便使用表模块方式;基于贫血模型的对象实现业务逻辑,基本就是Transaction Script方式了。
以SRM为例,夏新版本是Transaction Script;海信版本的Web Service是Transaction Script。
Object / Relational Mapping Patterns
1. Active Record
An object that wraps a record data structure in an external resource, such as a row in a database table, and adds some domain logic to that object. 活动记录本质上是一种领域模型,类和数据库中数据结构匹配,数据库存取操作和领域逻辑都放在了对象上。可以将活动记录视作一种粗粒度的领域模型。
2. Row Data Gateway 行数据入口
An object that acts as a Gateway to a single record in a data source. 一个类的实例操作一行数据。贫血模型通常会使用行数据入口方式,完成数据库单个记录到贫血模型对象的映射。另外活动记录也能很自然的使用行数据入口方式。
3. Table Data Gateway 表数据入口
An object that acts as a Gateway to a database table. 一个类的实例操作某个表中所有数据,通常使用SQL完成业务逻辑操作。
4. Data Mapper 数据映射器
Transfers data from a domain object to a database. The Data Mapper is a layer of software that acts as a mediator between the in-memory objects and the database.
如果映射器是硬编码的,最好为每个领域类或领域层使用一个数据映射器,如果使用元数据映射,一个映射器就足够。映射器通常使用于领域模型。
5. Metadata Mapping 元数据映射
Hold details of object-relational mapping in metadata. 两种实现方式:code generation和reflective program。象Hibernate元数据用XML表示,不少使用的框架使用attribute作为元数据。
6. Identity Map 标识映射
Ensure each object only gets loaded once by keeping every loaded object in a map. Lookup objects using the map when referring to them.
7. Identity Field 标识域
Save a database id field in an object to maintain identity between an in-memory object and a database row.
Meaningful key vs meaningless key: 标识域可能是一个GUID,可能是一个自增长的整数。最好尽量从业务分析中提取或者构造一些具有一定规则、意义的field作为标识,例如物料编码、销售 采购订单号、客户供应商代码、工厂代码等等。
Simple key vs compound key, table-unique key vs database-unique key.
Getting a new key: get the database to auto-generate, use a GUID, or generate your own. The most common auto-generation method is that of declaring one field to be an auto-generated field, an alternative approach to auto-generation is a database counter. 使用GUID时平台通常都提供生成GUID的API。The last option is rolling your own.例如使用SQL的max函数找到下一个序列。A better approach is to use a separate key table,在建表中保存生成规则设置以及下一个有效序列。
8. Serialized LOB 序列化LOB
Save a graph of objects as by serializing them into a single large object (LOB) and store the LOB in a database field.
例如公司的组织结构是一个树,不考虑关联等其它因素,可以将这个树序列化后作为一个字段保存。数据库中可以用BLOB(二进制)或CLOB(文本)字段。
9. Foreign Key Mapping 外建映射
10. Dependent Mapping 依赖映射
Have one class perform the mapping for another. 依赖者有且只有一个所有者,又不被其它任何对象引用,这样当加载所有者时就可以同时加载所有的依赖者。
11. Association Table Mapping 关联表映射
Save an association as a table with foreign keys to the tables that are linked by the association.
12. Embedded Value 嵌入值(Aggregate Mapping, Composer)
Map an object into several fields of another object's table. 例如日期范围,可能简历一个DateRange对象,而数据库中保存时可能用两个字段:StartDate、EndDate。
13. Lasy Load 延迟加载
An object that doesn't contain all of the data you need, but knows how to get it.
Lasy initialization(延迟初始化)、Virtual proxy(虚代理)、Value holder(值保持器)、Ghost(重影)。普通的field没有必要使用延迟加载,对一些内聚、关联的单个、集合对象使用延迟加载会比较合适。
14. Concrete Table Inheritance 具体表继承
Represent an inheritance hierarchy of classes with one table per concrete class in the hierarchy. 也可以叫做叶表继承,只有继承关系中的叶子节点类拥有对应的表。
15. Single Table Inheritance 单表继承
继承结构中的所有类都用一个表保存。
16. Class Table Inheritance 类表继承
Represent an inheritance hierarchy of classes with one table for each class.
Web Server Patterns
1. Model View Controller
2. Page Controller 页面控制器
An object that handles a request for a specific page or action on a web site.
As a result Page Controller has one input controller for each logical page of the web site. That controller may be the page itself, as it often is in server page environments, or it may be a separate object that corresponds to that page.
通常模型和表现层的分离比较明显,而视图和控制器的分离比较模糊。ASP.NET的Code Behind方式,每个页面的控制器是一个单独的对象;ASP、ASP.NET的Code Inline方式,控制器就是页面本身。
3. Front Controller 前端控制器
A Front Controller handles all calls for a web site. It's usually structured in two parts: a web handler and a hierarchy of commands. The web server software directs the http request to the handler. The handler pulls just enough information from the URL and request to decide what kind of action to initiate. The handler then delegates the request to a command, which usually isn't a web handler, to carry out the action.
Maverick.Net算是一种前端控制器。其实本质上看Code Behind跟Maverick.Net没什么区别,只是在意识形态上。Code Behind视图和控制器的结合紧密,很容易把它们视为一体,在解决问题时思路跟页面绑定在一起,容易导致将业务逻辑的理解和实现重心转移到页面上,淡化 Model的概念,淡化MVC之间的概念;Maverick.Net对MVC的分离比较清晰,有利于Domain Model的运用。
4. Template View 模板视图
Render information into HTML by embedding markers into an HTML page. ASP、ASP.NET等服务器页面都是模板视图。
5. Transform View 转换视图
A view that process domain data element by element and transforms it into HTML. 运用广泛的是XSLT转换,这要求将Model序列化成XML。
6. Two Step View 两步视图
Turn domain data into HTML in two steps: first by forming some kind of logical page, then rendering the logical page into HTML.
第一步将Model的数据转换成不带任何详细格式信息的逻辑表示,第二步再把逻辑表示转换成最终格式。如果页面采用目前比较热的DIV + CSS布局,一定程度可以减少采用两步视图的理由。
7. Application Controller
A centralized point for handling screen navigation and flow of an application.
类似Action Flow Engine,在业务逻辑非常复杂,而又要求相当的灵活性,这样某些Command的处理可能会相当复杂,使用一个Application Controller有助于逻辑的拆解和重新组织。
Base Patterns
1. Gateway
An object that encapsulates access to an external system or resource.
One of the key uses for a Gateway is to make a good point to apply a Service Stub. Keep a Gateway as simple as you can. Focus on the essential roles of adapting the external service and providing a good point for stubbing.
2. Record Set
An in-memory representation of tabular data. 例如ADO.NET的DataSet。
3. Value Object
A small simple object, like a money or date range, whose equality is not based on identity.
4. Money
Represents a monetary value. 是值对象的一种。
5. Plugin
Link classes during configuration rather than compilation. 插件式运用比较成功的有Eclipse、SharpDevelop。如果采用插件式,详细的掌握理解OSGI规范是必要的,象Eclipse都是OSGI 规范的成功应用。
6. Service Stub
A stand-in implementation of an external service.
项目如果需要依赖第三方系统的服务,开发过程会有很多不可预测的问题,测试也麻烦。使用服务桩就是解决这个问题。The first step is to define access to this service with an interface. Once the interface is defined and an implementation or two has been coded it's simply a matter of using the Plugin pattern to provide the proper implementations of services to your application at runtime.
7. Layer Supertype
A type that acts as the supertype for all types in its layer.
这种模式实际中也经常使用,例如所有的Domain Object可能继承自某个超类型,Service Layer中的类也可能是继承自某个超类型。
8. Special Case
A subclass which provides special behavior for particular cases.
处理一些特殊情况的类,例如Nullable类等。
9. Registry
A well known object that other objects can use to find common objects and services. 注册表模式使用起来比较方便,有的情况下也能很巧妙的解决一些问题,例如Hibernate里面的Dialect。
Distribution Patterns
1. Remote Facade 远程外观
Provides a coarse grained facade on fine grained objects to improve efficiency over a network. As well as providing a coarse-grained interface, several other responsibilities naturally go to the Remote Facade. The methods of a Remote Facade are a natural point to apply security. The Remote Facade methods also are a natural point to apply transactional control. 远程外观中不应当包含任何领域逻辑,如果需要工作流或协作的逻辑,应当放入细粒度对象或者一个事务脚本中,整个应用程序要能够在不使用远程外观的情况下工 作。
远程外观区别于服务层,服务层不需要是远端的不需要只有粗粒度方法,服务层不需要使用数据传输对象,它返回给client一个真正的领域对象即可。
Martin经验:对于中等应用,使用一个远程外观足够,大的系统使用5、6个远程外观。
2. Data Transfer Object 数据传输对象
An object which acts purely as a carrier of data in order to reduce method calls. 通常大家选择贫血模型的一个理由是,领域对象能够很方便的作为DTO使用。
Concurrency Patterns
1. Unit of Work 工作单元
Maintains a list of objects that are affected by a business transaction and coordinates the writing out of changes and resolution of concurrency problems. 类似于缓存,例如一个事物,需要执行多个更新操作,作为工作单元的类先将多个更新缓存起来,在事物提交的时候一起提交多个UPDATE SQL语句,以降低并发冲突。
所以Hibernate采用了Session机制,Session的一个作用就是工作单元。