⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 cnblogs.com/jurendage/p/12496101.html 「jurendage」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

说明:生鲜电商属于一个软件的产品,那么如何做好代码设计呢?代码设计,是程序员做项目时,在coding之前非常重要的一个步骤,可以说关系到整个系统、整个团队的研发质量和效率。一般说代码设计,可能涵盖以下几种:

1、整体设计 2、架构设计 3、领域模型设计 4、数据库设计 5、API设计 6、代码实现设计

代码设计的前提是,项目组成员已经完成正式的需求评审,并经过充分思考: 1、这个需求是为什么业务目标服务的? 2、这个需求描述的内容,是否为服务该目标最合适的方式(包括研发性价比、项目周期等)? 3、prd本身是否逻辑自洽? 4、是否每个内容点都可实现? 5、实现的技术方案是什么? 6、是否做过类似的功能,合并吗?复用吗?拆解吗?

整体设计与架构设计

项目的整体设计,有时会涵盖系统架构设计,这里要区分一下,系统架构设计并不完全等同于代码架构设计。

整体设计首先要考虑的,是当前项目是要做一个全新的项目,还是要做原有项目基础上改造、迭代;项目组的积累中,是否有可以复用的地方(模块或成熟方案),是否有可以通过改造以符合新项目需求的可能。

其次再考虑,如果是新起项目,要如何搭建整体实施方案,内容一般包括:

1、硬件部署与资源申请:硬件和资源,是要和业务需求结合来制定的,比如业务最大访问、TPS/QPS等,要切实讨论得出一个数据范围,以确定系统是否做高并发方案。另外还要考虑容灾等问题,以此制定系统高可用的设计。

2、分析项目特别突出的业务、技术难点:如千人千面的UI和查询,或灵活配置的业务模式,类似这种需求的项目,会在模块模型设计上做额外处理,可能是将各种规则单独做一层规则引擎,也可能是在数据建模时增加更多维度;再比如超大的QPS,可能要在整体架构上,添加额外的中间层,异步收集数据等功能;还有就是依赖于审核的迭代上线周期(IOS、微信小程序)等,都要在整体设计中考虑进去。

3、内外部信息交换通讯:整体设计系统,要明确划定项目范围,哪些是本系统的功能,哪些功能、数据依赖其它系统或模块。要明确此次项目对外交互系统的访问关系和访问条件。

4、数据的持久化存储方案,如何选择硬件,如何选择软件,一般有常规解决方案。特别要考虑和需求结合的一点是业务数据的生命周期,这也是数据归档方案等重要依据。

整体设计中非常重要的一部分是系统架构设计,要在业务边界确定的前提下进行。

首先,从业务视角进行拆解功能,定义系统由哪些模块构成。

其次,根据业务复杂度、模块功能性划分,选择合理的软件架构模式及方案。

比如,业务需求上,有非常复杂的流程处理,但各个业务子模块之间相对独立,则可以选择事件驱动型架构,基本上用状态机、工作流就可以满足功能了;再比如做微服务架构设计上,也不一定都做集中式负载均衡,如果整体功能需求对消息流转要求比较高,则可以考虑中心消息型服务(比如Rocketmq)。

以上两步更多是从需求上分析,偏近于从业务角度设计系统。后面两步要从技术上做架构设计。

下一步要根据模块划分和架构方案选择合适的开发语言和技术框架。生产上一般考虑优选Java语言+Spring框架,但也有许多混搭的成熟方案,有些基于JVM的开源框架中对多语言的支持还是比较好的。

再下一步要将系统拆解,一般考虑划分出展现层、(通讯层)服务层、数据层,再根据需求为每一层设计合理的服务及组件。如展现层上web还是mobile,服务层是用Spring Boot还是Spring Cloud做微服务,数据层使用Mysql还是Oracle等。另外还有项目服务等通用组件及各种业务、技术中间件、及整体项目通用的技术方案选型,如日志(ELK等)、监控告警,访问授权,token认证等。

整体设计要在需求明确(不一定所有细节完全敲定)的前提下尽心,一般形成定案要在正式需求评审会之后。

整体设计由团队leader或项目owner完成。

整体设计文档,优先考虑使用物理部署图、逻辑架构图、业务流程图等描述系统架构。

另:特别建议在需求评审会后,首先由研发lead组织召开设计讨论会议。目的是让项目成员尽早的参与到项目之中,并使用讨论到方式,更好的理解项目及整体设计方案。

一般设计讨论可用头脑风暴方式进行会议。有重大分歧时,可以投票表决。

