软件架构设计杂记: 分层架构 与 PO、VO、DTO、BO、POJO、BO/DO、DAO

久未见 提交于 2019-11-27 16:06:19

软件架构设计杂记: 分层架构 与 PO、VO、DTO、BO、POJO、BO/DO、DAO

原创: 陈光剑 Kotlin 开发者社区 前天

某位计算机大师说过:

 

计算机科学领域任何问题,都可以间接的通过添加一个中间层来解决.

什么是架构?

先引用《系统架构:复杂系统的产品设计与开发》里面的一句话:

结构良好的创造活动要优于毫无结构的创造活动。

架构始于建筑,是因为人类发展(原始人自给自足住在树上,也就不需要架构),分工协作的需要,将目标系统按某个原则进行切分,切分的原则,是要便于不同的角色进行并行工作。

一般而言,软件系统的架构(Architecture)有两个要素:

 

1.它是一个软件系统从整体到部分的最高层次的划分。

 

2.一个系统通常是由元件组成的,而这些元件如何形成、相互之间如何发生作用,则是关于这个系统本身结构的重要信息。

 

详细地说,就是要包括架构元件(Architecture Component)、联结器(Connector)、任务流(TASk-flow)。所谓架构元素,也就是组成系统的核心"砖瓦",而联结器则描述这些元件之间通讯的路径、通讯的机制、通讯的预期结果,任务流则描述系统如何使用这些元件和联结器完成某一项需求。

 

建造一个系统所作出的最高层次的、以后难以更改的,商业的和技术的决定。

 

在建造一个系统之前会有很多的重要决定需要事先作出,而一旦系统开始进行详细设计甚至建造,这些决定就很难更改甚至无法更改。显然,这样的决定必定是有关系统设计成败的最重要决定,必须经过非常慎重的研究和考察。

 

根据我们关注的角度不同,可以将架构分成三种:

1.逻辑架构

软件系统中元件之间的关系,比如用户界面,数据库,外部系统接口,商业逻辑元件,等等。

 

从上面这张图中可以看出,此系统被划分成三个逻辑层次,即表象层次,商业层次和数据持久层次。每一个层次都含有多个逻辑元件。比如WEB服务器层次中有HTML服务元件、Session服务元件、安全服务元件、系统管理元件等。

2.物理架构

软件元件是怎样放到硬件上的。

比如下面这张物理架构图,图中所有的元件都是物理设备,包括网络分流器、代理服务器、WEB服务器、应用服务器、报表服务器、整合服务器、存储服务器、主机等等。

 

3.系统架构

系统的非功能性特征,如可扩展性、可靠性、强壮性、灵活性、性能等。

系统架构的设计要求架构师具备软件和硬件的功能和性能的过硬知识,这一工作无疑是架构设计工作中最为困难的工作。

此外,从每一个角度上看,都可以看到架构的两要素:元件划分和设计决定。

首先,一个软件系统中的元件首先是逻辑元件。这些逻辑元件如何放到硬件上,以及这些元件如何为整个系统的可扩展性、可靠性、强壮性、灵活性、性能等做出贡献,是非常重要的信息。

其次,进行软件设计需要做出的决定中,必然会包括逻辑结构、物理结构,以及它们如何影响到系统的所有非功能性特征。这些决定中会有很多是一旦作出,就很难更改的。

根据作者的经验,一个基于数据库的系统架构,有多少个数据表,就会有多少页的架构设计文档。比如一个中等的数据库应用系统通常含有一百个左右的数据表,这样的一个系统设计通常需要有一百页左右的架构设计文档。

为什么需要架构?

有系统的地方就需要架构,大到航空飞机,小到一个电商系统里面的一个功能组件都需要设计和架构。

先来一个分层图:

 

一些经典的Java 企业应用架构图:

 

 

软件架构

软件架构是一个系统的草图。软件架构描述的对象是直接构成系统的抽象组件。各个组件之间的连接则明确和相对细致地描述组件之间的通信。在实现阶段,这些抽象组件被细化为实际的组件,比如具体某个类或者对象。在面向对象领域中,组件之间的连接通常用接口来实现。

