构建一个较为通用的业务技术架构

一、通用架构概述

创业之初,咱们每每会为了快速迭代出产品,而选择最简单的技术架构,好比LAMP架构,SSH三层架构。这些架构能够适应初期业务的快速发展,可是,随着业务变得愈来愈复杂,咱们会发现这些架构愈来愈难支撑业务的发展,出如今一个类中写好几千行代码,一个方法中处处都是if else语句,若是中间遇到主程序猿离职,后面介入的程序猿几乎没法理解这些代码,到最后,产品愈来愈难迭代,只能推翻重作。若是咱们在创业初始就以一种适应性较强的架构去写代码,后面就会少走不少弯路。下面的文章是我本身总结出来的一套架构,通过实践,适应性还算不错。html

二、通用架构实现

总的来讲个人通用架构仍是以三层架构为基础进行演变的,在经典的三层架构中,最上层的是controller,中间是service,下层是dao。在个人架构中,最上层是网关层,controller只是网关的一种,中间是业务层,service只是业务层的入口,最下层是基础层,dao只是基础层中的数据存储组件。redis

2.一、网关层

网关层本质上是对不一样的网络协议的请求进行处理,好比HTTP协议,TCP协议,固然,也能够对其余协议进行处理。具体见下图:spring

2.1.一、HTTP请求

通常来自PC端和APP端的请求都是基于HTTP协议的,对于处理HTTP请求的方案,业内已经很是成熟了。首先,tomcat容器自己已经把HTTP请求处理的复杂性封装掉了,其次,spring mvc对请求处理提供了RESTful风格的编码方式,大大下降了开发的复杂度。咱们要作的就是对controller按照业务领域划分,好比按照订单、会员去划分大的领域,里面的各类方法就是这个领域内的操做。这里的controller就是统一网关处理层,对于每一个controller的方法只作三件事,第一,将请求参数解析出来并组装成内部参数,第二调用下层服务执行业务逻辑,第三组装返回结果,对于异常状况,须要记录异常堆栈日志并转换错误码,堆栈信息不要暴露到调用方。数据库

2.1.二、TCP请求

对于处理TCP请求的方案,业内也已经很成熟了,好比Netty。可是,TCP请求毕竟太底层,咱们每每会基于TCP协议去开发本身的协议。另外,不少分布式框架都是基于TCP协议的,好比RPC框架Dubbo,消息框架RocketMQ等等。从单机系统到分布式系统,无非就是网关层多了处理TCP请求的逻辑,理论上底层的业务是无需感知本身究竟是出于单机环境仍是分布式环境,网关层的做用就是要屏蔽这种不一样外部调用源的细节。在Dubbo服务端中,咱们须要实现远程接口,并对远程服务调用进行内部的转发,转发的逻辑也很简单,首先是解析参数并组装内部参数,而后调用业务层的接口执行业务逻辑,最后组装返回结果,对于异常处理也须要在这里作掉,防止异常暴露给外部应用。编程

2.1.三、小结

网关层本质是对协议进行处理,同时将业务逻辑收敛到网关层,而不是暴露给外部,当内部业务逻辑进行重构的时候,外部调用方就不须要感知这些变化,当外部调用源增长时,内部业务逻辑不须要感知这种变化,从而将外部调用方和内部业务逻辑进行了解耦。数组

2.二、业务层

业务层是一个系统,不管是单机系统仍是分布式系统群中的某个业务系统,业务层都是承载业务流程和规则的地方。业务层从外到内包含三层:第一层是业务服务,第二层是业务流程,第三层是业务组件。具体以下图:缓存

2.2.一、业务服务

业务服务是业务层对外的统一门面,它由三方面组成:业务接口、入参、出参。tomcat

a) 业务接口网络