此讨论会议召开前,需要leader提前设定议题,会议上设有1名主持人(可leader兼任或者team member轮流担任),按照议题集中,轮流发言再集体讨论方式得出结论。

主要议题包括:整体设计方向、项目人员大致分工、待解决疑问和处理人。

领域模型设计与数据库设计

在整体架构设计完成后,要针对已经拆分的系统模块做模型设计,尤其是在项目需求中有重要功能的部分要重点设计。

领域建模要深度结合需求,从业务角度出发,用一种自顶而下的方式来建立模型。

领域建模方法很多,最重要的原则是在项目模型建立之前,要先做概念的统一整理,要对特定概念的名词做专业命名及能力约束。在此基础上,再进行重叠功能的归并和抽象。需要注意的是,此处制定的模型,和业务需求、数据库设计、代码类设计,都是一脉相承的,但并不完全等同。比如需求中有订单Order的概念,在设计订单Order都有哪些元素构成,可以实现什么操作时会发现,要在数据库和代码中,拆分为OrderHeader和OrderDetail。

在领域模型中,还有一个重点,是要标注清楚各抽象概念之间的数据关系和约束。一般会比较关注数据之间是一对一、一对多、多对多等关系,并在此基础上,结合业务流程泳道做系统模块依赖关系图、数据流图等。

数据库一般结合领域模型设计,是领域模型持久化存储的映射转化(ORMapping)。在项目数据库设计中,除了常规关注的范式、mysql约束等(单独写过mysql应用的usage,此文略),额外关注:

1、表字段在整体系统中的规范定义和统一使用。比如相同的概念,在各个表字段中的定义要一致,(在代码应用中也应保持一致)。

2、数据库事务的应用方案。

3、冗余字段与代码简洁实用的平衡。

4、特别说明,数据库一般仅作数据的持久化存储,不要将业务逻辑的处理放到数据库中进行。

5、生产业务数据delete,应该仅作逻辑删除,不做物理删除。

6、表设计要和性能结合起来,特别当DB成为性能瓶颈时,要特别斟酌索引设计的合理性。

模型设计一般为团队leader或项目owner完成,数据库设计一般为leader带领team member一起完成。

数据库设计一般要先于具体功能代码实现,在做此设计后,要针对存储方案和底层数据结构设计,做double check和集中评审,评审内容包括存储介质选型、表结构设计能否满足技术方案、存取性能和存储空间能否满足业务发展、表或字段之间的辩证关系、字段名称、字段类型、索引等。在评审一致通过后,沉淀为文档。

API设计与代码实现设计

+ View Code

API是系统模块对外提供的服务,现行系统基本满足前后端分离的框架使用条件,所以一般可以简单理解为前端和系统交互的入口。但实际系统设计中,API不仅仅提供给前端使用,所以每个API的实现设计是系统模块设计中非常重要的环节。

API的调用方一般会考虑给展示层多端调用(web、mobile等),还要考虑相同的API是否可以给其它系统模块使用,最后一层设计是,是否用相同的API对外提供openAPI服务。设计中应当特别考虑此类API的功能聚合、分离,多场景适用性等。以订单列表查询为例,设计一个订单列表query接口:

1、给前端页面查询,前端分页每次传参。这个场景最大的特点是,查询页面会设计较多分类查询的筛选条件,且此类设计实现经常可能是查询条件直接投射到DB上。

2、在整体系统中,给其它模块使用,如用户模块或报表模块。这个场景的特点是,如果其它模块功能为同步调用,则QPS不可预测,比如用户模块使用了这个订单query接口,那么这个接口的性能就会成为用户模块的瓶颈。还有一种可能是其它模块用此接口异步访问同步数据,就很有可能采用定时任务方式,固定分页,并发调用查询,如每5分钟调用一次,每次调用并发20,每个访问查询500分页数据。

3、给openAPI使用。如果开放给前端的query服务提供给开放平台直接使用或包装后直接访问,则容易出现的场景是,每次调用查询不确定分页,很有可能一个大分页(如十万)就打到DB上,这样即使索引匹配也容易造成数据库缓存区拥堵。 遇到这类需求,1、要考虑一个API接口是否可以满足所有需求,是否对数据访问做权限隔离。即,考虑所有的服务都集中到一个API上,还是定向拆分,将一个内部实现core,分别投射到多个API上。2、不同访问端如果有不同的QPS需求,还都考虑到,单个特大QPS接口,可以横向合并,即,不根据业务约束,而是把所有大访问的接口拆出来,给到单独技术架构和硬件部署的服务里。3、是否内部实现上一致,是否使用缓存、中间层方案等。