软件架构为软件系统提供了一个结构、行为和属性的高级抽象,由构件的描述、构件的相互作用、指导构件集成的模式以及这些模式的约束组成。软件架构不仅显示了软件需求和软件结构之间的对应关系,而且指定了整个软件系统的组织和拓扑结构,提供了一些设计决策的基本原理。

软件架构的核心价值应该只围绕一个核心命题:控制复杂性。他并不意味着某个特定的分层结构,某个特定的方法论(贫血、DDD等)。

软件架构分类

 

业务架构:由业务架构师负责,也可以称为业务领域专家、行业专家。业务架构属于顶层设计,其对业务的定义和划分会影响组织结构和技术架构。例如,阿里巴巴在没有中台部门之前,每个业务部门的技术架构都是烟囱式的,淘宝、天猫、飞猪、1688等各有一套体系结构。而后,成立了共享平台事业部,打通了账号、商品、订单等体系,让商业基础实施的复用成为可能。

应用架构:由应用架构师负责,他需要根据业务场景的需要,设计应用的层次结构,制定应用规范、定义接口和数据交互协议等。并尽量将应用的复杂度控制在一个可以接受的水平,从而在快速的支撑业务发展的同时,在保证系统的可用性和可维护性的同时,确保应用满足非功能属性要求(性能、安全、稳定性等)。

分布式系统架构:分布式系统基本是稍具规模业务的必选项。它需要解决服务器负载,分布式服务的注册和发现,消息系统,缓存系统,分布式数据库等问题,同时架构师要在CAP(Consistency,Availability,Partition tolerance)之间进行权衡。

数据架构:对于规模大一些的公司,数据治理是一个很重要的课题。如何对数据收集、数据处理提供统一的服务和标准,是数据架构需要关注的问题。其目的就是统一数据定义规范,标准化数据表达,形成有效易维护的数据资产,搭建统一的大数据处理平台,形成数据使用闭环。

物理架构:物理架构关注软件元件是如何放到硬件上的,包括机房搭建、网络拓扑结构,网络分流器、代理服务器、Web服务器、应用服务器、报表服务器、整合服务器、存储服务器和主机等。

运维架构:负责运维系统的规划、选型、部署上线,建立规范化的运维体系。

 

PO,DTO,VO 分层

 

常规的一个Java 应用中的PO,DTO,VO 分层图如下:

 

 

 

一、简单Java对象

 

PO

 

persistent object,持久对象。与数据库里表字段一一对应。PO是一些属性,以及set和get方法组成。一般情况下,一个表对应一个PO,直接与操作数据库的crud相关。

 

VO

 

view object/value object,表现层对象。通常用于业务层之间的数据传递,和PO一样也是仅仅包含数据而已。但应是抽象出的业务对象,可以和表对应,也可以不。这根据业务的需要而定。对于页面上要展示的对象,可以封装一个VO对象,将所需数据封装进去。

 

DTO

 

data trasfer object,数据传输对象。主要用于远程调用等需要大量传输对象的地方。

比如我们一张表有 100 个字段,那么对应的 PO 就有 100 个属性。 但是我们界面上只要显示 10 个字段, 客户端用 WEB service 来获取数据,没有必要把整个 PO 对象传递到客户端,

这时我们就可以用只有这 10 个属性的 DTO 来传递结果到客户端,这样也不会暴露服务端表结构 . 到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为 VO。

POJO

plain ordinary java object/pure old java object简单无规则 java 对象,纯的传统意义的 java 对象。

 

二、复杂Java对象

 

BO/DO

 

bussiness object业务对象、Domain Object域对象。封装业务逻辑的 Java 对象 , 通过调用 DAO 方法 , 结合 PO,VO 进行业务操作。 一个BO对象可以包括多个PO对象。如常见的工作简历例子为例,简历可以理解为一个BO,简历又包括工作经历,学习经历等,这些可以理解为一个个的PO,由多个PO组成BO。

 

DAO