一个业务接口表明一个领域的业务服务,好比订单域的业务服务就由接口OrderService表示,会员域的业务服务就由接口MemberService表示。接口能够按照执行性质分为读接口和写接口,好比OrderReadService和OrderWriteService。读写分离的好处是能够对集群进行读写分组,从而管理流量,固然,单机系统读写分离意义不是太大。领域内的操做则以业务接口中的方法的形式体现,好比订单域有下单createOrder,取消订单cancelOrder等等操做。对于这些操做,尽可能设计出有业务含义的方法,而不是增删改查,固然,对于一些简单的业务,也只能增删改查。架构

b)入参

接下来,是入参的设计。入参对于读方法,比较简单,不作讨论。对于写方法,咱们将入参设计成有层次的数据模型。首先须要设计出公共的数据模型,好比订单数据模型,商家数据模型,商品数据模型等,而后将这些数据模型和一些特定业务下的个性数据结合,组成Request对象,这个request对象按照不一样业务操做不一样而不一样,对应的返回结果就是response,它也是随着不一样业务返回的参数不一样。

举个例子,拿下餐饮订单来讲,首先,咱们应该识别出这些业务流程中一些比较基础的数据模型,好比餐饮领域的菜品、桌位等,这些模型之因此说是基础模型,是由于,无论下什么餐饮订单,菜品和桌位确定是逃不了的,它们是能够被复用的!所以,咱们分别为这些基础模型设计相对于的DO(Domian Object):DishDO(菜品)、BoardDO(桌位)等等,接下来,咱们为下餐饮订单设计一个请求对象DishOrderCreateRequest其中DishOrderCreateRequest内部包含了DishDO和BoardDO,另外会包含一些特定的属性,好比人数啊,折扣啊等等,这样一来就能作到通用和灵活兼顾,DishOrderCreateRequest表明的个性化的灵活的业务入参,而DishDO和BoardDO等则表明了不易变化的基础模型。

c) 出参

最后,是出参的设计。对于写方法,通常出参比较简单。对于读方法,出参每每是一个结构与层次比较复杂的组合对象。好比查询一个订单,这个订单有订单基本信息,还有商品信息,收货人地址信息等。在设计出参的时候,结构上要设计成组合对象,可是真正查询的时候,经过查询选择器,去查询不一样的组合对象。好比查询选择器设置商品查询为true,地址查询为false,那么此次查询出的订单就只包含商品,而不包含地址。

2.2.二、业务流程

业务流程其实就是对业务规则的解释,只是这种解释使用代码去实现的,咱们要作的其实就是准确翻译这些业务规则,并维护好这些业务规则。

业务流程中能够大体分为三种动做节点,一、组装参数节点  二、规则判断节点  三、执行动做节点,其中每一个动做节点都是一些业务代码的片断。举个例子,下餐饮订单,咱们第一步就是将上层传入的参数组装出一个基础的DishOrderDO(组装参数节点),而后按照特定的规则去填充这个DishOrderDO(规则判断节点),而后就是调用DAO去建立DishOrderDO(执行动做节点)。

业务流程是最容易变化的地方,要想维护好业务流程并不容易,总的思想是将大的业务流程拆分红小的业务流程,抽出每一个业务流程中共有的代码片断,变成可维护的业务组件。

2.2.二、业务组件

a) 基础组件

业务组件实际上是将一些内聚的可复用的代码片断进行封装。和业务流程中的三种业务节点相对应,业务组件也分为三种:组装参数组件 、规则判断组件 、动做执行业务组件。业务组件的抽象每每是对业务有了深入理解以后才进行的,盲目地进行业务组件的抽象,每每到头来白忙活。

b) 能力

对业务组件进行进一步抽象,能够获得能力。业务能力是具备必定复用性的组件的组合,好比发短信能力=组装短信参数组件+发短信组件。对于发短信能力,能够被不一样的业务流程复用,好比订单下单成功发短信,支付成功发短信,逻辑都是类似的,只有内容不一样。能力是一种粒度比较大的组件,粒度越大,每每复用性就越小,对能力的抽取,也是基于对特定业务深入的理解,没有一劳永逸的银弹。

