企业应用架构模式 Martin Fowler著 - 读书笔记
响应性不同于请求处理,它是系统响应请求的速度有多快。这个指标在许多系统里非常重要,因为对于一些系统而言,如果其响应太慢,用户将难以忍受——尽管其响应时间可能不慢。如果能够在处理真正完成之前就给用户一些信息表明系统已经接到请求,则响应性就会好一些。例如,进展条。
事务脚本是这样一个过程:从表示层获得输入、进行校验和计算处理、将数据存储到数据库中以及调用其他系统的操作等。基本的组织方式是让每个过程对应用户可能做的一个动作。所以,我们可以将这一模型想像成一个动作或业务事务的脚本。每一个动作是由一个过程来驱动。
在领域模型中,不再是由一个过程来控制用户某一动作的逻辑,而是由每一个对象都承担一部分相关逻辑。
这三种方法有利有弊。单表继承浪费空间,但避免了连接操作;具体表继承在超类改变时不得不改变所有表,但它也避免了连接操作,允许从一个表取得一个对象;类表继承需要多个连接操作来载入一个对象,这样通常损失了性能,但它是类和表之间最简单的关系。
如果机器处在屏幕流的控制下,那么你就需要应用控制器;如果这台机器是在用户的控制下,你就不要应用控制器。
在视图方面,可以考虑三种模式:转换视图、模板视图和两步视图。
对任何并发程序的本质来说,仅仅考虑正确性是不够的,还必须考虑灵活性(即有多少并发活动可以同时进行)。人们常常需要牺牲一些正确性以获取更多的灵活性,这取决于失败的严重性和可能性以及人们对并发处理数据的需求。
并发问题有两个非常重要的解决方案:一个是隔离(isolation),一个是不变性(immutability)。
在乐观锁和悲观锁之间进行选择的标准是:冲突的频率与严重性。如果冲突很少,或者冲突的后果不会很严重,那么通常情况下应该选择乐观锁,因为它能得到更好的并发性,而且更容易实现。但是,如果冲突的结果对于用户来说是痛苦的,那么就需要使用悲观锁策略。
超时控制和检测机制处理已经出现的死锁,而其他的方法则尽力防止死锁的发生。
跨越多个请求的事务称为长事务。
在请求开始时启动事务,在请求结束时提交事务,这是请求事务。
另一种方法是尽可能晚打开事务。使用延迟事务时,应在事务外完成读取数据的操作,只在修改数据的时候启动事务。然而,这可能会导致不一致读问题。
当可以并发执行并且结果与以某种顺序依次执行的结果相同时,事务就是可串行化的(serializable)。
如果系统中有很多用户,应该考虑使用集群来提高吞吐率。会话迁移(session migration) 允许一次会话从一台服务器转移到另一台服务器,从而可以由一台服务器处理一个请求,其他服务器处理其他的请求。与其相反的方式是服务器亲和(server affinity),它要求某次特定会话的所有请求只能由同一台服务器处理。
分布对象设计第一定律:不要分布使用对象。
这种情况下,怎样有效利用多处理器资源呢?大多数情况下是使用集群系统。在每一个处理器上都部署所有的对象并在其他几个节点上复制它们。这样一来,每个处理器上的对象只需用到本地调用,从而运行更快。还可以使用细粒度接口来设计对象,从而得到更简单的编程模型和更好的可维护性。
事务脚本(Transaction Script): 使用过程来组织业务逻辑,每个过程处理来自表现层的单个请求。
绝不要让事务脚本调用任何表现层逻辑;这样会使我们容易修改代码和测试事务脚本。
领域模型(Domain Model):合并了行为和数据的领域的对象模型。
作为组织企业级应用程序逻辑层的模式,服务层综合了脚本和领域对象类,在二者的长处之间找到了一个平衡点。有一些可选的方案可以用来是实现服务层,例如领域外观或操作脚本、POJO或会话beans,或者是二者的组合。最重要的是,不管采用哪种实现方式,服务层这一模式都为封装应用的业务逻辑、实现和支持不同的客户以一致的方式调用这些逻辑奠定了基础。
活动记录:一个对象,它包装数据库表或视图中某一行,封装数据库访问,并在这些数据上增加了领域逻辑。
活动记录与行数据入口十分类似。二者的主要差别是行数据入口仅有数据库访问而活动记录既有数据源逻辑又有领域逻辑。
离线并发(offline concurrency),其含义是多数据库事务中数据操作的并发控制。
如果每个事务都按相同的顺序处理需要进行编辑的表,就会大大降低死锁的风险。工作单元是保存固定写表顺序的理想地方,这样总可以用同样的顺序来访问表。
工作单元解决的基本问题是记录操作过的各种对象,以便知道为了使内存中的数据与数据库同步需要考虑哪些对象。
工作单元的强大功能是把所有的信息保存在一个地方。一旦使用了工作单元,就不必为记录所做的修改做很多操作。而且,工作单元还可以作为更复杂情况下的固定处理平台,例如处理一个利用乐观离线锁和悲观离线锁跨越几个系统事务的业务事务。
一般来说,用一个标识映射来管理所有修改了的数据库读出对象。标识映射的另一个用途是作为数据库读取操作的高速缓存。
主要有四种实现延迟加载的方法:延迟初始化、虚代理、值保持器和重影。
延迟加载很适合于面向方面的程序设计。
经常会遇到这样的情况:不同的用例和不同的延迟加载策略配合得最好。
方法对象(method object):它独立地把一个复杂的方法转变成一个对象。它最大的好处在于允许往域里填值而不是通过参数传递值。
MVC的价值主要在于两个分离。其中视图和模型的分离是最重要的软件设计准则之一。分离视图和控制器稍微次要一些。
对于远程外观而言,最大的错误之一就是把领域逻辑放在其中。任何外观都应该是一层薄薄的皮肤并且只负责很小的一部分责任。
在数据传输对象中的域都是非常简单和原始的,比如像字符串和日期这样的简单类。任何在数据传输对象之间的结构都应该非常简单——是一种分层的结构,而不像你在领域模型中看到的那么复杂的结构。在数据传输对象中保存着这么多简单的属性是为了序列化它们,并且可以是传输的双方更容易理解。
最后需要处理的是那些丢失的会话中锁的超时。如果客户端在事务进行的中途垮掉了,这个丢失的事务就无法完成,从而无法释放它占有的锁。理想情况是让应用服务器的超时机制来处理,而不是应用自己来处理。Web应用服务器提供HTTP会话管理。超时可以通过注册一个监听对象在HTTP会话无效时释放它所占有的锁。
另一个方法是给每个锁加一个时戳,定期清除哪些超过一定时间的锁。
对系统中的每一个实体加锁必然导致频繁的数据竞争,因此要记住悲观离线锁只是作为乐观离线锁的补充,只在真正需要的时候才使用悲观离线锁。
系统事务:发生在应用程序到数据库之间。
业务事务:发生在用户到应用程序之间。
用乐观离线锁让组中的每个对象共享同一个版本号来建立一个控制点,这意味着它们共享同一个版本号,而不是说它们的版本相同。增加这个版本号时,就成为一个锁住组中的所有对象的共享锁(shared lock)。
客户会话状态,如果使用HTML,一般有三种方法:URL参数、表单的隐藏域和Cookie。
在选择数据库会话状态和服务器会话状态时,关键取决于在特定应用服务器上使用服务器会话状态便于集群和故障恢复的程度。至少在一般情况下,使用数据库会话状态进行集群和故障恢复要更直接一些。
如果必须通过一个复杂的接口与可能位于系统之外的事物交互,你应当考虑入口模式(Gateway)。使用入口将复杂性封装起来,而不要让复杂性蔓延到整个系统中。使用入口几乎没有什么弊端,同时又可以使系统中入口类之外的代码可读性显著提高。
书已读完,不再更新。