LOFTER for ipad —— 让兴趣,更有趣

点击下载 关闭

LOFTER-网易轻博

架构设计

356浏览    18参与
hdw2000
hdw2000
火志溟
经过改造增强后的“角色”拥有记...

经过改造增强后的“角色”拥有记录更复杂关系和状态的能力。于是我也有了新的底层数据处理的思路。处理层级少了一到两层,分词为动态的而非固定字典表。分词调整往更高层级移动与结果缓存放到一起。
虽然新方案看起来效率更高,数据分析和重复传输量也大幅减少,但之前因多种类型的多种库而拆分开的功能区块也合并在了一起,势必造成单点的负荷严重增加。
目前唯一想到的可行解决方案就是废除现有的“角色控制器”,直接开发新的“分布式角色控制机”。
此“分布式角色控制机”显然必须支持灵活划分的主-从模式。为了数据安全,从-从之间可以做镜像,主-主间也可做镜像。也就是做到读取时的负载均衡。为了减少网络传输,它应该保证尽量不传递完整...

经过改造增强后的“角色”拥有记录更复杂关系和状态的能力。于是我也有了新的底层数据处理的思路。处理层级少了一到两层,分词为动态的而非固定字典表。分词调整往更高层级移动与结果缓存放到一起。
虽然新方案看起来效率更高,数据分析和重复传输量也大幅减少,但之前因多种类型的多种库而拆分开的功能区块也合并在了一起,势必造成单点的负荷严重增加。
目前唯一想到的可行解决方案就是废除现有的“角色控制器”,直接开发新的“分布式角色控制机”。
此“分布式角色控制机”显然必须支持灵活划分的主-从模式。为了数据安全,从-从之间可以做镜像,主-主间也可做镜像。也就是做到读取时的负载均衡。为了减少网络传输,它应该保证尽量不传递完整“角色”,而仅传递具体的值。

IT168文库

58同城数据库架构设计思路

 

58同城数据库架构设计思路


58同城,一个被贴上“神奇”标签的网站,海量信息背后到底支撑的数据库是什么?当并发访问量相当密集的时候,这些数据库系统都会采取缓存策略?在2015中国数据库技术大会上,58同城高级架构师沈剑跟大家分享了58同城数据库架构设计思路。


详细解读 和小伙伴们一起来吐槽

58同城数据库架构设计思路 - 332566262 - 拟声的主扬

 

58同城数据库架构设计思路


58同城,一个被贴上“神奇”标签的网站,海量信息背后到底支撑的数据库是什么?当并发访问量相当密集的时候,这些数据库系统都会采取缓存策略?在2015中国数据库技术大会上,58同城高级架构师沈剑跟大家分享了58同城数据库架构设计思路。


详细解读 和小伙伴们一起来吐槽

编程人生

架构设计篇

软件架构设计:程序员向架构师转型必备(第二版)


本书围绕“软件架构设计”主题,从“程序员”成长的视角,深入浅出地讲述了架构师的修炼之道。从“基础篇”、到“设计过程篇”、到“模块划分专题”,本书覆盖了架构设计的关键技能项,并且对于架构设计过程中可能出现的各种问题给与了解答。

本书可作为架构师速成教程,理论部分稍显单薄,但可执行性很强,对于有志于成为架构师的程序员们具有非常有效的指导意义,对于已经成为架构师的同行们系统化规范架构设计也是一本很好的参考书。


软件系统架构:使用视点和视角与利益相关者合作(原书第2版)...


软件架构设计:程序员向架构师转型必备(第二版)

架构设计篇 - dinstone - dinstone的IT农场

 

本书围绕“软件架构设计”主题,从“程序员”成长的视角,深入浅出地讲述了架构师的修炼之道。从“基础篇”、到“设计过程篇”、到“模块划分专题”,本书覆盖了架构设计的关键技能项,并且对于架构设计过程中可能出现的各种问题给与了解答。

本书可作为架构师速成教程,理论部分稍显单薄,但可执行性很强,对于有志于成为架构师的程序员们具有非常有效的指导意义,对于已经成为架构师的同行们系统化规范架构设计也是一本很好的参考书。

 

软件系统架构:使用视点和视角与利益相关者合作(原书第2版)

架构设计篇 - dinstone - dinstone的IT农场

 

       本书是软件系统架构领域的开创性著作,是两位拥有数十年软件行业工作经验的架构师工作经验的结晶,围绕利益相关者、视点和视角三大主题,创新性地提出了如何用架构视点和架构视图的方法来定义软件架构,如何用架构视角的方法来确保软件质量,以及如何用架构视点和架构视角的方法与利益相关者合作,具有里程碑意义。《软件系统架构:使用视点和视角与利益相关者合作(原书第2版)》还展示了一种实用的、经过验证的框架,你可以应用它来处理架构定义过程,并应对创建软件架构工作所带来的挑战。

       强烈推荐本书,本书是每个架构师的必备核弹。

 

企业应用架构模式

架构设计篇 - dinstone - dinstone的IT农场

 

       本书作者Martin Fowler是当今面向对象软件开发的权威,他在一组专家级合作者的帮助下,将40多种经常出现的解决方案转化成模式,最终写成这本能够应用于任何一种企业应用平台的、关于解决方案的、不可或缺的手册。本书获得了2003年度美国软件开发杂志图书类的生产效率奖和读者选择奖。本书分为两大部分。第一部分是关于如何开发企业应用的简单介绍。第二部分是本书的主体,是关于模式的详细参考手册,每个模式都给出使用方法和实现信息,并配以详细的Java代码或C#代码示例。此外,整本书中还用了大量UML图来进一步阐明有关概念。

 

软件框架设计的艺术

架构设计篇 - dinstone - dinstone的IT农场

 

       本书从理论、实战及日常应用三个方面详细讲解了软件开发和框架设计的艺术,着眼于保证软件设计能够应对时刻变化的需求和技术。书中将理论与实践有机地结合在一起,对框架设计领域进行了深层次的阐释。

   作为NetBeans框架的主架构师。作者在书中总结了自己多年的开发经验,与大家分享了API设计的技术细节、走过的弯路和教训。对于广大软件开发人员来说,这些都是不可多得的宝贵财富。本书就像一盏黑暗中燃起的明灯,为你照亮崎岖的开发之路,指明前进的方向。

 

IT168文库

玩转自动化运维_邵海杨

 

玩转自动化运维_邵海杨


本议题主要谈论:1、探讨云时代下的架构设计及运维挑战;2、运维人员的团队协作和架构师成长修炼;3、面对来自devops和云的挑战,运维工程师如何成长;4、怎么实现自动化运维,化被动为主动。


详细解读 和小伙伴们一起来吐槽


同步自网易博客 (查看原文)

玩转自动化运维_邵海杨 - 332566262 - 拟声的主扬

 

玩转自动化运维_邵海杨


本议题主要谈论:1、探讨云时代下的架构设计及运维挑战;2、运维人员的团队协作和架构师成长修炼;3、面对来自devops和云的挑战,运维工程师如何成长;4、怎么实现自动化运维,化被动为主动。


