一、编程思想
1.面向对象(OOP)
业务模型的抽象,对各个领域的分区管理和中央调配。主要体现在数据表的设计,业务模型更细粒度的拆分。
例子:以中国各大城市为例。四川省作为一个服务,成都对应的是一个表。
2.面向接口
对象具备哪些行为的抽象。接口和实现进行分离,主要体现在业务接口的设计如入参和回参的数据结构,以及对外提供的接口服务。
例子:成都有让全国来看大熊猫的功能行为。
3.面向切面(AOP)
不影响主流程的增强操作,如日志打印,参数校验,读写分离,事务控制等
4.面向服务(rpc和mq)
服务与服务之间的隔离,通过rpc或者mq等方式进行接口调用或者消息通信。
关键在于架构师对服务进行粗细粒度的拆分
5.回调思想
自己不去实现具体逻辑,中间件去调用它定义的接口,而接口具体的逻辑由业务方自己去实现。
这种回调的思想相比于自己沦陷去检查,效率是有明显提升的。
6.结构化组合编程
在设计上,有一个通用的原则叫做:组合优于继承。也就是说,如果一个方案既能用组合实现,也能用继承实现,那就选择用组合实现。
类是由这些一个一个的小模块组合而成,这种编程的方式就是面向组合编程。它相当于换了一个视角:类是由多个小模块组合而成。
代码的结构层次清晰,业务逻辑宛如流程图中的一个个节点。
7.DDD领域驱动设计
根据领域区分边界,降低架构的复杂度
二、代码规范
1.日志规范
1.调用的客户端三方服务一定要打日志,服务端最好也打印对应日志。且入参和返回结果是必须打的,否则线上排查相关问题相当困难。可以通过切面统一实现,并存储于数据库中,能有相对复杂的语句能查询出来,并且一般建议只保留近7天的日志。
2.日志的error级别日志单独分割,
3.日志级别默认为info。
4.日志必须按天分割。
2.异常处理规范
1.调用第三方服务一定要有降级处理,然后异常必须进行捕获而不是直接抛给上层处理,可以统一进行降级拦截。
2.对于http接口,rpc接口,异常打印一定要打印入参,否则你把调用错误反馈给其他部门,其他部门通过什么进行查询,最后还是得找到自己部门。
3.对于方法级别的异常,也要打印方法的入参,便于排查问题
三、架构
3.1 业务架构
1.业务流程,数据模型,服务模块,结构构造
3.2 技术架构
1.服务拆分,存储模型,中间件选型
2.高并发,高可用,高性能,数据一致性,锁
3.QPS,TPS
3.3 部门划分
用户平台
直接面向用户使用的平台,如终端:安卓,iOS,web及对应后端支持。
运营平台
面向后台运营人员,一般为xx管理平台。
基础架构
底层核心公有功能下沉抽象,如各种common包,sdk等。
1.中间件的自研或扩展。如消息中间件,缓存中间件,rpc中间件dubbo等
2.基础云平台的开发:阿里云,腾讯云等。
3.公有技术封装:如分布式锁
4.代码生成器开发:如根据数据表生成对应的mapper,dao,entity,service,controller等。
数据平台
包含DB部门,大数据部门等。
交易平台
包含订单,支付等。
业务中台
下沉的公有业务平台,所属为业务领域为上层业务赋能。一般有以下平台:
1.积分业务中台
2.im消息中台。包含消息推送,短信,聊天通信等
3.用户基础数据中台。包含:组织架构,用户基础信息等
4.权限中台。包含功能权限,数据权限等。
5.sso单点登录。
运维部门
包含服务部署,基础网路搭建,开发部署客户端平台等
common包需包含的核心
1.前后端返回数据结构统一定义,包含错误码等
2.分页数据结构定义
3.json工具抽象下沉,各业务部门可直接使用,无需单独开发json工具类。
4.接口参数校验框架
设计总结
1.4统计和数据分析
实时数据和离线数据统计思考
对接第三方服务架构设计
以调用百度地图为例
1.单独异常包围,并设置超时机制不影响主流程业务,发生异常时单独开启线程去补偿重试。
2.引入缓存扛并发,避免多次调用增加问题出现的概率。
异步流程设计
以商品下单发送mq消息或者短信为列
1.本地事务提交之后,才能发送mq消息。否则你想一想,你发送了消息给其他业务方,其他业务用你的订单ID查询明细,
结果你都还没有入库或者事务执行失败,那整个流程就会存在严重问题。
2.发送的消息不能和本地在一个事务。否则你想一想,发送的消息超时了,本地事务一直没有提交,那么线程将会在事务中不会释放,直到耗尽线程池资源。
交接流程
1.页面地址功能
2.服务架构部署关系
3.涉及的存储
4.异步流程如mq,job等
5.测试/线上环境部署和日志查询位置
参数校验
一般的参数校验是由前端作为第一道防线进行校验,提示更加友好,而后端作为最后一道防线,校验的提示时间不允许的情况下可以相对通用一些。
1.自定义校验
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
/**
* bean对象属性验证
*
* @author ruoyi
*/
public class BeanValidators {
public static void validateWithException(Validator validator, Object object, Class<?>... groups)
throws ConstraintViolationException {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
}
public static void validateListWithException(Validator validator, List<? extends Object> objects, Class<?>... groups)
throws ConstraintViolationException {
if (CollectionUtil.isEmpty(objects)) {
return;
}
for (Object object : objects) {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
}
}
}
2.@Validated注解校验
@ApiOperation(value = "养殖池口管理-开池")
@PostMapping(value = "openBreedPool")
@Log(title = "养殖池口管理-开池", businessType = BusinessType.INSERT)
public ApiResult<Boolean> openBreedPool(@Validated @RequestBody BreedPoolOpenRequest req) {
return breedPoolService.openBreedPool(req);
}