4、数据库设计尤其是索引设计要和接口设计(尤其是筛选条件)保持一致。

API的设计还有一个维度的考虑,是基于数据交互考虑。当两个系统模块要使用API交互数据时,定义的API要充分考虑使用场景,并做技术选型:

1、数据是推还是拉?

2、同步推送还是异步通知回调?

3、通过接口还是MQ?是否需要削峰?

4、是否需要保证强一致性?

5、crontab定时发起还是任务队列发起?需要延时队列、死信队列吗?

API拆分完成后,要做代码实现设计。此设计主要关注每个API的内部实现,将一系列领域模型转换为系统对象的类设计。这里面有3个图可以辅助设计:

1、如果一个API复杂度较高,调用链路上的涉及对象较多,可以使用时序图来表达并且明确各调用环节的输入与输出,以反映对象间的交互与协作关系。时序图对完成设计评审、辅助项目开发有很大作用。

2、如果某个业务对象的状态较多,可以使用状态图来表达并且明确状态变化的各个触发条件。首先明确对象有多少种状态,然后明确两两状态之间是否存在直接转换关系,再明确触发状态转换的条件是什么。状态图对测试用例有很大帮助。

3、如果系统中模型类超过较多,且存在复杂的依赖关系,可以使用类图来表达并且明确类之间的关系。类图对复杂系统设计,尤其是灵活配置、路由映射、设计模式应用等,有一定帮助。

类的设计要充分考虑单一原则。应当优先使用聚合/组合的方式来实现。不得已使用继承的话,要使父类能够出现的地方子类一定能够出现。根据依赖倒置原则,尽量依赖抽象类与接口,有利于系统的扩展与维护。

在设计抽象时,要考虑以下问题:代码直观吗(好的代码自注释性很强),它的编写巧妙吗?实现细节可能隐去了吗?程序编写是立足于问题域而不是计算机科学或语言结构域吗?

程序开发有一个场景比较典型,写第一版需求时,仅仅是一个简单功能,实现也比较简单,但后续功能增加很多,变化很大,每次在原有类定义基础上增加功能,倒置代码冗余,尤其容易造成if-else太多。此时要考虑提前预估功能,做扩展性设计,或者在每次功能迭代中,做小版本重构。比如订单明细查询,在定义查询接口(interface)后,需求要增加一个千人千面功能,不同用户访问返回的内容条目不一样。如果用if-else或switch写,会比较不好管理,代码也容易混乱,这里可以新设计一个接口,做不同内容配置,然后组合使用,或者采用其它设计模式。

设计模式的目的,是辅助程序员更好的实现代码抽象,将现实业务逻辑,映射到抽象维度的代码语言上。一般生产上经常用到工厂抽象工厂、模板方法、策略、状态等。选择合适的设计模式和数据结构,有助于提升代码的清晰简洁度。

这个层次的代码设计一般交给team member完成,并输出接口定义、接口详细设计、包括一些数据库的DDL等。

设计评审与文档中心

在项目实施之前,设计评审是非常重要的环节。研发lead应该组织内部设计讨论,每人的接口设计要通过peer或backup的review,更好的方式是通过集中评审。

因为再好的设计,也要确保项目组所有成员,理解正确且一致。这个不仅仅是lead对团队的灌输,要确保组员之间对同一个业务概念的理解也是正确且一致的。要确保每人的接口设计,都符合整体设计需要。要确保项目级别领域定义符合更上层定义(如公司级别命名),要确保项目级别领域定义统一、代码实现中英文命名统一。大型项目,在dev内部设计通过后,也可以由项目经理组织产品、研发、测试各方展开设计评审,此处尤其要和prd结合,为整理测试用例服务。

接口细节的实现也应当是动手coding之前先做设计,并建议形成文档,研发lead要提前组织好开发文档中心,整组统一设计文档格式。

一般常规内容包括:

•新项目背景、或常规迭代项目里程碑

•项目管理的时间节点(需求评审时间、设计时间、提测时间、上线时间点)

•本期项目概要设计说明

•分工(API、完成人、预估工时、实际工时等)

•详细设计:接口实现设计、DB设计、缓存设计等

•上线计划等等

文章目录
  1. 1. 整体设计与架构设计
  2. 2. 领域模型设计与数据库设计
  3. 3. API设计与代码实现设计
  4. 4. 设计评审与文档中心