详细解读 和小伙伴们一起来吐槽


同步自网易博客 (查看原文)

dryr

系统中的缓存设计

    系统架构设计中有一个很重要的要素,就是缓存设计。纵观目前流行的框架和系统,可以说基本都有缓存设计,通过缓存来解决性能、一致性等问题已经成为基本设计理念了。一个较为庞大的系统中如果没有任何的缓存设计,那是一件让人吃惊的事情。
    缓存设计的本质是用空间换时间,如果空间足够,将所有数据缓存在内存中都是可以考虑的,比如内存数据库。但在实际情况中,往往是需要权衡取舍的,需要分析出最需要缓存的数据(比如使用频率较高的数据,查询速度较慢的数据,对性能影响较大的数据等),虽然有难度,但这也正是设计魅力所在,所谓不取舍就无设计啊。
 ...

    系统架构设计中有一个很重要的要素,就是缓存设计。纵观目前流行的框架和系统,可以说基本都有缓存设计,通过缓存来解决性能、一致性等问题已经成为基本设计理念了。一个较为庞大的系统中如果没有任何的缓存设计,那是一件让人吃惊的事情。
    缓存设计的本质是用空间换时间,如果空间足够,将所有数据缓存在内存中都是可以考虑的,比如内存数据库。但在实际情况中,往往是需要权衡取舍的,需要分析出最需要缓存的数据(比如使用频率较高的数据,查询速度较慢的数据,对性能影响较大的数据等),虽然有难度,但这也正是设计魅力所在,所谓不取舍就无设计啊。
    缓存设计的主要目的是提升性能,这个就无需多说了;有时候也可能是为了保持数据一致性和完整性方面的考虑,比如在一个批处理程序中,为了在处理任何一笔数据时都能使用相同的参数,则需要在每次批次开始阶段将参数缓存起来,以确保批次处理过程中参数保持不变。
    缓存设计的基本前提是被缓存数据在一个时间段内是不变的,只有时间段内不变的数据才有缓存的必要(为保持一致性和完整性的缓存除外)。
    在缓存设计时需要对缓存数据进行分类,每一类数据的缓存策略可能是不同的,最简单的缓存过期时间可能是不同的,比如系统参数是每天更新一次的,而业务参数则是每小时更新一次,甚至有些业务数据是10分钟更新一次的,设计时相应的缓存过期时间也是不同的。
    缓存作为暂时存储的数据,必然需要有一个刷新机制,而缓存是否需要刷新则取决于缓存是否已经过期,所以缓存的过期时间是一个非常关键的属性。过期时间的设计需要考虑很多方面,比如需考虑数据更新频率(是每天更新一次还是每小时更新一次),也需要考虑各类参数刷新时间的错开,防止雪崩效应(在大规模数据缓存的系统才需要考虑此问题,在一般系统中不会出现),另外还要考虑是否有特殊事件,比如有些参数有一个发布机制,只有参数发布后参数才生效,对于这类数据可考虑在发布等特殊事件时刷新缓存,这是最简单有效的办法。
    缓存是个系统工程,在大数据量和多层结构的系统中,可考虑使用多层缓存技术,即在系统的每个层次结构上都可能缓存数据,上层结构的系统通过缓存的过期时间来判断缓存是否过期,没有过期则不访问下层系统更新数据,过期后再访问下层系统来更新缓存(对类似特殊事件触发刷新的缓存数据,不大适合使用这种方法多层缓存),这样可以大大降低上下层系统间的交互和通讯频率,从而提高性能。
    缓存刷新方式也可以分为主动式和被动式。主动式是在使用缓存时判断缓存是否过期,如果过期则主动刷新缓存,主动式刷新控制比较精准,但只能刷新自身,从管理角度看比较分散;而被动式则通过另一个线程轮询各类缓存,通过判断各类缓存是否过期来确定是否刷新缓存,被动式刷新因为是轮询,故肯定存在时间上的偏差,理论上存在使用到过期缓存数据的情况,但此方式可以将所有缓存集中统一管理,这是它的优点。
    缓存设计中,还有两个必须考虑的问题,就是缓存穿透和雪崩效应。针对缓存穿透问题,可以通过罗列所有缓存数据类的方式来解决,比如系统中缓存了两类参数,一类是产品参数,一类是销售人员参数,那么当客户端来刷新除这两类参数外的其他参数比如门店参数时,系统可以判断出门店参数并不在缓存的类别列表中,故无需去刷新缓存,直接返回空或错误即可。雪崩效应前面也说了,只有较大数据规模缓存的系统才有可能出现此问题,一般系统不会出现,针对此问题,只有在设计各类缓存数据过期时间时考虑时间上岔开,不要在同一时间多类参数同时刷新,从而避免导致后台系统或数据库的崩溃。
    说了很多,但最重要的是动手做,在系统设计时实现缓存,这才是王道。


参考文章:《淘宝应对"双11"的技术架构分析》

dryr

系统架构分析设计方法

    最近几天一直都在想着系统架构的事情,想着如何才能更好的描绘出一个业务以及支撑系统的情况,过程中也不乏向一些前辈请教,慢慢的形成了一些自己的想法或做法。
    今天下班回家的公交车上,脑子中还在不停的想着系统架构的事情,忽然有了一个念头,将之前涉及的一些方法串联了起来。下面描述一下这个从用例和业务流程出发分析得出系统架构的构想。
    首先,我们要将业务流程分层描述,业务流程不需要指明执行角色(在较高层次业务流程中也无法确定执行角色),然后逐层细化,可以将业务流程细化到可以根据业务流程编写出伪代码的...

    最近几天一直都在想着系统架构的事情,想着如何才能更好的描绘出一个业务以及支撑系统的情况,过程中也不乏向一些前辈请教,慢慢的形成了一些自己的想法或做法。
    今天下班回家的公交车上,脑子中还在不停的想着系统架构的事情,忽然有了一个念头,将之前涉及的一些方法串联了起来。下面描述一下这个从用例和业务流程出发分析得出系统架构的构想。
    首先,我们要将业务流程分层描述,业务流程不需要指明执行角色(在较高层次业务流程中也无法确定执行角色),然后逐层细化,可以将业务流程细化到可以根据业务流程编写出伪代码的程度。在各层的业务流程图中,我们需要识别出能表述单一功能的流程点(这个可能需要经验来判断),这些识别出的功能点在之后可以作为验证用例图完整性的依据。
    然后通过对不同业务角色人员操作的不同功能,以及边界外系统与本系统的一些交互功能,以及本系统的处理功能,画出用例图,将此用例图使用之前从业务路程中识别出的功能点进行完整性的验证,以防止用例遗漏。再针对每个用例图,给予一个简单的说明,将用例中的事件流等说清楚,最后形成用例描述文档(也就是功能说明文档),另外还应该产出一个补充文档用来说明非功能性需求。
    从用例推导出类图,就是采用领域模型(domain modeling),就是第一步将用例描述中的名词识别为域对象,然后再根据用例描述识别出对象的属性,最后将用例描述中此对象名词的动词再识别出对象的方法(这一步也可以通过先根据用例画出用例序列图,然后将序列图中的序列动作转化为对象的方法),按照此种方法推导出的是最原始的类,在此基础上,再补充相关的一些类,还需要识别出这些类之间的包含、关联关系,同时将一些类的共同点进行抽象(将抽象出的抽象对象作为一个新对象,被抽象对象从抽象对象泛化),最后就形成了完整的类图。在推导类图时,还需要注意系统的技术架构,因为技术架构确定了类图的依赖层次关系,比如技术架构中分为jsp,action,service,dao,db这几层,那么在类图中也必然有这些层次。
    在类设计时,必须根据技术架构将整体框架设计完成,划分好类层次结构;然后先对底层模块进行设计,比如先设计权限模块、通讯模块、日志记录模块等,然后再对业务模块进行设计。在设计业务模块时可以通过局部设计技术,对某个或某几个功能点进行局部设计,多使用设计模式。
    在类设计完成后,最好能进行业务驱动的功能推导(可以借助完善用例的序列图来推导),以验证类图设计的完整性和是否存在矛盾的地方。

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------

