业务系统的开发经验小谈
概要
- 关键要素:业务理解 + 系统架构 + 技术集合 + 迭代开发 + 严谨测试
业务理解
业务从何而生? 一个具体的场景下遇到了问题不知道怎么解决或解决的办法很原始很笨拙,由此而产生了需求:在具体场景下需要解决某个问题,或者以更优的方式来解决。解决后,实现某事的效率大幅提升,进而促进整体的效益。
对业务有整体而深入的理解, 才能设计出更好的系统架构来容纳业务的有效处理和扩展。
业务抽象
业务可抽象为:1. 需求层面:用户故事与需求; 2. 语义层面:实体、关联、约束; 3. 实现层面:流程、规则与数据。
-
提炼用户故事是业务抽象的首要。需求通常可以讲述为一个简短的用户故事:用户在某种场景下需要实现某个目标,他需要通过执行某些步骤来完成这个任务。提炼出多个关联的用户故事后,就可以考虑在建模层面来思考。
-
业务系统通常会有一个核心业务模型, 这个核心业务模型由以下要素组成: 重要的业务实体及其关联和约束、使系统能够RUN起来的业务规则集合。
-
上层是“实体+流程+逻辑”。 通过业务实体及其关联, 组合以业务流程及规则,完成各种业务功能。
-
底层是 “数据+规则”。 数据是核心,规则是外延。“数据+规则” 构成了业务系统的底层视图。
-
必须发现那些使得系统运作起来的充分必要条件,即“整体规则”,这样的规则通常由一系列子规则组合而成,每个子规则规定了相关的构成子部件必须满足的充要条件。比如, 为什么 Ajax 能够异步刷新页面? 为什么 XEN 能够让多个虚拟机运行在一个机器上?而要发现这些规则,必须像科学家那样充满好奇心地去探索、尝试、挖掘和创造,而不仅仅只是照搬其成果。
-
将使得系统RUN起来的核心业务流程与规则集合梳理出来, 实现具体业务功能时就有据可依, 以不变应万变, 能够更好滴考虑系统的可扩展性; 一旦完成核心业务规则的编写,接下来可以充分地考虑各种错误条件和异常检测,增强系统的健壮性和一致性。
-
业务功能开发,即是在不同子系统之间传输数据以及在不同层次对各个来源的数据使用不同的规则进行检测和组合, 从而使得系统 RUN 起来, 或者快速返回错误。 当编写代码时, 最核心的就是使系统RUN起来的核心业务流程与规则集合。
数据基础
-
数据是一个业务系统的中心要素。整个的技术的作用最终是使得数据能够在其生命周期内在系统内顺畅、正确、一致、高质量而低成本地流动;
-
数据源的生成和维护;数据的采集、传输、计算、存储、去向;
-
即使是前端设计和开发,也会发现:本质上就是: a. 从服务端拉取数据; b. 让数据在页面间流畅而安全地传递;c. 使用合适的组件展示得更加直观易读。
-
数据存储是业务系统的关键导图。 通过分析数据存储设计, 理解该系统负责管理哪些数据, 可以推出该系统所涉及到的重要业务实体。
评估量级
通过对用户故事的提炼和梳理,预估业务量级,可以在架构设计层面做出更好的决策,降低实现和运维成本。比如,限定时间范围搜索,限定某些筛选条件搜索,减少需要考虑的活动业务量,就可以减少分库分片的需要,同样达到相同的目标。
系统架构
-
架构可以分为开发架构、部署架构、产品架构、业务架构;程序员常常接触到的是开发架构和部署架构;
-
开发架构的主要作用: 1. 良好的工程结构与整体设计,简化业务逻辑处理模式,使开发者更专注于实现业务处理逻辑; 2. 选择适合的长远的技术集合,使得业务系统具备良好的可扩展性和持久发展;
-
部署架构需要关注多个应用之间的依赖关系,梳理这些关系,进行服务化治理和服务可用性监控;
-
业务功能的正确性和可维护性通常取决于数据库设计、如何存取和更新资源记录;性能与可靠性取决于算法与系统整体架构;
-
持续提升代码可读性,Keep Code Clean, 非常有益于系统的快速理解、维护和扩展。
技术栈
概览代码组织结构
-
代码组织结构是业务系统的最明显的整体特征, 勾勒了系统的基本构成。
-
通常会按照两种方式划分: 按照业务模块划分; 按照组件模块划分。
-
熟悉代码的组织结构,能够尽快地增加对系统整体的印象, 了解代码该放在何处。一般 Java 业务系统都会划分为 controller, service, dao 三层, 此外会有一些基础设施作为辅助, 比如缓存、线程池、异步任务流框架等;
通读配置文件
-
通读工程的配置文件, 可以获得该系统的不少重要信息。
-
对于Java业务系统而言,一般包括:IOC/AOP/ORM, 线程池/缓存/框架配置、第三方调用与服务、消息中间件、定时任务、对外提供的服务或接口等。
熟悉使用的技术及框架
- 通常在参与一个业务系统的开发之后, 会对业务系统里使用到的技术和框架有基本的认识,而Java业务系统所使用的技术和框架往往是大同小异,因此, 通过浏览工程里的代码组织结构和配置文件, 大致阅读里面的一些重要类, 通常就可以了解到这个系统所使用了哪些技术和框架。
熟悉子系统之间的交互
-
对于一些中大型业务系统, 通常会涉及到多个子系统之间的交互;
-
一个中大型业务系统可能包含openapi, 二方库以及提供二方库实现的内部服务系统, 或者可能会包含一个对外的api,一个控制调度系统和多个分布式节点的执行子系统。
-
熟悉子系统之间的交互, 会对整体业务的实现有更好的认识。
从重要业务操作及代码着手
- 只读不写是难以真正深入到系统的。因此, 最好能够从一个重要业务操作着手, 或者能参与到项目的重要业务功能的开发中,就能将以上所有元素的理解串联起来, 形成对系统业务的实际理解。
迭代开发
开发主流程
-
熟悉系统的整体设计、主要技术栈以及运行部署;
-
持续沟通并理清业务处理逻辑;
-
仔细分析其中的重难点问题,确保在真正投入时有可接受的解法;
-
制定开发计划和进度表;拆分任务,作出todo列表;
-
开发/测试/持续改进的交替进行;严格测试,保证 UT, FT 和 测试用例通过。
-
细致周全地处理异常。
-
反馈、维护、改进和文档化。
细则
-
需求讨论需要具备“条分缕析”的耐心和能力,像蚕食桑叶一样把每个业务小点都梳理清楚不含糊。 尽可能反复彻底与业务方、产品经理、工作伙伴询问和讨论清楚业务需求,定义清晰的目标,梳理核心主要的业务流程,理解业务全景图;切忌急于投入开发。
-
概要设计需要通悉常规的设计方法,主要是业务实体设计、数据库设计、业务流程图绘制等。平时也要多了解各种设计方法和实现,才能在需要的时候运转自如。
-
详细设计需要不急躁且不过度的平衡。不急躁是指必须先将技术难点攻克再投入实现的方法,避免方案有难以解决的瓶颈而导致返工,不过度是指不要沉迷于完美的前期设计。只要方案能够解决问题,实现可接受的性能和可扩展性,就可以开始投入了。
-
数据库设计中,根据业务目标确定业务实体及其关联、数据模型,设计正确的数据库表,或增加合适的字段。
-
如果业务功能需要多个子系统协作完成,则需要整体布局能力,明晰和合理分配每个子系统的责任。
-
投入开发之前,制定开发计划和进度表是值得尝试的。自主地有效管理自己的开发活动,尽可能接近于可控时间范围内按时按质完成任务。对自己的管理能力也是很好的挑战和磨炼。
-
开发的基本方法是“拆分”与“连接”。无论多么复杂的逻辑,总可以拆分为一系列的逻辑小块;然后,将逻辑小块连接起来,从而使得系统能够RUN起来。可以采用意图导航编程法。不急于写实现,首先编写一系列空方法,在方法的注释中表达其意图,还可以增添开发评估时间,然后依次实现这些意图。可以有效地简化复杂逻辑的开发,亦便于后续的测试和维护。
-
测试方面,单元测试尽可能粒度小、覆盖全;接口测试必须对重要的、典型的、常见异常的三类场景充分测试。后台服务的测试尽可能自动化,标准化测试环境,采用并发提升执行性能,降低测试时间成本。
-
考虑子系统交互及部署结构;
-
在项目开发过程中,或者已经上线后, 积极听取反馈意见和持续改进,撰写文档持久化存储从中所学到的知识。也是非常重要的一个环节。程序员不仅要会写代码,也要善于表达自己的观点和思考。只有写下来才能真正理解所知所学。口说无凭。
实际开发技巧
业务处理模式
- 检测权限与可访问性、日志记录(切面);
- 读取请求,检测参数有效性;业务处理逻辑、格式化处理结果并输出;
代码模式
- 从数据库或缓存中读取资源记录并检测是否存在或是否合法;
- 对底层系统或第三方系统发送指令, 做实际的业务处理;
- 根据指令结果更新资源记录。
- 总的来说: a. 拷贝数据; b. 使用规则检测数据; c. 聚合数据,使之符合使系统能够 RUN 起来的规则。
错误处理
-
暂时先“忽略”那些错误检测, 确定使得功能可用的基本核心的充要条件。当我们发现使得系统运作起来的有效规则和充要条件后, 就很容易梳理那些错误条件了: 如果缺少 X 数据会怎样? 如果 Y 状态不对会怎样? 这些错误条件就变成系统中的各种检测, 能够及早发现错误并返回,或者适当地纠正和提醒用户。接着, 就需要考虑一些异常情况了: 如果网络中断了会怎样? 如果数据库访问超时了怎么办?
-
设计阶段:探索、挖掘和确定使得系统运作起来的充要条件 => 梳理错误条件, 确定各种检测条件 => 考虑异常处理
-
编码阶段:编写主流程代码, 不考虑任何错误和异常 => 添加错误检测, 分离到单独的函数 => 进行异常捕获
严谨测试
-
现在互联网知名企业都逐渐在推行研发测试融合,“信任”开发能够也应该写出合格的测试。
-
开发的测试主要包括单元测试和单接口测试。单元测试包括数据库访问层测试和业务逻辑中新增方法的测试;单接口测试主要是对系统公开的单个服务接口( http, dubbo ) 进行测试确保接口服务是OK的;当然,进一步有时还需要接口组合测试,即测试用例。
应牢记: “开发软件的本质是为了帮助他人”。如果所开发的系统已经能够满足人们的需要,即使它是不完美的,也是足够有价值的;反之,如果系统不能满足人们的需要,无法帮助到人们,即使它看上去很COOL,也是没有价值的。即使不编程,也可以通过其他方式去帮忙人们。