data access object,数据访问对象。此对象用于访问数据库。通常和 PO 结合使用, DAO 中包含了各种数据库的操作方法。通过它的方法 , 结合 PO 对数据库进行相关的操作。夹在业务逻辑与数据库资源中间。

(原文:https://www.jianshu.com/p/d9cfd1a85068)


 

架构设计原则

 

6大设计原则

 

  1.   Single Responsibility Principle  : 单一职责原则

  2.   Liskov Substitution Principle        : 里氏替换原则

  3.   Dependence Inversion Principle  :依赖倒置原则

  4.   Interface Segregation Principle   : 接口隔离原则

  5.   Law of Demeter                  : 迪米特法则

  6.   Open Closed Principle                  : 开闭原则

 

  软件开发之所以会有这些原则,就是因为复杂多变且不可预料的需求。并不是说在实际项目开发中对这六大原则中的每一条都遵循到极致,而是说在项目开发的过程中,根据项目的实际需求尽量的去遵守这些原则。当然要做到这些肯定是不容易的,能真正做到并且做好的恐怕也只能是有经验之人。

 

高内聚低耦合(High cohesion and low coupling)

 

什么是高内聚、低耦合?

问题场景:

 

模块独立性指每个模块只完成系统要求的独立子功能,并且与其他模块的联系最少且接口简单,两个定性的度量标准――耦合性和内聚性。


耦合性也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。

 

 

耦合性分类(低―高)

 

1 无直接耦合:

2 数据耦合: 指两个模块之间有调用关系,传递的是简单的数据值,相当于高级语言的值传递;

3 标记耦合: 指两个模块之间传递的是数据结构,如高级语言中的数组名、记录名、文件名等这些名字即标记,其实传递的是这个数据结构的地址;

4 控制耦合: 指一个模块调用另一个模块时,传递的是控制变量(如开关、标志等),被调模块通过该控制变量的值有选择地执行块内某一功能;

5 公共耦合: 指通过一个公共数据环境相互作用的那些模块间的耦合。公共耦合的复杂程序随耦合模块的个数增加而增加。

6 内容耦合: 这是最高程度的耦合,也是最差的耦合。当一个模块直接使用另一个模块的内部数据,或通过非正常入口而转入另一个模块内部。

 

 

内聚性又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。
 

内聚性分类(低―高)

 

1 偶然内聚: 指一个模块内的各处理元素之间没有任何联系。

2 逻辑内聚: 指模块内执行几个逻辑上相似的功能,通过参数确定该模块完成哪一个功能。

3 时间内聚: 把需要同时执行的动作组合在一起形成的模块为时间内聚模块。

4 通信内聚: 指模块内所有处理元素都在同一个数据结构上操作(有时称之为信息内聚),或者指各处理使用相同的输入数据或者产生相同的输出数据。

5 顺序内聚: 指一个模块中各个处理元素都密切相关于同一功能且必须顺序执行,前一功能元素输出就是下一功能元素的输入。

6 功能内聚: 这是最强的内聚,指模块内所有元素共同完成一个功能,缺一不可。与其他模块的耦合是最弱的。

 

    耦合性与内聚性是模块独立性的两个定性标准,将软件系统划分模块时,尽量做到高内聚低耦合,提高模块的独立性,为设计高质量的软件结构奠定基础。

 

 

间阶层与重构(Indirection and Refactoring)

某位大师说过: 计算机科学中的所有的问题, 都可以通过增加一个间阶层来解决.

这位大师好像是 Dennis Debruler .

间阶层的价值:

  • 允许逻辑共享

  • 分开解释意图和实现:你可以选择每个类和函数的名字,这给了你一个解释自己意图的机会。

  • 隔离变化:通过使用子类来隔离同一对象修改对另一处引用带来变化的风险

  • 封装条件逻辑:使用多态机制将条件逻辑转化为消息机制,这往往能降低代码的重复,增加清晰度并提高弹性.

 

但是:

  • 大多数重构都为程序引入了更多的间接层

  • 过多的间接层会导致代码的层次太深
    使代码难以阅读.因此要权衡加入间接层的利弊

     

     

理解设计模式

 

 

 

1、GOF在书中说:设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述;

 

设计模式就是不断反省,将软件开发经验抽象积累成解决问题的预案。

 

2、Dennis DeBruler曾说过的一句话:计算机科学是一门相信所有问题都可以通过多一个间接层(indirection)来解决的科学。

 

技术问题的解决思路是类似的:添加间接层。如:J2EE的分层技术、重构等。

 

间接层的好处:共享逻辑(重用)、分离意图和实现(提高灵活性)、 隔离变化(封装)、解耦等。

 

但是要记住,间接层应用过于泛滥,则会过犹不及,它会导致简单问题复杂化、跳跃阅读、难以理解等问题,适度的使用间接层,设计模式就是很好的范例。

 

不过在具体操作上,23种模式中间接层的应用可大可小,无比灵活,注意其应用场景。

 

3、设计模式中广泛遵循了两条设计原则:面向接口编程,而不是实现;优先使用组合,而不是继承。

 

 

 

下面内容是摘自一篇文章的内容:

 

分层架构

分层是一种常见的根据系统中的角色(职责拆分)和组织代码单元的常规实践。常见的分层结构如下图所示:

CQRS

CQS(Command Query Separation,命令查询分离)最早来自于Betrand Meyer(Eiffel语言之父,OCP提出者)提出的概念。其基本思想在于,任何一个对象的方法可以分为两大类:

命令(Command):不返回任何结果(void),但会改变对象的状态。

查询(Query):返回结果,但是不会改变对象的状态,对系统没有副作用。

六边形架构

六边形架构是Alistair Cockburn在2005年提出,解决了传统的分层架构所带来的问题,实际上它也是一种分层架构,只不过不是上下,而是变成了内部和外部(如下图所示)。


六边形架构又称为端口-适配器架构,这个名字更容器理解。六边形架构将系统分为内部(内部六边形)和外部,内部代表了应用的业务逻辑,外部代表应用的驱动逻辑、基础设施或其他应用。

适配器分为两种类型(如下图所示),左侧代表 UI 的适配器被称为主动适配器(Driving Adapters),因为是它们发起了对应用的一些操作。而右侧表示和后端工具链接的适配器,被称为被动适配器(Driven Adapters),因为它们只会对主适配器的操作作出响应。

洋葱圈架构

洋葱架构与六边形架构有着相同的思路,它们都通过编写适配器代码将应用核心从对基础设施的关注中解放出来,避免基础设施代码渗透到应用核心之中。这样应用使用的工具和传达机制都可以轻松地替换,可以一定程度地避免技术、工具或者供应商锁定。

不同的是洋葱架构还告诉我们,企业应用中存在着不止两个层次,它在业务逻辑中加入了一些在领域驱动设计的过程中被识别出来的层次(Application,Domain Service,Domain model,Infrastructure等)

另外,它还有着脱离真实基础设施和传达机制应用仍然可以运行的便利,这样可以使用 mock 代替它们方便测试。

在洋葱架构中,明确规定了依赖的方向:

  • 外层依赖内层;

  • 内层对外层无感知。

COLA应用架构

COLA架构是我团队自主研发的应用架构,目前已经开源( https://github.com/alibaba/COLA )。在COLA的设计中,我们充分汲取了经典架构的优秀思想。除此之外,我们补充了规范设计和扩展设计,并且使用Archetype的方式,将架构固化下来,以便可以快速的在开发中使用。

分层设计

COLA的分层是一种改良了的三层架构。主要是将传统的业务逻辑层拆分成应用层、领域层和基础实施层。如下图所示,左边是传统的分层架构,右边是COLA的分层架构。

其每一层的作用范围和含义如下:1)展现层(Presentation Layer):负责以Rest的格式接受Web请求,然后将请求路由给Application层执行,并返回视图模型(View Model),其载体通常是DTO(Data Transfer Object);

2)应用层(Application Layer):主要负责获取输入,组装上下文,做输入校验,调用领域层做业务处理,如果需要的话,发送消息通知。当然,层次是开放的,若有需要,应用层也可以直接访问基础实施层;