向公司的一位大神请教了一下关于系统架构方面的思想和方法,他的想法值得借鉴:
我们做一个系统,需要解决两件事情:做什么,和怎么做。
那我们在做系统架构时,能有什么资料,我们手中唯一的资料就是需求说明书,这就是系统架构的输入资料。我们不要妄想这需求说明书是写的非常清晰非常详细的,大部分的情况下拿到手的都是写的很烂很糟糕的,可能只是很笼统的说明了要做什么,更清晰的需求需要我们自己去挖掘。
做什么是对功能职责的定义和描述,做什么是从业务出发的,即是业务要求系统做什么,在这一步骤中,我们只从业务角度谈论功能职责定义,不考虑功能具体的实现(具体实现之后步骤中会考虑,这里如果考虑具体实现容易陷入具体实现中而不能全面整体的发现和定义功能职责)。
一般采用从上到下,从子系统到模块再到功能点的分解步骤来进行。具体做法如下:
1、项目开始阶段,我们根据拿到的需求说明书以及我们所了解的需求情况,确定系统包含的子系统(在这个阶段按照我们所了解的需求程度也只能给出子系统级别粒度的应用架构),定义各个子系统的功能职责(子系统包含哪些功能模块,各子系统职责划分的原则),以及各子系统之间和与外部系统之间的交互(存在哪些数据的交互,存在哪些功能调用)。画出应用架构图,描绘各子系统之间及与外部系统间的关系。
2、应该说一般情况下,我们在划定子系统的时候,大致已经可以确定各子系统的模块。但识别出子系统的模块还不够,还需要对模块进行分析识别出公共模块以及模块间的交互关系,这步需要花费大量的分析和对需求的深入了解。针对每个子系统,分解为各个模块,定义各个模块的功能职责(模块包含哪些功能点,各模块职责划分原则),以及各模块之间的交互(数据交互和功能调用)。此步骤中需要识别出公共模块,以及公共模块给其他模块提供的服务(这是模块间交互关系的一部分)。
3、在完成模块划分后,接下去就是针对每个模块,分解为每个功能点,定义每个功能点的需求描述,以及对外提供的服务接口(或者将服务接口也看成是功能点,归并到功能点中),定义每个服务接口的需求描述。一般我们都在说推荐使用用例的方式来描述需求,如果这里使用了用例方式来描述需求,那这个功能还是比较好识别的,每个用例就是一个功能点,定义功能的输入和输出(包括数据和接口的输入和输出,在开始阶段数据和接口定义可以适当模糊,随着需求清晰再将数据和接口描述清晰),再在此基础上分析合并,识别出公共功能点。
完成了以上3个步骤,就将整个系统需要做什么分析的比较清楚透彻了,在此基础上,可以再进行业务推导,找出遗漏或重复的功能点(补充或删除),以及进行功能点的抽象等,最终形成系统完整的应用架构,完整功能点视图。

在完成应用架构和功能点分析梳理后,我们需要考虑怎么做的问题了,怎么做是对功能点的实现方式的考虑。实现方面需要考虑的几个方面:1、技术架构(实现的层次结构),2、部署架构(应用的实际部署情况),3、开发设计需遵循的规范和原则(开发规范等),4、系统框架搭建(框架功能的开发搭建)
我们在确定做什么这个方面我们是通过逐步细化的方式来最终得到结果的,同样道理,在系统实现方面也是分阶段的,不可能一开始就想清楚系统是怎么实现的。在项目初期,我们可以根据经验和所了解的需求,子系统级别的应用架构等,初步整理出系统的技术架构和部署架构。需求分析+子系统级别的应用架构+技术架构+部署架构就构成了系统方案,是粗粒度的系统架构。随着项目进展,对需求的了解和分析越来越深,会形成越来越清晰的系统方案,对应用架构进行细化,对技术架构和部署架构进行完善,最终形成完整的系统架构。因为系统的建设不是一个人或几个人的事情,一个大的系统的建设往往需要很多团队很多人的参与,不同团队及人都会有不同的开发习惯和方式,这就需要从项目或系统层面出一个规范和原则,来约束所有参与的团队和人都遵循,统一的规范对项目和系统来讲有很多的好处。另外,系统开发需要分工,但其中有些公共的功能必须要有人统一来开发和维护,这就需要搭建系统框架,在系统框架中统一实现公共功能。

系统架构并不一定是一个人或一个团队一直持续分解细化的,可能系统架构师完成了子系统级别的应用架构、技术架构和部署架构,然后就交给各个团队自己完成各子系统的模块分解和功能点分解,以及局部的设计,但系统架构的思想必须层层传递,不能出现曲解和误解,所以系统架构文档就显得极为的重要,系统架构文档除了必须说明系统架构情况外,一定还要说明清楚系统架构如此设计的思想和意图,以让其他设计者或开发者能明白所以然。

这里有几个问题:
1、数据库这块如何融入应用架构?
2、用例(usercase)这个方法何时使用?如何无缝应用到系统架构设计中?
3、在分解为模块时,会识别出公共模块,按照这种思路,在分解子系统时应该也会识别出公共子系统(比如Portal系统,流程管理子系统,规则管理子系统等),在与业务人员交流应用架构时,这些公共子系统是否需要展示给业务人员,业务人员是否理解这些公共子系统?个人认为如果不将这些公共子系统识别展示给业务人员,将难以将完整的系统全貌展现给业务人员,比如不识别和展示出Portal系统,将会让业务人员难以理解所有子系统都是通过Portal系统统一登录和展现的,比如不识别出流程管理子系统,将会让业务人员难以理解所有的流程都是集中管理的,所以个人还是认为这些公共子系统应该被识别出并且展示在应用架构中,对应用架构配以一定的说明将使业务人员有足够的智慧来理解这些公共子系统。
4、当在项目(尤其是存在旧系统的项目)前期,有部分需求或关系还比较模糊,比如A子系统需要使用B子系统的某类数据,但却并不知道具体是什么数据,而且A子系统是否还需要B子系统其他的数据这些都比较模糊,这种情况下该如何处理?是在应用架构中只体现模糊关系,到之后AB两个子系统关系清晰后再修改,还是先去全面了解AB两个子系统的关系后,在应用架构中给出一个相对清晰的关系?另外,处于项目前期时,对子系统的内部模块的划分以及模块之间的关系(数据和服务接口的关系)很难清洗的识别出来,只有一个模糊的大概关系时,模块分解该如何进行?如果只描述模糊关系,那何时修改清晰?

