遗留系统的定义
请你先思考这样一个问题:假如一个系统七八年了,它是不是个遗留系统?系统的时间长等同于就是遗留系统,这是很多人的一个误区。虽然大多数遗留系统确实是存在的时间很长,但并不等于时间长的都是遗留系统。
一个有着12年历史的老系统,它的技术栈最初是.NET Framework,现在已经有部分迁移到了.NET Core;它最初是单体架构,现在是一个小单体加多个微服务;它从第一行代码开始就使用 TDD 的方式开发,至今已经有 30000 多个不同类型的测试;它一开始使用 SVN 来管理源代码,不过早在十年前就被迁移到了 Git;它从第一天就有 CI/CD 相伴,并且一直坚持基于主干开发的分支策略,每个月都有稳定的版本发布;它没有一行注释,但是任何开发人员都能通过阅读源代码快速了解系统的实现,也就是说代码质量相当高。
这个系统历史12年之久,比很多公司活的时间都长。那它是遗留系统吗?答案是否定的。因为它的代码质量高、架构合理、自动化测试丰富、DevOps 成熟度也高,各种技术、工具都是相对先进的,怎么能说是遗留系统呢?
那么,存在时间短的系统就不是遗留系统么?
拿我个人经历过的一个项目来举例。它是 07 年左右开发完成的,当我 10 年加入项目组的时候发现,它仍旧是基于 JDK 1.4 的(那个时候 Java 6 已经发布 4 年了),很多 Java 的新特性都无法使用。它是一个 C/S 结构的软件,前端基于 Java 富客户端,后端是一个大单体向前端提供 RPC 服务;它没有一行测试,每改一行代码都提心吊胆,有时为了不影响别的功能,只好把代码复制一份,加入自己的逻辑,这就导致了大量的重复代码;每次发布日期都是一拖再拖,而部署到生产环境上的 war 包,甚至在是开发机器上打包的
所以说,时间长短并不是衡量遗留系统的标准。代码质量差、架构混乱、没有测试、纯手工的 DevOps(或运维)、老旧的技术和工具,才是遗留系统的真正特点。
判断遗留系统的几个维度:代码、架构、测试、DevOps 以及技术和工具。
遗留系统的特点:旧,过时,重要,仍在使用。
遗留系统现代化
1.代码现代化
将“祖传”代码重构成职责清晰,结构良好的优质代码,重点是:可测试化重构
例如如下代码,我想测试if的逻辑,当Dao方法返回一个null时,这段代码会抛出一个异常。
public class EmployeeService { public EmployeeDto getEmployeeDto(long employeeId) { EmployeeDao employeeDao = new EmployeeDao(); // 访问数据库获取一个Employee Employee employee = employeeDao.getEmployeeById(employeeId); if (employee == null) { throw new EmployeeNotFoundException(employeeId); } return convertToEmployeeDto(employee); } }
这是典型的不可测代码,因为EmployeeDao内部会访问数据库,从中读取出一个 Employee 对象。而这个 EmployeeDao 是在方法内通过 new 的方式直接构造的,就意味着这个方法对 EmployeeDao 的依赖是固定的,无法解耦的。
在单元测试中,我们不可能直接访问真是的数据库,因此这样的方法需要对其进行可测试话重构。比如下面这样:
public class EmployeeService { private EmployeeDao employeeDao; public EmployeeService(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } public EmployeeDto getEmployeeDto(long employeeId) { Employee employee = employeeDao.getEmployeeById(employeeId); if (employee == null) { throw new EmployeeNotFoundException(employeeId); } return convertToEmployeeDto(employee); } }
通过这次重构,我们把会访问数据库的 EmployeeDao 提取成类的私有字段,通过构造函数传入到 EmployeeService 中来,在 getEmployeeDto 方法中,就可以直接使用这个 EmployeeDao 实例,不用再去构造了。由于传入的 EmployeeDao 并不是 EmployeeService 构造的,所以后者对前者的依赖就不是固定的,是可以解耦的。
如果我们传入 EmployeeService 的是一个 new 出来的 EmployeeDao,那和原来的方法一样,仍然会去访问数据库;如果传入的是一个 EmployeeDao 的子类,而这个子类不会去访问数据库,那么 getEmployeeDto 这个方法就不会直接访问数据库,它就变成可测试的了。比如我们传入这样的一个子类:
public class InMemoryEmployeeDao extends EmployeeDao { @Override public Employee getEmployeeById(long employeeId) { return null; } }
这样,想测试原方法中 if 的代码逻辑就非常方便了。
2.架构现代化
1)改造老城区:指对遗留系统内部的模块进行治理、让模块内部结构合理、模块之间职责清晰的一系列模式。前端方面包括单页应用注入、微前端等,后端包括抽象分支、扩张与收缩等,数据库端包括变更数据所有权、将数据库作为契约等。
2)建立新城区:指将遗留系统内部的某个模块拆分到外面,或将新需求实现在遗留系统外部的一系列模式。包括绞杀植物、冒泡上下文等。为了对新建立的新城区予以各种支持,老城区还可以通过提供 API、变动数据捕获、事件拦截等各种模式,与新城区进行集成。
3.DevOps现代化
要从头开始搭建一个 DevOps 平台,包括代码、构建、测试、打包、发布、配置、监控等多个方面。
4.团队结构现代化
四种团队拓扑包括业务流团队、复杂子系统团队、平台团队和赋能团队。
三种团队交互模式包括协作、服务和促进。我们在进行开发团队的组织结构规划时,应该参考这四种团队拓扑。
遗留系统现代化的三个原则。即:1. 以降低认知负载为前提2. 以假设驱动为指引3. 以增量演进为手段
标签:实战,employeeId,读书笔记,代码,系统,EmployeeDao,public,遗留 From: https://www.cnblogs.com/rickybelment/p/16626757.html