在我的职业生涯中,我曾处理过各种遗留项目,每个遗留项目都有许多缺陷。
当然,主要的问题通常是软件质量差(缺少单元测试,拒绝使用简洁代码的原则...),但是也存在困难,其根源是在项目开始时甚至在公司系统成立时就做出了体系结构决策。 我认为,这类问题是许多项目遭受最大痛苦的原因。
从本质上讲,改进代码非常简单,尤其是现在,软件工艺运动正在促进团队中的良好实践。 但是,更改系统的核心部分,即在系统生命周期开始时施加的限制,是一项极其困难的任务。
我将向您介绍我所遇到的几种类型的体系结构解决方案,它们可能会成为参与支持此类系统的团队的真正负担。
您的整个公司共享您的数据库
这也许是我所见过的最常见的问题之一。 当多个应用程序需要使用共享数据时,为什么不只给他们共享访问共享数据库的权限呢? 毕竟,重复在软件开发中被认为是有害的,对吗? 嗯,这并不总是正确的,尤其是涉及数据库时。 Venkat Subramaniam这样说:“数据库就像牙刷:永远不要让任何人使用它。” 共享数据库有什么不好? 实际上,很多...
首先想到的是数据模型的耦合。 想象一下,两个应用程序A和B处理汽车上的数据。 负责维修工作的团队使用附录A,因此他们需要存储许多技术信息:有关汽车的机械,故障,干预历史的信息...附录B用于为技术团队设置任务,因此它只需要有关汽车的基本信息就足够了。其标识。 在这种情况下,两个程序都使用相同的数据结构是没有意义的:它们使用不同的数据,因此每个程序都应使用自己的数据结构。 由于汽车易于识别,因此变得更容易,因此不需要一般参考。
第二个问题也是由于数据模型的这种网格划分。 想象一下,应用程序B要重命名汽车标识符-为它的主题区域提供一个更合理的名称。 在这种情况下,还必须更新应用程序A-因为列名已更改...结果,为了不打扰应用程序A团队,开发人员B将开始在另一列中复制信息,因为他们无法更改现有列...当然,开发人员A会说他们计划将来更改列的名称,以免在两列中存储相同的信息,但是我们都知道:这很可能永远不会完成...
当应用程序不仅从一个来源读取数据,而且还要更改它们时,一切都变得更加令人恶心! 在这种情况下,谁是数据所有者? 谁信任? 如何保证数据完整性? 即使同一应用程序的不同部分更改了相同数据,这也很困难,但是当几个不同的应用程序执行此操作时,问题将变得更加严重...
我见过的最后一个案例:共享相同数据结构以存储有关两个业务对象的信息的两个应用程序,它们相对相似,但同时又相差很多,从而使对数据所引用的应用程序的理解严重复杂化。 两种应用程序都使用同一张表对金融市场中的执行进行建模,但聚合级别不同。 没有任何东西表明该表包含两种类型的数据,因此我们必须查看另一个表(属于第二个应用程序)来确定每个程序生成的行……每个不得不使用该表的新开发人员都不可避免地来了与其所有前任一样,使用了不正确(易受攻击)的数据,由此给公司带来了所有风险。
您的系统与公司使用的软件绑定在一起
并非每个公司都可以开发满足其业务所有需求的软件。 实际上,在许多情况下,这将是自行车的发明,因为许多公司的需求是相同的,并且在市场上很容易找到已经具有必要功能的软件。
通常,购买产品通常比制造产品便宜。 但是,当然,您刚刚购买的产品不能立即与您已经使用的软件无缝地协同工作,因此您需要在两个(大多数情况下为专有)应用程序之间建立桥梁。 您一定会为业务的特定部分开发自己的工具,只要您已经购买的这款昂贵的软件具有合适的模型,您只需使用其数据库并将数据添加到该数据库的表中即可。
几年过去了,十几个开发人员或团队一次又一次地执行这些操作,然后您陷入了僵局:如果您购买的软件的开发人员关闭了该软件,或者停止了对该产品的支持,或者某些新软件被证明是更适合您的需求。 在某些情况下,您甚至可能对外部软件有技术依赖性。 如果您使用的软件解决方案的作者希望您使用他需要的语言/框架/其他版本,则意味着您的系统架构不属于您。 如果您想出售一个新版本以提供您确实需要的功能,但是此版本可能会因技术要求的变化而有所变化,那么您将被迫更新您的技术堆栈,以满足其他人的建议。 我知道感觉如何,您不希望这种强迫迁移成为常事...
我在一个项目中工作,我们使用的产品的开发人员不想为所有客户添加新功能,因为对他们而言,维持竞争性变更和几个当前版本太困难了(每个客户都有自己的版本,仅具有他所需的功能)。 因此,他们决定向我们出售SDK,以便我们可以实现我们自己的功能。 当然,他们没有提供足够的文档来说明如何执行此操作,而且,我们被迫使用其业务实体,为了理解其结构,我们必须对其进行反编译-因为我们既没有源代码也没有文档...实现最简单的功能花了几天的时间,几乎不可能进行测试,因为一切都太复杂了,甚至还需要一种脚本语言的知识,而团队中没有一个人已经拥有复杂的技术堆栈……
多个应用程序之间的强大啮合
回想一下2000年代初期:使用Enterprise Java Bean(EJB)处理信息系统中应用程序之间的远程调用是多么令人愉快。 当时,这似乎是个好主意。 与其他团队共享代码以避免重复看起来也不错。 是的,所有团队都被迫同时推出他们的应用程序,以确保二进制依赖关系不会中断,但这是一个有趣的夜晚-与同事一起吃披萨,等待应用程序交付2个小时,对吗?
好吧,实际上并不是那么有趣。 仅仅因为公司中的其他人喜欢您的代码并且他们决定在未经测试的应用程序中使用它,就不可能在您自己的代码中重构一个单独的类仍然是一种乐趣。
一旦意识到这些早期决策有多么混乱,将您的应用程序与世界其他地方分开所需要付出的巨大努力。 从字面上看,您将需要花费数年的时间将您的项目切成不同的组件,以便其他应用程序无法再使用主题区域的核心,客户端或缓存机制; 删除对外部类的所有调用,这些调用导致与其他项目的紧密联系; 用REST API替换所有EJB调用...但是,通过所有这些努力,与该项目相关的每个员工都将获得丰厚的回报:开发和测试将得到简化,交付过程将得到加速,因为现在不再需要将您的工作与其他人同步; 您自己的代码中的概念会更好地分开; 当您将一堆其他应用程序的依赖项导入到类路径中时,将简化依赖项管理,传递性依赖项的问题将消失……所有这些代价高昂的更改只会挽救团队的生命,如果您在项目开始时进行了更改,它们将变得更容易实现!
您在其他人的项目的基础上构建项目
您很可能不会遇到此问题,但是仍然会发生,并且如果发生这种情况,那么这是最坏的情况,因为它结合了前面讨论的几个问题。 实际上,我在职业生涯的第一个项目中遇到了一个类似的项目。
当我加入该项目时,被告知公司系统已完全改写,并且该项目的工作仅在2个月前才开始。 想象一下当我看到一个具有完善的管理模块的复杂Web应用程序,已经实现了复杂的业务功能以及一个用于编写其他模块的开发框架时,我会感到惊讶。 很快,我发现所有这些都不是我的团队写的:为了不从头开始,决定重用我们小组中另一家公司开发的框架。 问题在于该框架不是与开发该框架的项目隔离的。 因此,我们的团队只是收到了包含另一家公司项目的所有源代码(包括其业务逻辑)的存档,而这些存档与我们自己的业务无关。 更糟糕的是,我们还从它们那里继承了数据库模式和数据本身……
对于团队的新手来说,要理解哪个代码与框架,哪个代码与我们的项目以及哪个公司的业务有关,并不容易。 团队希望清除这种混乱状态,但是由于代码部分之间的依赖关系,几次尝试都以严重的回归错误结束(由于只有一个模块,该语言未将其称为模块!),当然,那里没有自动测试是。 而且,我们不得不放弃使用另一台应用程序服务器的想法,因为它在整个系统中都有另一家公司使用的特定代码,这对于我们的小型团队来说,这样做的迁移成本太高。
在某个时候,我们想为框架添加一些不错的功能,但是我们被告知这已经在另一家公司完成了。 因此,我们被要求将当前版本与另一家公司代码的当前版本合并...团队设法通过简单地重定向(樱桃选择)与新功能相关的一些提交来避免这种噩梦,但是与之相比,它仍然过于复杂和复杂。我们需要...
我们设法完成了这个项目,但是它的质量实在是令人痛苦。 至少40%的代码和数据库内容是未使用的镇流器,删除此无效代码不是优先事项。 我希望团队最终有机会分离自己的代码-我不知道,我在这件事发生之前就离开了团队!
您所有的业务逻辑都存储在规则管理系统中
将您的一些业务逻辑放入规则管理系统是一种常见的做法。 例如,如果您的某些业务规则需要经常更新,并且单片应用程序的交付过程需要很长时间的测试阶段才能验证发行候选版本,那么这很有用,这使得无法配置某些“易失性” “规则。 尽管我更喜欢主题区域的所有规则都在源代码中的方法,但我可以理解,在某些情况下,规则管理系统会很有用。
但是,我遇到了一个几乎所有业务逻辑都在规则管理系统中的应用程序,有时规则是基于Excel文件生成的! 而且,由于该项目总体上是一个简单的ETL包 ,因此不应经常更改规则。 所有这些基础的Java项目仅包含有关批处理框架的技术细节以及源和目标系统的纯读写操作,而与主题区域没有任何联系。
结果,所有规则都是用一种特殊的语言编写的,团队中没有人真正了解它,这很难编写(我们的IDE不支持),并且几乎不可能调试和测试。 当请求一个新规则或对现有规则进行更改时,团队中的大多数开发人员只是复制了现有规则,导致一个完全相同的文件,但有一个特定更改除外(通常唯一的更改是应用规则的字段)。
如果这似乎已经成为一个问题,那么这里还有其他事情:在任何规则中,绝对没有关于其目的的任何线索。 规则被命名为Rule1,Rule2等,总共有100多个! 基本上,每个规则都是由检查和分配硬编码值组成的,没有任何域术语。 甚至项目的名称也没有阐明整个ETL的总体目的。
结论
正如Bob叔叔在他的《清洁架构》一书中所解释的那样,在考虑其项目的架构时,应推迟某些决定,直到我们真正需要做出选择为止,否则,我们将无法继续为我们的产品增值(例如选择数据库)。例如)。 还必须尽早做出其他决定-不要等到情况恶化。 幸运的是,这类关键决策很容易识别,因为它们可以被称为“灵魂建筑”:当您想到这样的想法时,您不会发现它们有什么好处,只是一个早晚会出现并会重新出现的问题。困扰你。 不幸的是,在处理遗留项目时,此类负担通常深埋在代码中,这使得消除负担非常昂贵。
但是我们不应该害怕。 是的,清理多年来甚至数十年来积累的混乱并不是一件容易的事,但是作为专业软件开发人员,我们根本不能允许此类问题继续腐烂并扼杀开发人员的动机,客户的信任以及我们受益于他们的能力,并将其嵌入我们的产品中。
当然,我描述的每种体系结构负担都可以通过各种方式消除-没有解决所有这些问题的灵丹妙药。 但是,我确信任何团队都可以提出一些建议,以最终摆脱这种负担。 因此,让我们一起面对我们的问题,并开始整理问题!