godspeed712

restful 架构

总结了下 restful架构的一些设计约束,设计原则和主要的组成元素!

总结了下 restful架构的一些设计约束,设计原则和主要的组成元素!

RestFul架构

godspeed712

2013 北京qcon总结一

比较早的预定了这次的北京qcon会议,中间出了点变化,差点此行不能启程,正应了那句老话,夜长梦多,后来总算是顺利出发,算是好事多磨吧。
这是第二次去qcon,第一次是去年qcon的杭州站,由阿里集团联合主办,这次的北京是百度,这种的会议少了当地的大型企业很难支撑起来,这个也是为什么qcon这么多年了,但是一直没来上海,因为上海本土缺少一个重量级,在国内说的上话的IT企业,而且最好是互联网企业,但是听说10月份会来上海,不知道是会和谁一起承办,这个公司出了BAT三家之外能进入qcon的视野的话,基本算是在上海技术界拥有比较大的话语权了,盛大在没落,携程是业务驱动型企业,点评还未成气候,1号店在电子...

比较早的预定了这次的北京qcon会议,中间出了点变化,差点此行不能启程,正应了那句老话,夜长梦多,后来总算是顺利出发,算是好事多磨吧。
这是第二次去qcon,第一次是去年qcon的杭州站,由阿里集团联合主办,这次的北京是百度,这种的会议少了当地的大型企业很难支撑起来,这个也是为什么qcon这么多年了,但是一直没来上海,因为上海本土缺少一个重量级,在国内说的上话的IT企业,而且最好是互联网企业,但是听说10月份会来上海,不知道是会和谁一起承办,这个公司出了BAT三家之外能进入qcon的视野的话,基本算是在上海技术界拥有比较大的话语权了,盛大在没落,携程是业务驱动型企业,点评还未成气候,1号店在电子商务领域还只是沧海一粟,豆瓣?可能吧,到时候就知道了!

这三天,选择性的听了一些课程,有好有坏,好的专题也并不是说醍醐灌顶的给你一个很好的解决方案,在大的技术层面,一般的公司大多还只是在炒冷饭,将一些已有的概念结合自身的特殊业务,加上包装,再拿出来卖弄下。目前也就google可以创造性的提出一些东西,类似mapReduce之类的学术论文。

还是回到主线,本次会议接受到好的东西就是那些大规模的公司把你一些想法加以验证是否可行,这个就足够,明确了大致的方向,具体的操刀还是靠你自己把握。打算其中的两个演讲做个详细记录,其他的大多没什么可取之处,会单独开篇的一笔带过。分别是Architecture Design and Architects和分解应用程序以实现可部署性和可扩展性。两个主题演讲在一头和一尾,是否可以认为好的东西是放在最前面和最后面呢?

林仕鼎:Architecture Design and Architects (架构设计与架构师)
讲的还是蛮落地的,但是有一些人觉得他太过虚,个人感觉还是有料,梳理了之前个人对于架构,对于设计中的一些概念,因为没有落地到具体的技术实现上,所以让人感觉比较虚。不同层面的架构设计原则又是不同的,很难很全面的去阐述,而且在最后的实际案例中举了三个不同业务模型下的架构设计,实虚结合,算是好的分享。有时候好的分享并不是具体到某某框架的具体实现才算是。几个核心的观点总结:

代码=数据结构+算法
软件=代码+架构
系统=软件+资源
大规模系统=系统+分布式架构
云=大规模系统+人+数据

最后的云,通过分析数据+人改进原来的架构以更符合我们的业务场景,很重要的一点是做业务架构不要考虑什么重用,只需要考虑可伸缩即可,因为你的业务就是特殊的。

架构中的组合方式:


  • 1.结构:高层次的分解及模块的划分

  • 2.交互方式:划分后要解决的是模块之间的划分方式 同步还是异步。 异步通常是为了解决系统之间的偶尔和提升性能两点上。

  • 3.发布部署模式:嵌入式、独立部署、唯一实例等方式


架构师的要求

  • 1.把握全局:抽象业务,划分未及格模块,几个功能

  • 2.降低复杂度:分解和划分

  • 3.把握过程:敏捷、迭代等方式


常见的模型解决方案

实例--存储

  • 存储结构:File Object Table

  • 数据特点:可变性,大小,数据结构

  • 访问模式:实时读写、批量读 实时读、流式读、范围查询

  • 实时性:非实时性、高实时性、一致性


矛盾:
1.延迟与吞吐
2.随机与顺序
3.规模与实时性

可采用考虑的模型
1.B+树 (实时、随机)
2.Log-based (批量、顺序)

两种方案有各自的有点
对于大的数据可以按照B+树,顺序性能好
对于小的数据可以按照LogTable:随机性能好
正因为这两种处理模型有自己的优缺点,所以在一个系统我们根据系统局部的不同特性选择其中合适的一个,但是有点会违反我们的设计的一致性。

其他的解决方案
1.弱化需求,是否有必要一定是实时性、高一致性
2.局部数据的特性处理,例如热点数据的处理
3.按照实时性、空间进行切割:例如一行数据只有一个字段的数据是热点数据,那么针对该字段做特殊的处理,可以剥离这个字段,也可以冗余这个字段(读取这个数据就是依据最符合业务逻辑的数据)。在解决分布式读写的R+W >N的解决方案中采用的就是类似方案

tips:
实时性:系统大之后就必然是分布式,那么分布式最大的一个问题是CAP中的C如何处理,多节点之间的实时性和一致性。

总结:分解系统的时候,只考虑我们关注的几个点,简化问题,再使用对应的模型,再在外面封装一层,满足自己的个性化需求。
实例--服务模型

因为服务是一个不平衡的流量分布,所以存在高点和低点,我们要处理的就是在高点时保持系统的可用性。所以我们的目标是
1.高吞吐
2.极限压力下的稳定输出

基本的处理模型
1.基本:threadpool + queue
2.复杂:Event-drivn 也就是我们所提出的EDA架构
两种的模型基本思路:并行和异步

稳定性保证:
1.减少资源分配粒度,主动调度 因为我们的请求是不均匀的,那么近
2.流量控制 :
负载反馈、Throttling 极端情况下的服务降低等操作。
延迟截断 分级队列

这个是对于并行和异步的更细粒度描述,以保证系统的可用性。

实例--计算模型

  • 大致分两种

  • 数据密集型

  • MapReduce、Scan-Filter