c)更高纬度的抽象

通过本人的实践,对于互联网这样的需求变化极快的场景,更高纬度的组件抽象每每性价比很低,不建议你们去作。

2.三、基础层

基础层包含两个部分,第一是接口定义,第二是技术组件。

2.3.一、接口定义

接口定义是按照不一样的技术框架,同时结合业务须要,设计出合理的接口,对于业务组件来讲,它们只会感知技术接口,而不会去感知技术实现,咱们也不该该将具体的技术细节向上暴露,这也就是所谓的面向接口编程。技术接口每每是业务与技术之间的桥梁,接口自己是含有业务含义的,最多见的就是DAO接口,咱们设计DAO接口的时候,不会设计成insert、update、query这样业务无关的接口,而是设计成insertUser,updateUserById等等和业务相关的接口,一样的道理,设计缓存接口的时候,也不能设计成put、get这样的接口,而应该设计成cacheUser,deprecateUser这样的接口。

2.3.二、技术组件

单机系统的技术组件通常来讲分两种,一种是通用的技术组件,好比:数据存储、缓存、消息和调度任务、事务、锁。一种是基础设施,好比spring容器,tomcat容器。下面稍微谈谈通用技术组件。

数据存储:数据存储包括关系型数据库、非关系型数据库以及文件存储系统。关系型数据库,好比MySQL,适合存放绝大部分业务数据。非关系型数据库,好比hbase,能够存放历史日志,也能够对历史的MySQL数据进行归档。文件存储系统,通常都是基于Linux文件系统,好比图片、html文件等等,也有基于HDFS的,用于大数据分析。

缓存:缓存按响应时间分,能够分为纳秒级缓存,毫秒级缓存和百毫秒级缓存。纳秒级缓存就是通常的基于本地内存的缓存,好比encache,毫秒级缓存通常是集中式的内存缓存,好比memcache,因为访问时远程调用,所以响应时间会延长到几毫秒,百毫秒级缓存通常是集中式可持久化的缓存,好比redis,因为存在远程访问以及缓存击穿致使的读取持久化记录,它的响应时间会更长些,到几十甚至上百毫秒。单机系统通常用本地内存缓存就够了,当缓存被击穿的时候,直接访问数据库。

消息和调度任务:消息和调度任务本质都是一种异步化的手段,区别在于消息没法控制异步的时间,而调度任务能够。通常,消息发送出去后,监听消息的系统会当即收到消息,从而当即触发业务逻辑的执行,而调度任务则会按照调度规则,一次或者屡次的执行业务逻辑。单机系统中消息和调度任务用到的比较少,在作日志监控的时候可能会用到消息,在进行数据报表统计的时候可能会用到调度任务。

事务:事务本质都是基于数据库去实现的,单机系统的事务就是依赖数据库的事务,咱们可使用spring-tx的事务模板进行事务操做,在业务逻辑开发中,必定要把握事务的大小,建议把业务比较紧密的一堆数据库操做放在一个事务里,不要随意的为每一个方法都开启事务。

锁:单机系统中主要用到两种锁:乐观锁和悲观锁。乐观锁依靠在数据库的业务表加版本字段来实现,每次更新都会去判断版本是否变化,若是变化则须要重试,这种锁的粒度比较小。悲观锁是基于JDK的Lock接口的,对一个业务流程进行加锁和释放锁的操做,锁的粒度比较粗。

三、总结

以上是我通过很长一段时间的实践后摸索出来的业务技术架构,自认为还算通用,并且可以在必定程度上支撑易变的业务。固然这套架构确定不是银弹,不可能解决全部业务场景,因此最终仍是须要围绕到具体的场景加以借鉴。

关于做者

吴极心,目前在杭州旅居星球担任架构师,专一于技术架构治和产品架构。

相关文章
相关标签/搜索