3)领域层(Domain Layer):主要是封装了核心业务逻辑,并通过领域服务(Domain Service)和领域对象(Entities)的函数对外部提供业务逻辑的计算和处理;

4)基础实施层(Infrastructure Layer)主要包含Tunnel(数据通道)、Config和Common。这里我们使用Tunnel这个概念来对所有的数据来源进行抽象,这些数据来源可以是数据库(MySQL,NoSql)、搜索引擎、文件系统、也可以是SOA服务等;Config负责应用的配置;Common是通用的工具类。

扩展设计

对于只有一个业务的简单场景,对扩展性的要求并不突出,这也是为什么扩展设计常被忽略的原因,因为我们大部分的系统都是从单一业务开始的。但是随着业务场景越来越复杂,代码里面开始出现大量的if-else逻辑。此时除了常规的策略模式以外,我们可以考虑在架构层面提供统一的扩展解决方案。

在扩展设计中,我们提炼出两个重要的概念,一个是业务身份,另一个是扩展点。

业务身份是指业务在系统唯一标识一个业务或者一个场景的标志。在具体实现中,我们使用BizCode来表示业务身份,其中BizCode采用类似Java包名命名空间的方式。例如,我们可以用“ali.tmall”表示阿里天猫业务,用“ali.tmall.car” 表示阿里天猫的汽车业务,而用"ali.tmall.car.aftermarket"代表这是阿里天猫的汽车业务的后市场场景。