计算密集型
seti@Home

通讯密集型

  • 机器学习系统

  • 矩阵计算


到目前为止,上面就是我们常见的模型,要做的就是在实际的开发中选择合适的模型并在迭代中进行反复,直到找到适合的模型或是多个模型的组合。

架构师的三板斧
1.看清需求
tradeoff:
1)无法满足所有需求
2)无须同等对待所有需求
发现根本需求
1)分解、抽象、降维
2)定义primitive和组合规则
了解需求随时间的变化
对于不合理的需求say,但是要给出端到端的解决方案

2.选择方法、

  • 测算、模拟、实现

  • 分解、迭代

  • 设计模式、设计原则、OO设计原则等等


3.把握节奏:

  • 规划可达路径

  • 定期产出


更多是从项目的把控上,更多是多方资源之间的协调

  • 编程、重构是内力

  • 学术论文是招式

  • 项目是我们的实际经验


在上面的三个之间循环的迭代,不断的修炼自己。
godspeed712

系统设计 -- 会话状态的处理

对于状态要从两个角度来考虑,一个是对象状态,对象包含了状态(数据)和行为,一个没有状态的对象是不合理的,是种不良设计。
另一种是分布式服务器无状态:指的是在多次请求间不保存对象的状态,但是对象自身是有状态的。服务器端的无状态保证了每次请求不关心是哪个对象来处理,若是需要保存状态,就需要找同一个对象来处理。无状态可以使得我们缓存这些对象,用很少的对象就能处理很多用户。同时在高流量的情况,我们的服务端一般对应的是一个集群,无状态对于集群的横向扩展基本是0代价。无需考虑服务的状态。

但是实际的业务场景中,还是存在一些需要记录会话状态的场景,例如登陆信息,购物车信息,我们需要在某一个地方记录用户是否登陆,...

对于状态要从两个角度来考虑,一个是对象状态,对象包含了状态(数据)和行为,一个没有状态的对象是不合理的,是种不良设计。
另一种是分布式服务器无状态:指的是在多次请求间不保存对象的状态,但是对象自身是有状态的。服务器端的无状态保证了每次请求不关心是哪个对象来处理,若是需要保存状态,就需要找同一个对象来处理。无状态可以使得我们缓存这些对象,用很少的对象就能处理很多用户。同时在高流量的情况,我们的服务端一般对应的是一个集群,无状态对于集群的横向扩展基本是0代价。无需考虑服务的状态。

但是实际的业务场景中,还是存在一些需要记录会话状态的场景,例如登陆信息,购物车信息,我们需要在某一个地方记录用户是否登陆,购物车是否加载了东西。那么就牵扯出如何保存会话状态的方式。通常的有三种:


  1. 客户端会话状态

  2. 服务端会话状态

  3. 数据库端会话状态


客户端会话状态有这么几种实现方式:

  1. 将会话信息编码在URL中

  2. 将会话信息保存中cookie中

  3. 将会话信息通过一个隐藏域提交


优点:
将会话状态保留在客户端,减轻服务端的压力,中间需要做的是一个会话信息的转译,为了安全性等方面考虑
缺点:
若是会话信息非常大的话,那么带宽就会成为灾难。
总结的看客户端会话状态适合于那些会话信息非常少的场景下

服务端会话就是将信息保存在服务端的内存中,同样面临的第一个问题,服务端的内存资源是非常珍贵的,所以对于单台服务器能够承受的会话数量是非常有限的,对于此的做法是添加服务器数量,通过更多的内存来承载更多的会话信息,但是多服务器又会带来一个问题,就是会话复制,一个用户不可能每次请求都落到同一台服务器中,这个会话复制的陈本是不可避免的一点。当然对于这点也是可以考虑服务器亲和的策略,将IP绑定到第一次访问的服务器中,但是这里就引入了一个中间环节,对于扩张和分布和宕机处理很不友好!

最后种是数据库会话状态,就是将会话信息保存进行持久化,也正因为这个持久化过程,加剧了会话的流程,所以在系统响应中不是很好的体验,有一个不太靠谱的数据对比:

  • 从L1缓存中取数据是1ns,

  • 从L2是5ns

  • 从内存中获取数据是10-15ns

  • 从持久化设备就无一个准确的值,基本应该到100+ns


可以看出,数据库会话的最大问题是速率。

开发陈本:
服务器会话状态陈本最低,基本不需要什么特殊的处理,但是考虑的是数据会话迁移及数据安全隔离,若是集中式的服务端会话控制的话,还需设计到网络通信问题。
客户端和数据库的都需要考虑数据的解析和转化 ,同时数据库还涉及到磁盘IO.

选择:
客户端奔溃,服务器挂机,网络突然断掉、数据库挂了等等情况看,其中任何一种都有可能发生,但是不太可能一起发生,所以对于以上手段,应该更复合的去使用,若是较少量的会话信息,通过客户端保存,保存的仅仅是一个会话标示,若是类似购物车等较多的信息,通过集中式的服务器会话保存是一个好的方案,但是对了应对各种异常和做容灾的话,那么必须将部分重要会话信息持久化到数据库了!

以上都只是大体上提到了处理方案,期间还是有很多具体的考量点,例如集中式的服务器会话如何做到关键数据的隔离,保证ACID的事务特性等等!
godspeed712

系统设计 -- 并发控制

并发应该是软件开发中最难处理的问题之一。因为在多进程或是多线程操作一个数据时候,为了保证数据的一致性就必然碰到并发。这个数据可能存在于内存中,也可能存在一个持久化设备中,更复杂的情况是存在于多个数据源中。例如一个订单请求,库存存在于一个数据源中,在库存安全的情况下,去检测用户的金额是否足够,这个是存在于另一个数据源中,在完成两者的检查都满足条件下,去建立一个订单,但是可能在你检查金额的过程中,另一个用户成功下单,并修改了库存信息,而在你检查完金额后,实际库存已经不满足你的下单条件了,但是你还是下单成功了,如此就是并发导致的问题。术语上称之为离线并发。

并发的原因
上面那个例子是造成并发的一个原因:...

并发应该是软件开发中最难处理的问题之一。因为在多进程或是多线程操作一个数据时候,为了保证数据的一致性就必然碰到并发。这个数据可能存在于内存中,也可能存在一个持久化设备中,更复杂的情况是存在于多个数据源中。例如一个订单请求,库存存在于一个数据源中,在库存安全的情况下,去检测用户的金额是否足够,这个是存在于另一个数据源中,在完成两者的检查都满足条件下,去建立一个订单,但是可能在你检查金额的过程中,另一个用户成功下单,并修改了库存信息,而在你检查完金额后,实际库存已经不满足你的下单条件了,但是你还是下单成功了,如此就是并发导致的问题。术语上称之为离线并发。

并发的原因
上面那个例子是造成并发的一个原因:不一致读。在并发的读取多个数据源数据的情况下会存在,另一个并发问题原因是:更新丢失,这个也是我们日常最多取考虑的问题,还是拿下单来说,两个请求同时读取了库存为40,再同时去更新库存的,正确的结果应该是38,但是因为库存40已经读取到内存中,双方都同时更库存更新为39.实际这个并不是我们要的结果。

