注意,这是个人架构实践心得的第二季的系列文章,第一季有10篇你也能够回顾。 见https://www.cnblogs.com/lovecindywang/category/1296779.htmlhtml
最近我一直在思考几个问题:前端
其实,这几个问题或多或少是相互关联的。有的时候你们也会自嘲说,“程序员接手的代码永远是烂摊子,而后本身继续在这个烂摊子上产出代码,留给又一波后人接手”。十几年来经历过十来个公司,我看了很多差的代码,也看了很多好的代码,本身产出过垃圾代码,也带领团队实现过一些自认为不错的代码。你可能会说,业务代码就是增删改查,和框架代码的难度不能比,彻底是机械劳动,其实我以为不彻底是这样,甚至彻底不是这样,我我的认为写出能跑的业务代码不难,但要写出好的业务代码实际上是挺难的,更重要的是若是系统设计的足够好,在很长一段时间内系统的可维护性是可控的,只须要简单扩展便可,若是基础打的不够好,那么项目可能就是一次性项目,下面我列出业务系统我关注的一些点,你想一想是否是有道理。java
我本身很是注重搭建项目结构的起步过程,模块的划分、目录(包)的命名,我以为很是重要,若是作的足够好,别人导入项目后可能只须要10分钟就能够大概了解结构了。程序员
一、有些名词是约定俗成的,你们一眼就能看出是啥东西的,好比:面试
比较重要的是肯定先进行分类再分业务,仍是先分业务再分类,在代码里混用这两种风格的结构就会很混乱:数据库
对于直筒的三层架构的纯数据表驱动的代码我建议第一层是分类,第二层是业务功能:后端
对于有一些项目,不必定每个逻辑都涉及到三层架构,数据流量比较复杂,我建议是按照业务功能先来分,下一层视状况也不必定彻底是须要按照组件类型分二级目录,也能够是按功能来分:设计模式
对于这种目录结构一眼望去就能知道大概项目数据流和架构了,core对内,dispatcher作分发的感受,callback是外部来的回调数据,notification是通知外部的数据流。这种数据流向复杂的项目,使用这种结构会比前一种合理的多,由于咱们须要先关注数据流,而不是三层结构的层次,甚至对于core、dispatcher、notification咱们知道实际上是没有controller的。api
二、有些名词可能就须要内部有一个统一,好比不一样的层次面向数据库,面向业务,面向UI,面向RPC须要有不一样的POJO,咱们须要明确一套对应的命名,能明确就好,好比下面的这些POJO咱们其实挺难分辨其用途的,须要进行规范,而且放置于匹配的目录结构中:缓存
咱们能够约定第一组用于服务自己访问外部(的Rpc服务也好,REST服务也好),第二组用于服务自己对外提供的Web Api,好比:
OrderController
QueryOrderParam
QueryOrderDto
CreateOrderParam
CreateOrderDto
LoginRequest
LoginResponse
RegisterRequest
RegisterResponse
OrderService
OrderServiceImpl
OrderEntity
OrderMapper
OrderModel
总之,虽然可能10+人在维护相同的项目,目录结构的风格、命名、专用名词的使用必定要统一。
这个须要在开展项目以前明确下来,我见过有项目中同时使用了Spring MVC和Jersey作Web API,同时使用了Spring Scheduler和Quartz作任务调度。最好是项目开展前明确框架的版本而且搭建好项目脚手架,大概涉及:
固然,咱们也能够独立出依赖管理的项目,专门由独立模块进行依赖版本管理。最差也要在Wiki上进行明确。
若是项目涉及到对外提供API,那么很是有必要在初期就规范API的框架定义,涉及到:
Result<T>
的定义(见过一个项目用了三种包装类的)以及遇到错误的状况下,Http状态码的体现public class ApiResult<T> { boolean success; String code; String message; String path; long time; T data; }
咱们能够这么和客户端的开发来明确:
一、即便遇到错误,Http状态码仍是200,Http状态码若是是500或是404的话那必定是网关层面的错误了,这个错误不是后端服务返回的
二、在Http状态码仍是200的时候表明收到了后端的返回,前端去按照ApiResult以Json格式反序列化Http Body的报文
三、而后查看success(若是没success也行,咱们能够约定code是200就是成功),若是是success表明后端服务成功处理了请求,若是不成功,则根据后端给的错误代码映射表根据code进行处理或直接提示message中的内容。注意,这里的success只表明后端是否成功处理了请求,不表明请求表明的业务逻辑是否成功处理。举一个例子,若是这个请求是异步支付请求,那么success==true表明前端给的参数都正确,后端正确接受了支付申请,不表明支付成功
四、在success==true的状况下再去解析data中的内容,拿取客户端须要的信息,仍是前面的例子,data里能够是{"orderStatus":"PROCESSING", "orderId":"1234"}
,这个才是真正业务逻辑的数据和状态,success并不表明订单支付操做就是成功的,也多是处理中的状态
因此这是几个层次的事情,Http Status->ApiResult.status->ApiResult.data.orderStatus
加解密规范和签名规范
Api的加密解密以及签名最好在设计的时候就考虑进去,并且要仔细斟酌,不然之后很难变动,特别Api的使用方是客户端的状况,客户端很难轻易强更。若是作SAAS服务,建议参考大厂的规范,好比亚马逊AWS的API规范或阿里云API的规范,不建议本身造轮子,大厂作的API规范都是通过安全方面的专家深度思考的。
版本管理规范(好比Url path路由仍是Http header路由)
若是使用了老版本的话,是否须要在返回内容中提示新版本的Url、版本号、老版本最后维护时间呢?这个就不展开了
因此统一Api这个事情不只仅是Api的格式还涉及到安全处理、版本处理、客户端操做方式等等。对于一些须要服务端驱动客户端的业务(UI逻辑动态)来讲,咱们能够定义一套更复杂的ApiResult,让服务端告知客户端这个时候应该是提示仍是跳转仍是返回等等。
如今你们都使用Git,分支如何管理每个公司(在Gitflow的基础上)都会略有不一样,也须要和你们明确:
别小这个,虽然这个和代码质量和架构无关,可是梳理清楚能够:
见过一些项目在实现业务代码的时候是不考虑任何异常处理、事务处理、锁处理的,在流量小无并发的状况下,这些项目不容易爆发出严重的问题,基本能用。可是对于高并发的项目或未来可能会高速发展的项目,若是不考虑这些问题会死的很难看。
咱们来想一下,若是如今在设计一个订单服务,若是由于网络问题、并发问题致使数据错乱、服务中断的可能在千分之一,若是一个业务一天只有1000次请求,1天才遇到这样1次问题,即便遇到了问题用户也不必定会来反馈,即便来反馈每每客服也能经过后台取消订单等操做来处理,这个问题不会爆发出来,若是一天的单量是1000万,那么天天可能就会有10000单异常的订单,这个可能就超过了客服的处理能力了。
不多有项目真正100%彻底作好了全部细节,只不过每每是由于量小得不到你们的重视罢了。但咱们想一下,若是遇到数据库或中间件级别大规模故障的状况下,若是一致性处理的很差,那么数据库恢复后可能会留下一大堆异常数据须要修复,若是处理的好,业务数据不会错乱,数据库恢复后业务立刻能够恢复。在遇到事故的时候,系统这方面的设计功力就体现出来了。
在实现代码的时候须要考虑以下事情:
@Transactional
,可是方法内部彻底catch了全部异常的状况
@Transactional
是否真正生效了?这些要作好真的很难,每一步都须要认证考虑,可是很遗憾见过的不少具备复杂业务的代码,在Service中一连串调用了N个外部服务进行写操做,方法内也实现了N个表的写操做,即不考虑外部服务的事务和补偿问题,本地也没有事务控制,出了错只是打印了堆栈而后客户端看到的是一个服务器忙。
异常处理不只仅是狭义上遇到了Exception怎么去处理,还有各类业务逻辑遇到错误的时候咱们怎么去处理。
就拿记日志这一件事情来讲:
ClassicConverter
咱们知道catch到了异常或遇到了业务错误,咱们除了记录日志还有不少选择,也须要认真考虑何时应该作什么:
这又涉及到了弹力设计的话题,咱们的系统每每会对接各类外部服务、Api,大部分服务都不会有SLA,即便有在大并发下咱们也须要考虑外部服务不可用对本身的影响,会不会把本身拖死。咱们老是但愿:
表的设计和Api的定义相似,属于那种开头没有开好,之后改变须要花10x代价的,我知道,一开始在业务不明确的状况下,设计出良好的一步到位的表结构很困难,可是至少咱们能够作的是有一个好的标准:
[domain]_[tablename]_[tabletype]
除了标准,尽量须要结合业务以及业务可能的扩展思考一下:
对于表结构文档,我以为列出字段类型、长度、说明是不够的,若是能结合业务代码梳理清楚下面这些,那这个文档就是真正有价值的表结构文档:
我想90%的业务项目都是所谓的三层结构,Web层处理参数调用Service层作Db层的聚合,Db层基本就是代码生成或Orm框架补充少许的手写SQL。对于这样的项目,大部分人认为是没有设计的,也不须要设计。我认为那是由于没有好好思考:
说白了就是利用各类设计模式和OO思想,来尽量在业务变化须要扩展的时候:
在一个公司层面,若是有几十个,几百个业务项目,咱们看这个公司的技术水平到了什么程度,我我的认为不只仅是用了什么新技术,而是是否:
最简单的一个例子,一个业务从前到后跨10个事业部,100个服务,实现灰度测试,想一想这件事情有多难?整个公司层面要实现步调一致的这些东西还确实很难,不只仅是技术能力的体现,没有良好的组织架构,人心不齐,恐怕这些没法实现,实现了也没法推广,推广了也没法持续……固然,这些已经超出我的能作的了,做为程序员的咱们应该从我作起,认真考虑前面提到的这些问题,至少在项目内部作良好的设计。
再来看看文首的问题,你看,虽然只是写业务代码,若是要写的足够好,必需要了解设计模式、理解各类弹力设计、理解事务、熟悉框架、了解中间件原理,怎么可能学不到东西,要实现健壮的业务代码,其实很难,要考虑的东西太多了,若是说写框架咱们须要考虑不一样的使用方和使用环境,这很难,写业务代码咱们要考虑到千奇百怪的使用行为,要考虑到层次不起的对接方,这不比写框架简单。对于5年+经验丰富的程序员应当有能力开一个好头,或者说愿意在老代码上去作一些改变,不然你的价值在哪里呢?
本文只是展开了一些想到的内容,每一点都有不少东西能够写,也没时间一些子展开说太多,这些细节留在从此的文章慢慢展开了。