每个业务或者场景都可以实现一个或多个扩展点(ExtensionPoint),也就是说一个业务身份加上一个扩展点,可以唯一地确定一个扩展实现(Extension)。而这个业务身份和扩展点的组合,我们将其称之为扩展坐标(ExtensionCoordinate),如下图所示。这样,通过业务身份+扩展点,我们就可以从框架层面实现对不同租户,不同业务,不同场景的扩展定制了。整个阿里业务中台正是基于这个思想,实现的多业务支撑的。

规范设计

任何事物都是规则性和随机性的组合。规范的意义就在于我们可以将规则性的东西固化下来,尽量减少随心所欲带来的复杂度,一致性可以降低系统复杂度。从命名到架构皆是如此,而架构本身就是一种规范和约束,破坏这个约束,也就破坏了架构。

COLA制定了一些列的规范:包括组件(Module)结构、包(Package)结构、命名等。

比如对于组件,我们要求使用COLA的应用都应该遵循如下图所示的组件划分:

COLA架构总览

在架构思想上,COLA主张像六边形架构那样,使用端口-适配器去解耦技术细节;主张像洋葱圈架构那样,以领域为核心,并通过依赖倒置反转领域层的依赖方向。最终形成如下图所示的组件关系。

换一个视角,从COLA应用处理响应一个请求的过程来看。COLA使用了CQRS来分离命令和查询的职责,使用扩展点和元数据来提升应用的扩展性。整个处理流程如下图所示:

应用架构的核心

纵观上面介绍的所有应用架构,我们可以发现一个共同点,就是“核心业务逻辑和技术细节分离”。

是的,六边形架构、洋葱圈架构、以及COLA架构的核心职责就是要做核心业务逻辑和技术细节的分离和解耦。

试想一下,业务逻辑和技术细节糅杂在一起的情况:所有的代码都写在ServiceImpl里面,前几行代码是做validation的事,接下来几行是做convert的事,然后是几行业务处理逻辑的代码,穿插着,我们需要通过RPC或者DAO获取更多的数据,拿到数据后,又是几行convert的代码,在接上一段业务逻辑代码,然后还要落库,发消息.....等等。

再简单的业务,按照上面这种写代码的方式,都会变得复杂,难维护。

因此,我认为应用架构的核心使命就是要分离业务逻辑和技术细节。让核心业务逻辑可以反映领域模型和领域应用,可以复用,可以很容易被看懂。让技术细节在辅助实现业务功能的同时,可以被替换。

最后我们发现,应用架构的道就是:“让上帝的归上帝,凯撒的归凯撒。”