对于第二个导致并发的问题:更新丢失,我们可以采用的手段是隔离:划分数据,每一个数据都只能被一个执行单元执行。常见的例子是我们需要获取一个数据,并独占的对这个数据进行读写,那么就建立一个文件锁,在一个请求在未释放文件锁条件下,其他请求都无法访问该数据。
只有变化的数据才会有并发问题,当然不变化的数据就不会有并发问题。

在操作系统层面,我们对于请求的处理有两种方式,一是一个请求一个进程,进程是一个重量级的执行语境,会分配单独的内存,能够解决在内存层面的并发问题,但是无法解决离线并发的问题,缺点是太耗费系统资源。另一种是一个请求一线程,线程是一个轻量级的执行单元,一个进程内可以存在多个线程,多个线程共享一片内存,那么一个独立数据就会面临并发问题。因为JAVA领域采用的是线程模型,所以也是我们程序员苦苦在追求和解决线程间并发的问题。优点是高效利用资源,缺点是并发问题严重。
大多数应用选择的还是线程模型,因为现在的持久化设备:mysql和oracle能够很好的解决单数据源的并发处理,所以留给我们程序员的就是内存层面的并发,也就是线程并发问题。

乐观锁和悲观锁
无论是持久化层面的并发处理还是内存层面或是离线并发,通常的处理方式有两种:
1.乐观并发锁 也就是说可以两个请求同时更新数据,A请求率先更新了数据,B请求在数据在更新时,发现跟原本取出来的不一致,那么就拒绝更新数据,并提示B请求改怎么去处理,这个检测不一致的手法常用的有版本号(时间戳也是很好的手段),拉取数据时候同时带出当时数据的版本号,在更新数据时候,更新版本号,那么另一个请求想要更新数据时就会发现版本号的不一致,就提示拒绝更新!

2.悲观并发锁 在一个请求获取数据的时候,另一个线程根本无法获得数据,只有第一个请求处理完成后,才能获取数据。主要手段是分为读锁和写锁,读锁是共享锁,可以一次被多个请求持有,但且还有一个请求持有读锁,那么其他请求就无所获得写锁,若是一个请求持有了写锁,那么其他请求都将无法获得读锁或是写锁。
可以说,乐观锁是关于冲突检查的,而悲观锁是关于冲突避免的,不给机会发生冲突。悲观锁是减少并发的程度,乐观锁相对自由些,只在更新时候才提示是否会存在并发问题。
两者的选择在与冲突的频率和严重性,若是冲突的后果不是很严重的话,乐观锁是一个不错的选择,因为能够得到很好的并发性。若是并发造成的后果非常严重,那么优先选择悲观锁。

乐观锁和悲观锁都能很好的解决更新丢失和单数据源的不一致读,但是对于多数据源的不一致读就无能为力,解决办法是通过一个多数据源控制器,这个控制器维护一个时序列表,对于每个数据源的更新都采用乐观锁或是悲观锁,在其中一个数据源处理失败时,完全的回滚事务。

死锁
既然有锁,那么就会存在死锁的情况,死锁就是存在多个锁,A请求获得了A1锁,B请求获得了B1锁,同时双方都需要去获得对方的锁,这个时候A1和B1都被持有的,但两个请求都在等待对方的释放,如此就导致了死锁,处理死锁的常见方式就是:超时控制和死锁检测,死锁检测的实现复杂度较高,更多采用的方式就是超时控制,在java中,对于超时已经有了很好的处理机制。
还有种处理死锁的方式就是一个请求在开始获得锁的时候,一起获得所有可能的锁,如此也能避免死锁,但是这种陈本更高。

以上所说的保证数据的正确性或是安全性,用专业的术语将就是事务,那么体现正确性就需要有以下几点要求:
1、原子性(Atomicity):动作序列的每个步骤要么全部成功,要么全部回滚。
2、一致性(Consistency):事务开始和结束时,系统资源必须处于一致状态。
3、隔离性(Isolation):一个事务,直道提交之后,其结果才对其他事务可见。
4、持久性(Durability):一个已提交事务的任何结果都必须是永久性的。

事务
对于夸多数据源的事务我们称之为:长事务。常见的事务是请求开始时候,开启事务,在请求结束时候关闭事务。但是在面临高并发的时候,如此就会影响吞吐量,还有种方式在写数据的时候才开启时候,如此提高效率,但是会带来的问题,不一致读,这种做法一般不推荐做,除非真的竞争很激烈。

对于事务分为两类,一是系统事务,也就是单个数据源保证的事务(数据库),还有种就是业务事务,也是我们一直想解决的问题,离线并发问题。解决方案前面也大致提到了,抽取出一个事务控制器,通过维护一个事务序列,采用乐观离线锁或是悲观离线锁,来控制各个数据源的事务,至于选择的原则就是冲突的频率和严重性,若是一个请求数需要用户花费大量的时间来填写页面的表单,若是你采用的乐观锁,只有在提交的时候才告诉用户,并发冲突了,那么用户体验是非常不好的,发现失败的代码太好了。悲观锁就能很快的发现错误,但是也会带来灵活性上的损失。

还有种处理离线并发就是将多数据源数据的副本集中到一个地方,统一的对这个地方坐乐观锁或是悲观锁处理!但是需要维护的就是副本和源数据之间的更新!

前面提过了操作系统层面的回话访问分为进程和线程两个级别,进程的优点就不多提了,缺点是耗费资源,也就是创建进程的代价大,服务的资源毕竟是非常有限的,那么改进的方式是复用进程,自己维护一个进程池,但是一定的进程若还是不能满足我们的吞吐量要求,那么再进一步的改造方式就是一个进程中开启多个线程进行处理,如此带来的就是需要合理的处理并发问题。同时还有就是这个进程挂掉后,在这个进程中的所有线程都会挂掉。
apache中的prefork模式就是一个请求一个进程,而worker模式就是一个进程多个线程,至于选择还依赖于很多因素,例如系统方面的cpu和业务上的要求等等,具体这里就不展开了!

godspeed712

系统设计 -- 表现层

随着互联网的普及,浏览器已经成为当之无愧的信息展示方式,主要是基于不需要安装客户端软件,对于用户的接入门槛很低,同时丰富的UI展现方式和标准化的解析方式构成了它现在这种局面的原因。
浏览器通过URI调用WEB服务器,WEB服务器解释请求方的URI并调用服务端程序来处理这些请求。对于如何解析请求,看往过去我们发现有这么几种方式。
最早期开用的是一些服务端的小程序,例如java中的servlet或是CGI脚本,这个可以统称为服务端脚本方式。这些脚本能够很好的解析请求中的参数,并选择合适的子程序,组后通过流的方式将结果输出到浏览器。但是它的缺点很明显,将页面展示的渲染也放在脚本中(servlet)中,这...

