企业开发框架包括垂直方向架构和水平方向架构。
- 垂直方向架构是指一个应用程序的由下到上叠加多层的架构,同时这样的程序又叫整体式程序。
- 水平方向架构是指将大应用分成若干小的应用实现系统功能的架构,同时这样的系统叫做分布式系统。
在架构上java和.NET世界都有优秀的框架支持构建垂直和水平方向架构。
ASP.Net Core非常轻量且具有很高的性能,不仅适合做整体式程序,也非常适合做分布式系统。随着微服务的兴起,各种语言的混合应用是个趋势。
一、 垂直方向架构
1. 多层架构
分层架构通过程序包或者程序的隔离构建松耦合的应用。我们以最近流行的洋葱架构模型进行分析,如图
1.1 领域模型
包括领域实体/存储接口/服务接口,是整个程序的核心。
- 贫血模型
如果把大量的业务逻辑委托给服务接口实现者,领域模型显得很瘦小,就可以称之为贫血模型。这种模型下的领域对象仅仅表示“状态”。“行为”(也称为逻辑、过程)放在了N层结构的Logic/Service/Manager层中。优点是易于理解和实现,缺点是随着业务发展模型会难以表达业务领域。目前不少业内软件架构是这种模式。
贫血模型的简单图示:
- 充血模型
如果在领域模型中实现主要的业务逻辑,把不方便实现的业务(比如汇率结算,地理坐标解析等)委托给服务接口实现者,此时领域模型显得粗壮,就可以称之为充血模型。这种模型下的领域对象既表示“状态”又有”行为“,领域对象之间还通过聚合在一个根(聚合根),然后由根对象保证状态的一致性(类似于数据库表之间的约束一致性)。优点是模型易于跟进业务发展,容易通过重构表达最新的业务领域;缺点是不易掌握。
充血模型的简单图示:
1.2 存储仓库
领域模型中的对象自从被创建出来后不会一直留在内存中活动,当它不活动时会被持久化到数据库中,然后当需要的时候就会重建该对象;重建对象就是根据数据库中已存储的对象的状态重新创建对象的过程。
可见持久化和重建对象是一个和数据库打交道的过程。从更广义的角度来理解,程序会像集合一样从某个类似集合的地方根据某个条件获取一个或一些对象,往集合中添加对象或移除对象。
也就是说,这就需要提供一种机制,可以提供类似集合的接口来帮助程序管理对象。仓储就是基于这样的思想被设计出来的。
贫血模型下的存储仓库:实现实体对象的CRUD.
充血模型下的存储仓库:创建聚合根对象,实体对象的创建/更新/删除的行为由聚合根完成。
1.3 服务
一般有三种服务:应用服务、领域服务、基础服务。
- 应用服务
调用领域服务,形成工作流程,提供事物上下文。应用服务可以直接为UI提供服务或者直接作为API暴露出去。
- 领域服务
在具体一个业务上下文中提供服务。比如订单生成服务提供订单的生成功能,订单的跟踪服务提供订单的执行信息。
- 基础服务
基础服务提供的是和业务无直接关系的工具辅助服务。比如日志,安全,通信,事件总线等等。
1.4 UI
提供应用程序界面,支持用户输入。高效的UI开发往往会依赖UI框架,比如MVC框架。
UI框架比较多,比如MFC,WinForm,Asp.net WebForm,Asp.net MVC,Structs等等。
综上:洋葱模型的各层由外到内的单向依赖,简单直接的降低了代码之间的耦合度。
2. 典型框架
2.1 数据存储框架
2.1.1 数据访问辅助
这类框架往往提供了一套操作连接/命令/结果集的接口。用户先发起连接,发生sql命令,然后得到结果集以按行(又叫记录)方式进行遍历。不同的数据库需要相应的数据库驱动和实现,但对于用户来说操作数据的方式都是一样的。
优点:
- 编写原始sql,执行效率高。
- 握有连接对象,可以自定义事物的发起,提交或者回滚。
- 可以针对不同数据存储方式定义数据访问库。
- 一般比较轻量,依赖少。
缺点:
- 编写sql容易出错,随着数据库的演变,sql的潜在错误必须依赖充分的单元测试才能消除。
- 数据库的演变,sql重构难度会逐渐增大。
2.1.2 对象-关系映射
对象-关系映射(Object-Relational Mapping,简称ORM),以面向对象的开发方法实现数据的增删改查。
优点:
- 提高开发效率,降低开发成本
- 使开发更加对象化
- 可移植
- 可以很方便地引入数据缓存之类的附加功能
缺点:
- 自动化进行关系数据库的映射需要消耗系统性能。其实这里的性能消耗还不大,一般来说都可以忽略之(除非在性能特别关键应用)。
- 在处理多表联查、where条件复杂之类的查询时,ORM的语法会变得复杂。
典型框架:
2.2 MVC 框架
- Model
负责提供界面绑定的数据,以及业务逻辑处理。
- View
负责提供用户和应用之间的交互界面。不同类型的应用交互界面不同,可以是桌面程序窗口和web页面。
- Controller
负责收集用户输入事件,并分配事件给对该事件感兴趣的模型处理。对MVC的理解难点在于控制器的解读,在下面我们会用经典MVC模式和后端MVC模式分析。
2.2.1 经典MVC模式
这种模式大量用在复杂交互界面的桌面应用程序,比如MFC框架;以及web富应用框架中,比如AngularJS 1(尽管有人称之为为MVVM框架)。
控制器把输入事件通知给对该事件感兴趣的模型(步骤3),模型更新自己的状态并引起视图更新(步骤4),然后将更新事件通知控制器(步骤5),此时如果有对该更新事件感兴趣的模型会进一步进行处理(步骤6)。事实上控制器和模型之间构成了订阅/发布关系。
MVVM框架:把Controller变成了ViewModel,它与View进行双向绑定:View的变动,自动反映在 ViewModel,反之亦然。
2.2.2 后端MVC模式
自web应用普及开来,涌现了不少后端MVC模式,比如JAVA的Structs,.NET的Asp.net MVC等。由于页面在浏览器中显示,用户输入通过http到达后端,在后端看来MVC模式默认如下图所示:
与经典MVC模式不同,控制器直接获得输入事件(http请求),调用对应的模型,模型处理完后传递视图对象(VO)给控制器,控制器找到合适的视图并传递VO给视图,视图产生html/json/文件流等资源,应用程序根据资源类型Response结果。通常后端MVC的控制负责的处理比较简单;而复杂的用户输入可以用前端MVC框架实现(类似经典MVC模式)。
2.2.3 典型框架
2.2.4 MVC模式总结
模型负责提供视图数据和业务逻辑处理,视图负责提供用户交互界面,控制器将输入事件分配到对该事件感兴趣的模型。控制器居于中心位置,类似于功能胶水,但功能仅限于胶水是它不错的定位;避免写入过多业务逻辑代码使控制器变得复杂,难以维护。
MVC模式的价值在于更好的处理洋葱架构的UI层。
2.3 IOC框架
2.3.1 概念
用现实生活中例子引入:由于汽车依赖轮胎,如果按照正向依赖,则汽车厂商需要根据轮胎供应商能提供的轮胎来定义自己的接口(螺丝,大小等)。但如果依赖倒置一下(让轮胎厂商依赖汽车厂商),则汽车厂商只需要定义自己的接口(螺丝,大小等)由轮胎厂商去按照这个规格规范生产,就成为现在汽车行业的例子。这种依赖倒置是一个思想概念,需要一个容器来实现,这就是IOC框架。
- 依赖倒置(Dependency Inversion Principle, 英文缩写为DIP)
从依赖具体类变换为依赖抽象就叫依赖倒置,这是一种设计原则。
- 控制反转(Inversion of Control,英文缩写为IoC)
依赖倒置的一种实现方式,通常用于构建框架,比如WinForm,WebForm程序中你可以自定义具体的窗口类,然后在窗口初始化和虚函数里面写代码,框架会按照自己的一套流程运行:启动具体窗口,调用初始化方法,在不同的运行节点调用对应的虚函数。运行控制权始终在框架手中而不是我们写的代码。
- 依赖注入(Dependency Injection,英文缩写为DI)
依赖注入也是依赖倒置的一种具体实现,是类库设计的一种常用模式。类库基于依赖模式设计:调用者依赖于接口,而不是具体的实现,调用者在运行时被注入所依赖接口的具体实现。注入方法不在此处赘述。
- IOC框架
IOC框架包括了控制反转和依赖注入功能。以下通过QA的方式来读懂IOC框架:
Q: IOC的控制是对什么的控制?
A: 是对应用程序中对象的创建和生命周期的控制。
Q: 正向控制是什么样子?
A: 由应用程序new对象,并在合适的时候释放对象。
Q: 反转控制是什么样子?
A: 对象的创建和生命周期管理由IOC框架控制,而不是应用程序控制,这种情形与正向控制相反就叫做反转控制。
Q: 控制反转带来了什么好处?
A: 控制反转不仅减轻了应用程序的代码复杂性,更给IOC框架的扩展性带来好处,比如IOC框架可以实现对象单例模式,多例模式,引入事物管理,日志管理,安全管理等等功能,比如Spring产品家族。
Q: 依赖是什么?
A: 依赖包括:临时使用,关联,聚合,组合,依赖关系从左到右增强。可以参考UML相关概念描述。
Q: 注入的目的是什么?
A: 使得对象可以依赖抽象,而不是具体实现类。主要好处是让应用程对象之间松耦合。
2.3.2 典型注入框架
二、 水平方向架构
多层架构适合整体式程序,即一个程序实现了系统全部功能。随着企业规模越来越大,会面对不断的新需求和需求改动,在一个程序中进行扩展变得越来越难以进行。同时面对大并发的业务请求,程序在一个进程或者多个进程中运行都难以克服一个低效运行的功能代码造成的性能瓶颈。
人们找到了解决办法:将程序功能分割成服务程序,各服务程序在水平方向上并行开发和部署,由API连接各服务,这种方式使降低服务之间的耦合,部署更加灵活,性能调优可以针对独立的服务。
1. SOA架构
面向服务的体系结构(英语:service-oriented architecture)是构造分布式计算的应用程序的一种方法。它将应用程序功能作为服务提供给最终用户或者其他服务使用。
它采用开放标准、与软件资源进行交互并采用表示的标准方式。
1.1 简洁版架构
1.2 服务的基本要素
- Policy(策略)
服务提供者有时候会要求服务消费者与某种策略通信。比如要求消费者提供安全标识,才能获取某项服务。
- Endpoint(终结点)
终结点是一个描述提供或者接受服务的方式,包括网络协议,域名或者IP,端口信息。
- Contracts(合约)
服务合约是服务提供者(通常是远程的)和使用者(客户)之间使用合约语言(XML、JSON、Java Object等)约定数据输入和数据输出的一份协议。
- Messages(消息)
服务提供的消息,应满足服务的合约。
1.3 服务治理
服务治理SOA最大的话题,涵盖了以下内容:
- 服务定义(服务的范围、接口和边界)
- 服务部署生命周期(各个生命周期阶段)
- 服务版本治理(包括兼容性)
- 服务迁移(启用和退役)
- 服务注册中心(依赖关系)
- 服务消息模型(规范数据模型)
- 服务监视(进行问题确定)
- 服务所有权(企业组织)
- 服务测试(重复测试)
- 服务安全(包括可接受的保护范围)
为了解决以上问题,最为流行的做法在SOA架构中增加ESB(Enterprise Service Bus,即企业服务总线)集成。
2. 微服务架构
微服务是SOA的进化版本,把服务做到微型化。
2.1 简洁版架构
2.2 服务的基本要素
微服务架构下通过REST API提供提服务,所以不强调策略和合约,只要服务提供方暴露终结点和提供消息就足够了。
2.3 服务治理
SOA会遇到的治理问题,在微服务中一样会暴露出来。由于微服务系统中服务数量更多,生产环境中出现的各种问题更加突出。一部分人觉得微服务架构应该是去中心化,不需要ESB的中介作用,事实上为微服务的网关可以具备ESB的一些功能,比如安全检查/事物管理/复杂的工作流程等功能。
3. 整体式 vs SOA架构 vs 微服务架构
如果是小团队,中小项目用整体式开发更好;如果有大量或者难以重构的遗留代码建议采用SOA架构;比如是全新的项目且预计将来会快速迭代成大项目则建议用微服务架构。
4. SOA典型框架
5. 微服务典型框架
微服务思想重点在服务的微(自治,减少依赖)和治理(一个可靠安全的网关),它鼓励混合应用顺手的编程语言、开发框架,以减少高效开发与部署系统和软硬件环境之间的摩擦,比如企业内有java和.net开发团队则可以在Spring Cloud下用Asp.net core做平台开发微服务程序。
三、 总结
对于整体式程序或者一个服务作为整体式程序开发 ,可以采用洋葱模型架构。在处理数据时采用数据存取框架,对于UI可以采用服务器端MVC和客户端MVC框架。
对于具有遗留程序的系统可以采用SOA架构来整合系统功能;如果系统越来越复杂,负载压力在不同程序功能上不平衡可以采用微服务架构。在全新的即将成为大型应用的系统采用微服务架构也是不错的选择。