(原文: https://developer.aliyun.com/article/714126)
 

 

 

 

 

软件架构风格

 

软件架构设计的一个核心问题是能否使用重复的软件架构模式,即能否达到架构级别的软件重用。——基于这个目的,学者们开始研究和实践软件架构的风格和类型问题。

 

        软件架构风格是描述某一特定应用领域中系统组织方式的惯用模式,即定义了用于描述系统的术语表和一组指导构建系统的规则。

 

        软件架构风格为大粒度的软件重用提供了可能性。

 

        软件架构风格涉及的问题:设计词汇表是什么?构件和连接件的类型是什么?可容许的结构模式是什么?基本的计算模型是什么?风格的基本不变性是什么?此风格的优缺点是什么?其常见的特例是什么?

 

一、数据流风格 

        包括两种具体的风格:批处理序列和管道/过滤器。

 

       批处理的风格的每一步都是独立的,并且每一步都是顺序执行的。依赖关系。典型应用包括:经典数据处理、程序开发、Windows下的BAT程序。

 

       在管道/过滤器软件风格中,每个构件都有一组输入和输出,一般对输入进行变换和增量计算完成。

 

二、调用/返回风格

        实际上是一种分而治之的策略,其主要思想是将一个复杂的大系统分解为一些子系统,以便降低复杂度,并且增加可修改性。

 

        包括:主程序/子程序、面向对象风格、层次结构风格。

 

 

 

三、独立构件风格

       每个构件都是相对独立的个体,之间不直接通信,以降低耦合度,提升灵活性。

       包括进程通信风格、事件系统风格

 

 

四、虚拟机风格

        基本思想是人为构建一个运行环境,在这个环境之上,可以解析与运行自定义的一些语言,这样来增加架构的灵活性。

        包括解释器和规则为中心的风格。

 

 

五、仓库风格

       包括数据库系统、超文本系统、黑板风格。

 

       超文本系统的典型代表就是早期的静态网页。

 

       黑板风格包括:知识源、黑板(共享数据)、控制

 

六、特定领域软件架构

        DSSA:领域分析——领域设计——领域实现

 

七、状态转移

        RESTFULL风格

 

八、分布式风格

        包括C/S、B/S、CORBA、EJB、DCOM等。

(原文:https://blog.csdn.net/aganliang/article/details/82823605)

 

 

在软件设计界曾经有很多人认为功能是最为重要的,形式必须服从功能。与此类似地,在建筑学界,现代主义建筑流派的开创人之一Louis Sullivan也认为形式应当服从于功能(FORMs follows function)。

 

几乎所有的软件设计理念都可以在浩如烟海的建筑学历史中找到更为遥远的历史回响。最为著名的,当然就是模式理论和XP理论。

正如同软件本身有其要达到的目标一样,架构设计要达到的目标是什么呢?一般而言,软件架构设计要达到如下的目标:

 

1.可靠性(Reliable)。软件系统对于用户的商业经营和管理来说极为重要,因此软件系统必须非常可靠。

2.安全性(Secure)。软件系统所承担的交易的商业价值极高,系统的安全性非常重要。

3.可扩展性(SCAlable)。软件必须能够在用户的使用率、用户的数目增加很快的情况下,保持合理的性能。只有这样,才能适应用户的市场扩展得可能性。

4.可定制化(CuSTomizable)。同样的一套软件,可以根据客户群的不同和市场需求的变化进行调整。

5.可扩展性(Extensible)。在新技术出现的时候,一个软件系统应当允许导入新技术,从而对现有系统进行功能和性能的扩展。

6.可维护性(MAIntainable)。软件系统的维护包括两方面,一是排除现有的错误,二是将新的软件需求反映到现有系统中去。一个易于维护系统可以有效地降低技术支持的花费。

7.客户体验(Customer Experience)。软件系统必须易于使用。

8.市场时机(Time to Market)。软件用户要面临同业竞争,软件提供商也要面临同业竞争。以最快的速度争夺市场先机非常重要。

 

 

Kotlin 开发者社区

国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

Kotlin 开发者社区

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!