随着互联网的普及,浏览器已经成为当之无愧的信息展示方式,主要是基于不需要安装客户端软件,对于用户的接入门槛很低,同时丰富的UI展现方式和标准化的解析方式构成了它现在这种局面的原因。
浏览器通过URI调用WEB服务器,WEB服务器解释请求方的URI并调用服务端程序来处理这些请求。对于如何解析请求,看往过去我们发现有这么几种方式。
最早期开用的是一些服务端的小程序,例如java中的servlet或是CGI脚本,这个可以统称为服务端脚本方式。这些脚本能够很好的解析请求中的参数,并选择合适的子程序,组后通过流的方式将结果输出到浏览器。但是它的缺点很明显,将页面展示的渲染也放在脚本中(servlet)中,这个对于程序员来说是非常痛苦的,同时像java这种解释执行的语言,若是简单修改可一个页面,都需要重新编译,发布。

所以就出现第二种方式,服务器页面。将页面输出通过在服务端构造,对于其中的动态参数用脚本的方式替代,并在输出到浏览器前进行渲染,明显的例子就是jsp,php,freemarker等前端展示技术。同时将请求的解析及具体子程序的调用放到了脚本中。
服务器页面的方式是将页面的渲染和请求的解析和业务处理进行了剥离,但是随着业务复杂度的增加,也会面对领域层同样的问题,就是代码副本,后来就出现了MVC模型。


  • 模型:负责领域逻辑

  • 控制器:处理请求

  • 视图:基于模型创建应答消息


一条请求进入输入控制器,控制器从中获取数据并调用合适的领域对象处理业务逻辑,领域对象和数据源交互,并根据消息的请求处理数据并返回,控制器根据返回结果选择合适的视图显示应答消息。输入控制器和视图中不直接交互,通过一个请求级别的会员共享数据,这个数据根据需要有不同的生命周期,有page,request,session,application等。同时控制器还需要将一个领域对象的结果转化为一个表现层展示对象,若是不同页面之间的这些领域对象和表现层对象转化逻辑非常类似的话,我们可以再抽取出一个应用控制器来处理这部分工作,但是更多的时候应用控制和输入控制器是一个。
到现在为止,输入控制器就作为了页面和领域对象的中间层。

早期的时候MVC模型中 jsp是承担了控制器和视图的功能,javabean作为业务模型,但是这种模式并没有很好的分离控制器和视图,容易造成代码的冗余,这种形式历史上称为mvn model 1 .后来对于控制器这个角色交给了服务端脚本就是servlet,如此就进化到了mvc modle 2 。

对于视图的展示存在二种模式:
1.转化视图 常见的就是XSLT,领域对象通过XML方式的存在
2.模板视图 这个是我们现在最流行的,常见的例如就是freemarker,jsp等,通过预定义的标签,来动态的加载数据。

对于使用的技巧就是会对一些多个页面公用的东西进行封装,例如宏等手段!
godspeed712

系统设计 -- 数据源层构建

抽取出数据源层的作用是能够将领域层需要的不同的基础设施通信的细节进行屏蔽。
现在最成功的持久化方式就是关系数据库,它成功的原因在于SQL,一个标准化的数据通信语言,尽管不同的厂商有细节上的改进,但是总体还是相同的。

分离
对于领域逻辑访问数据的方式,早期的时候我们使用JDBC,是直接将SQL写在了我们的设计代码里面,这样可能在开发过程中效率会更高,但是DBA希望也能够访问到SQL,从DB的角度来更好的调整和组织索引。
所以对于此,就很有必要将SQL和我们的领域逻辑进行剥离,放到独立的类中,每个表对应一个类,这个类就是我们访问数据库的入口。对于单条记录的获取,我们称之为行数据入口,就是一条数据就是一个对...

抽取出数据源层的作用是能够将领域层需要的不同的基础设施通信的细节进行屏蔽。
现在最成功的持久化方式就是关系数据库,它成功的原因在于SQL,一个标准化的数据通信语言,尽管不同的厂商有细节上的改进,但是总体还是相同的。

分离
对于领域逻辑访问数据的方式,早期的时候我们使用JDBC,是直接将SQL写在了我们的设计代码里面,这样可能在开发过程中效率会更高,但是DBA希望也能够访问到SQL,从DB的角度来更好的调整和组织索引。
所以对于此,就很有必要将SQL和我们的领域逻辑进行剥离,放到独立的类中,每个表对应一个类,这个类就是我们访问数据库的入口。对于单条记录的获取,我们称之为行数据入口,就是一条数据就是一个对象。这样的类就是单条的获取数据!但是,我们实际开发过程中还会碰到的一种情况就是一次获得一个记录集,这种持有记录集的入口类,我们称之为表数据库入口,不再是以一种对象的方式来理解数据。
实际的开发过程中,很难见到单纯的行数据入口和或是表数据入口,一般这个入口类包含以上两种情况:既有批量的获取数据,亦有获取单条的数据。

在简单的应用中,领域模型和数据库结构是相当的一致的,一个领域对象对应一个数据库的话,那么完全可以通过领域对象直接与数据库通信,也就是说一个领域类既有领域逻辑,也有同数据库通信的逻辑。这种做法的优点是能避免代码的副本,但是随着领域逻辑的复杂性提高,领域对象已经不能单一的对应一张表的情况下就会出现问题,同时对于采用继承,多态来实现的设计模式就很难实现。

对于这个问题的解决是在领域模型和数据库入口类中间添加一个数据映射器。双方通过数据库映射器来通信,两者完全独立。对应到我们现在开发中就是DAO层。

数据源的其他功能处理
随着业务的复杂,会出现在一个领域逻辑中对应了多个不同的数据源层,可能是同一个应用的,也可能是外部应用的,对于此,就需要保证保证多个数据源层间的提交进行统一的管理,在完成所有的业务逻辑后,再持久化数据,我们将负责此类工作的类称之为工作单元,在多事务中就是一个事务管理器,事务管理的具体实现有二阶段提交,以及它的改进版,三阶段提交,具体这里就不展开了。
同理,在单个数据源中保证一个数据的并发访问的数据一致性的处理,也可以称之为一个工作单元。它是领域模型之下,数据源层之上的中间处理层。

数据结构的映射
通过领域建模,那么必然在多个领域对象之间有依赖,组合,聚合等关系,对于单项或是双向的一对一依赖,在映射到数据库的设计上就是一个外键来解决,若是一对多的话也是外键,不过一个表中多条数据对应另一个表中数据。若是多对多的话,那么一般采用的方案是在另个表之间添加中间表。
现在ibaits和hibernation帮我们解决的就是这种领域对象和数据库表之间的映射,不过它们都是通过元数据的方式来实现,如此对于通用性的代码完全可以通过反射等方式来实现。看个ibaits中的具体例子就是:
<resultMap id="domain" class="xxx.xxx.java">
<result property="id" column="ID" />
<result property="name" column="Name" />
<result property="age" column="Age" />
</resultMap>
将数据库字段和对象中的字段通过元数据映射,然后对于具体数据库记录通过反射生成对象。

继承的映射
之前说的都是组合方式的数据库映射,例如一个对象持有单个对象,或是持有一个集合的对象,或是双向的持有等,对于继承这种类关系一般有三种方式处理。
加入A类是一个接口,B、C是A类的具体实现,D是C的继承。
1. 单表继承:就整个继承体系中的类映射到一张表张。
2. 具表继承:就是为整个体系中的所有实现类映射到一张表中,也就是说现在会有B、C、D三张表存在
3. 类表继承:为整个体系中的所有类各自映射到一张表中,也就是说会有A、B、C、D四张表存在。

以上三种方式中,类表继承是类和表之间最简单的关系,但是代价需要通过多张表的join才能获取一个对象或是多次的查询来获得一个对象,对于性能损耗太大。因为不同表存了一个对象的不同属性。
具表继承避免了join,允许从一个表中获取一个对象,但是改变起来很难,若是对于超类的任何改变都不得不改变它对应的子表数据,同时若类继承结构改变的话,也会是很大的困扰的。
单表解决了以上两种的问题,但是带来的就是空间的浪费,越高层次的类,空间浪费越严重。

godspeed712

系统设计 --- 领域层的组织

目前的领域逻辑的组织可以分为三种:
一 事务脚本:
这个是我们最为常见的构建方式,简单的说:从表示层接受到参数,进行校验,计算,在将数据存储到合适的数据源层。然后再给表示层一个合适的返回,基本上是一个过程对应一个用户的可能的一个动作,在多个行为之间可以抽取出子例程在不同事物脚本之间共享!
优点:


  • 简单,目前大多数开发者使用的方式

  • 能够很好同行数据入口或是表数据入口的简单数据源层很好的协作

  • 设定事物边界的方法显而易见,一个事务始于其脚本的开始,终于其脚本的关闭


缺点:
前面也提到了,当在多个事务脚本中存在相同代码的时候,可以抽取出公共的子例程,但是这里有...

目前的领域逻辑的组织可以分为三种:
一 事务脚本:
这个是我们最为常见的构建方式,简单的说:从表示层接受到参数,进行校验,计算,在将数据存储到合适的数据源层。然后再给表示层一个合适的返回,基本上是一个过程对应一个用户的可能的一个动作,在多个行为之间可以抽取出子例程在不同事物脚本之间共享!
优点:


  • 简单,目前大多数开发者使用的方式

  • 能够很好同行数据入口或是表数据入口的简单数据源层很好的协作

  • 设定事物边界的方法显而易见,一个事务始于其脚本的开始,终于其脚本的关闭


缺点:
前面也提到了,当在多个事务脚本中存在相同代码的时候,可以抽取出公共的子例程,但是这里有两个问题,一我们需要知道何时出现了副本,二我们如何取消除副本 这两个都是比较头疼的问题。

二 领域模型:
领域模型的出现就是为了解决事务脚本的缺点,我们以名词为构建方式,抽象出对象的概念,将该对象相关的计算和验证放入对象自身内部,这样原本一个完整的行为会分布到各个名词对象中。
优点:
在业务逻辑复杂度较高的情况下,解决了事务脚本中的副本发现和消除

缺点:
需要一定的思维适应,若是领域建模的不好,反而会适得其反
同数据库之间的映射复杂度变高,一般需要数据映射器,常见的做法是在领域对象和数据库间加上实体对象,通过全自动或是半自动的ORMapping工具。

跨领域的行为处理
一般有两种方式,一是在领域对象中调用另一领域对象,二是在各个领域对象之上抽取一个外观的SERVICE,在这个SERVICE层面统一调度,具体后面还会提到。

三 表模块:
这种构建方式类似于领域模型,但是有点稍微的不同,领域模型是数据库中每一个数据对应一个实例,而表模型只有一个公共的实例。举例子来说,一个定合同的行为,我们会生成合同类,产品类、收入确认类。
表模块的方式通过合同类从数据库中拉取一个合同的记录集,然后通过传递这个记录器给产品类来完成费用的计算,最后将计算的结果通过收入确认类插入到数据库中,若是只计算其中一个合同的费用,那么接需要传递一个合同的ID给产品类!
领域模型的方式每次只传递一个合同记录给产品类来完成费用的计算,再将结果通过收入确认类来插入到数据库中。(同时领域对象可能不等同于实际的表结构)
事务脚本是通过数据源层获取一个记录集,再计算费用,最后插入数据库中,并不会将不同的动作区分到不同的领域对象中。

优点:
对比上面的三种方式,可以看出表模块是事务脚本和领域对象的中间地带,做了分类行为处理,但是更灵活的采用的是记录集的方式来处理!围绕表来而非围绕行为来组织领域逻辑,同时更容易的发现和移除冗余代码。很好很直接面对我们的表数据!这种方式也是比较常见的开发模式!

缺点:
因为围绕的是表记录集,最小单位不是领域对象,所以不能细粒度的组织逻辑结构,很难去使用一个继承、组合和其他面向对象的设计模式。

如何选择:
对于一些简单的业务场景,完全可以使用事务脚本的方式来做,若是业务有一定复杂的话,那么还是推荐使用领域对象,如何判断复杂,还是需要根据自己平时的工作经验,或是请教资深的开发人员,至于表模块的话,取决于是否拥有一个记录集的工具,目前现在的ibaits和hibernation都能很好的支持记录集的数据获取,读过DDD的人大多还是处在这种模式下,目前看到的代码中。当然这三者也可以同时使用,但是我个人根据设计代码风格的一致性考虑的话,还是尽量的统一,虽然会付出一定的陈本(个人偏好而已)!

领域层的组织
上面说了这么多,那么该如何具体组织领域层,若是事务脚本的话,就毋庸置疑了,简单的一个service搞定一切,若是领域模型或是表模块的话,那么就需要将服务层单独的剥离,置于底层的领域模型或是表模块之上,服务层中处理事务、安全、基础的校验等等。
有种做法是服务层包含所有的业务逻辑,以事务脚本方式组织,下层的领域对象变成实体对象,简单的POJO,并与数据库记录一一对应。

还有种做法是 控制器--实体风格,就是将每个用例或是行为特有的逻辑放到一个控制器类中,将各个不同共性的行为组织到领域对象中,这种做法最典型的案例就是MVC。这个控制器可以称之为:用例控制器或是应用控制器。
这种做法是将行为分不到事务控制器和领域对象中,如此还是会产生冗余代码。

简单总结的说:将逻辑层分为两层,在服务层尽量减少业务逻辑,让他只负责安全、事务等事宜,而将业务逻辑落到领域对象中,最小化服务层,对外暴露的接口是基于用例!

对于表模块和领域对象区别:
还是拿合同的用例说来,在服务层中若是面对一个记录集,是先生成一个合同类,并将记录集一次性传递给产品类,而领域对象是获取一个记录集后,循环的调用产品类来处理数据!

LOFTER

让兴趣,更有趣

简单随性的记录
丰富多彩的内容
让生活更加充实

下载移动端
关注最新消息