Blog
Blog
PHODAL

上一周,我参加了一个为期一周的 Event Storming 的工作坊,便想写一篇文章梳理一下对于 DDD 的理解。

你是不是经常烦恼于放错层级的代码?你是不是经常在为一坨坨代码而奋斗?你是不是经常这样?

或许是软件正在吞噬世界,或许是软件不断被重写,越来越多的架构师、资深程序员开始关注起软件质量。

你可能不需要微前端,但是需要一个胶水框架。

起先,我只是想写一篇如何提升软件质量的文章。后来在写质量文章的过程中,发现我还差一部分重要的内容没讲:依赖管理。于是,我着手准备写这篇文章,结果发现我还缺少一篇关于:依赖安全机制的文章——依赖孪生。所以,也就有了上一篇文章《依赖孪生:低成本的依赖安全方案》。 笑,为了安装好软件质量这个包,我已经陷入了依赖地狱了。 回到这篇文章,我们将介绍一些依赖管理的相关实践:从基础的依赖环境隔离到告别单体应用。 ## 1. 依赖的环境隔离 事实上,针对于不同项目、不同语言进行环境隔离,本不是这篇文章讨论的范围。但是,我们又不得不提及一些相关的内容。因为,我们总是习惯性也忘记了相关的内容。 由于不同的编程语言的包管理工具,存在对于依赖的 link 机制,所以对于它们的管理也稍有不同: - 全局统一依赖型语言的环境隔离。对于诸如 Python、Ruby 这一类脚本型语言来说,它们使用的是全局依赖,所以需要依赖于诸如 ``virtualenv`` 或者是 ``rbenv`` 为每个项目创造一个独立的运行时依赖环境。 - 项目独立的依赖隔离。对于使用 NPM 或者 Yarn 的前端开发人员来说,依赖存在于项目目录下的 ``node_modules``,自然而然地就进行了依赖隔离,但是需要对 Node.js 的版本进行限定。稍微一提,这种机制特别浪费硬盘。 - 包管理工具版本隔离。对于 Java 的 Gradle 来说,它稍有不同:统一的依赖存储路径,但是却不需要进行环境隔离,不过需要限定 Gradle 的版本。 - 其它。由于我的语言经验有限,暂时只有这些案例。 ``注``:由于经验有限,上述内容有些可能不太准确,如有有错误,欢迎指正。 总之,我们需要针对于不同类型的语言和包管理工具,采用与之匹配的有效的依赖环境隔离。 ## 2. 自建依赖创建 我们已经讨论了太多这方面的内容,这里就不详细展开了。 - 对于中大型组织来说,可以自建自己的依赖中心。 - 对于小型组织和团队来说,使用 Git 仓库也是一个不错的折中方案。 ## 3. 进行 LICENSE 扫描 对于依赖的 LICENSE 进行管理和风险识别,是依赖管理的一个重要的部分。除了常见的商用管理方案之外,我们还可以采用诸如于 Node.js 项目中的 https://github.com/mwilliamson/node-license-sniffer 或者是 https://github.com/3rd-Eden/licensing 都是一些不错的工具。 至于 LICENSE 选择的问题,永远都是 MIT 优先。 ## 4. 是否进依赖进行内联? 这是自 left-pad 事件之后,我采取的包的新策略(在我的开源项目上):对于小的软件包,直接复制源码,在项目中创建 ``third-party`` 放置相关的代码及 LICENSE。 这主要是结合了三个要素而考虑的: - 安全性 - 可迁移性 - 便利性(因人而异) 当然了内联依赖,涉及到一个版权问题——适合于代码少,不维护的项目。 ## 5. 依赖的协议隔离策略 除了内联之外,我们还要考虑的一个问题就是依赖的协议问题。 ### 策略1:再发布 当我们从 Google 上搜索代码的时候,往往会遇到代码在 ``gist``、``stackoverflow`` 或者 ``codepen`` 上,它们非常适合于我们复制到项目中使用,但是难免地可能会遇到一些版权问题。对于开源项目来说,可以直接在项目中使用,并进行标即可。而对于外部项目而言,一种最简单的方式就是二次发布这些软件包。 又或者是对于一些已经不维护的依赖包,而我们又需要,那么就只能在修改后二次发布这些软件包。 ### 策略2:二次协议 / 协议隔离层 对于 JavaScript / TypeScript 类库来说,在这方面的问题比较少,毕竟前端运行在客户端上,所以 Web 前端即『开源』。 如果真的非要使用某个开源软件,并且基于它之上修改,那么可以采取和 Android 开源协议类似的二次协议的方式。Linux 内核采用的是 GPL 协议,而 GPL 具有传染性——如果直接在 GPL 协议的类库上封装,那么只需要开源这部分的代码,因为认为我们的代码并不是衍生产品。而事实上 Free Software Foundation 动态地链接文件也产生了衍生产品,也就是说你用了 GPL 的库,你也需要开源你的代码。 为此,Android 系统在类库上采用的主要是 Apache-2.0 软件许可授权,对 GPL 进行了二次,它允许 Android 上的开发商基于 Android 的源代码进行开发而不向社区反馈。 所以,对协议进行隔离也是一个潜在可行方案,它从某种意义上将风险转移到了这一层级。 ## 6. 依赖更新机制 在日常的业务开发中,最痛苦的一件事莫过于:没有时间做技术升级,没有时间处理技术债。在这个时候,对于依赖更新这个问题,可能就会变得非常痛苦:你可能要亲眼目睹一个遗留系统的形成。所以,作为一个有责任的开发人员,你应该尽量去说服相关的人员,去做好相关的技术更新和升级。 除此,当依赖发现更新的时候,是否有相应的工具可以提供我们?一些现代化的工具,诸如于 Intellij IDEA 可以帮助我们更新依赖。也可以尝试包管理工具自带的升级命令,诸如于 NPM 中的:``npm outdated`` or ``npm outdated --parseable|wc -l``。不过大多数时候,我们可能会放弃升级——除非我们不得不升级。除此,https://david-dm.org 也是一个不错的工具——用于开源的公开 Node.js 项目。 相似的工具还有 [next-update](https://github.com/bahmutov/next-update) 是一个不错的更新工具。又或者是同一个作者写的 [next-updater](https://github.com/bahmutov/next-updater) 的工具。 ## 7. 版本选择?宽松型,还是严格型 在引入依赖的时候,我们需要稍微注意一个对于依赖的版本选择问题:到是宽松型,还是严格型。依赖的版本问题,主要会出现在**持续集成**和新成员加入项目。诸如于使用兼容发布条款,如 ~= 0.6.4,对于那些早期已经搭建了的开发来说,并不会有问题。而新的成员安装的时候,可能就变成了 0.6.5,在 API 上可能会发现一些细微的变化 。而诸如于使用 ``*`` 匹配版本的话,就更不严谨。 以 Python 语言中的 ``requirements.txt`` 为例,存在不同的描述版本的方式: ``` ~=: 兼容发布条款,如 ~= 0.6.4 等价于 0.6.* ==: 版本匹配条款 !=: 版本排除条款 <=, >=: 广泛的有序比较条款 <, >: 排除的有序比较条款 ===: 独断的比较条款 ``` 一些常见的情况如下: - 对于间断型维护的项目来说,往往选择严格型的依赖版本策略,因为大家都不想去碰了,哈哈。 - 对于长期开发的项目来说,选择宽松型的依赖版本策略,可以让项目可容易维护。 - …… ## 8. 使用依赖锁定 为了方便于在不同的开发人员之间能平稳地运行起项目,我们需要使用依赖锁定,以避免出现 DLL 地狱的问题。 > DLL 地狱(DLL Hell)指 Microsoft Windows 系统中,因为动态链接库(DLL)的版本或兼容性的问题而造成软件无法正常执行。 为此,我们需要依赖锁定配置文件,来记录已经安装的依赖的确切版本。除此,它还描述了生成的依赖树及其关系,以便于可重现安装依赖(即在后续的安装中能够生成相同的依赖树),而忽略依赖的更新。 依赖锁定配置与依赖配置的最大区别在于,依赖配置文件只描述了需要哪些依赖及期版本,而依赖锁定配置描述了依赖的子子孙孙依赖及期关系。 如我们的一部分依赖是在内部服务器,那么在安装的时候只会从内部服务器拉取相应的依赖,不会去外部环境下载。这个时候,我们所能做的是拷贝依赖,或者是删除这个文件,重新生成。与此同时,这个依赖锁定文件可以通过源码控制工具(如 git)来进行管理。 常见的锁定配置文件有,Ruby 中的 ``Gemfile.lock``,Node.js 中的 package-lock.json 或者 yarn.lock。无论你如何修改 ``package.json`` 的内容,它都会按照 ``package-lock.json`` 来安装依赖。 ## 9. 依赖代码安全检查 就目前来说,常见的有两种方式: - 商用解决方案。 - 依赖孪生机制。《依赖孪生:低成本的依赖安全方案》 笑~ ## 10. 告别大的单体架构 我的意思并不是说微服务好——不过真的很香,我的主要目的是想说:大的单体应用在依赖上存在着滞后的问题。 对于微服务来说,频繁更新的服务,必然可以做出依赖的及时更新;而对于基本不修改的服务来说,我们基本不会考虑去更新依赖。对于某一长期开发的单体应用而言,它们采用新的构架和编程语言非常困难。因为某个接口的修改,导致要修改一系列的代码,有可能相当于重写整个应用。 诸如于 Spring 框架本身一直在更新,而你正在使用的版本是 2.x,那么你可能与新版本已经不兼容。甚至于,需要维护自己版本的 Spring——出于更新漏洞的原因,你们重写了系统中的部分软件。 ## 11. == 什么是好的依赖 见下一篇文章。 参考内容:https://github.com/bahmutov/talks/blob/master/dependencies-best-practices-node.md

再三犹豫,终于在 rebase 到上海之后,还是决定写下这篇文章。三年多以前,我 rebase 到了深圳,开始了国内交付项目的旅途,我也几乎成为了待在该 account 下最长的 developer 没有之一。在那之前,我在 offshore 项目,混吃混喝混 session。

在阅读《零信任网络》的期间,我突然有了一个新的安全实施方案: 依赖孪生——针对于中小型 IT 组织的依赖方案。(PS:其实这只是吹吹而已,并没有那么夸张)。所以,我就顺手写了一篇文章,记录一下相关的实践和模式。

这里主要是指在 2019 年我看的一些不错的书,哈哈 ## 《零信任网络》 书的全称:《零信任网络:在不可信网络中构建安全系统》,英语:Zero Trust Networks: Building Secure Systems in Untrusted Networks 先介绍一下概念: > 零信任是一种安全模型,用于保护企业远离网络攻击并保护敏感数据安全。零信任扩展生态系统(ZTX) 包括数据和信息保护以及网络安全。 这本书介绍了一个理想的现代安全模型框架,包含了大量极具价值的实战经验。虽然,从某种意义上来说,它看上去更像是一本 DevOps / Ops 或者是信息安全方面的书籍,但是总的来说,我从中 GET 到了一些之前没有 GET 到的网络相关的知识。这本书从我们熟悉的 DMZ 区(Demilitarized Zone,隔离区)讲起,介绍了 DMZ 潜在的种种风险,进而引起了我对这本书的兴趣。 对于开发人员来说,有这么一点特别有意思: 本地认证解决方案。诸如于 UAF 标准,将信任从大量服务转移到——数量相对较少的用户可控的终端上。 值得关注的还有**倒数**第二章『零信任网络的实现』,介绍了如何去实现这么一个网络,并结合了 Google 的 BeyondCorp 的例子来展示。 ## 《架构整洁之道》 之前推荐过『《架构整洁之道》有感』,这里更新一下。 这本书是 Uncle Bob 的博客合集,所以这本书最吸、启发我的地方,怕是结尾的 《架构设计考古》相关的故事。编程年限时间越长,经常思考更好的解决方式,能收获到的知识也就越多。如果我们不能决定一个系统的架构,那么就思考、实践如何把它变好。过程中,大抵也是能收获更多的东西,对下一个系统有更多的启发。 第一、二部分,资深程序的程序员都会有一定的了解、实践。第三部分,是他最为人所知的 SOLID 原则,他最早出现 15 年前的《敏捷软件开发》等书上。而作为一个接受过 ThoughtWorks OO Bootcamp 训练的学员,我大抵还是能相当熟悉的运用它们。 第四部分,在讲述组件化架构,都是能以 UI 项目,而非后端服务谈论组件化架构,那倒能多得几分。终于在第五部分,开始总结性地介绍软件架构——这大概就是这本书最精彩的地方。除此,书中的这部分 Clean Architecture,经过我们的充分验证,这个架构模式对于大型项目来说确实不错——通过架构来规范代码。 然后,剩余的部分都在讲代码故事,Uncle Bob 讲述故事的能力,大概是我的好几个数量级。 说到底,还是因为我太年轻了,没能读懂这本书的精髓。看上去像可有可无的大道理,在落地上倒是得自己花时间去考虑。遇到一个架构方面的难题,带上这本书,加上所谓的悟性,或许我的问题就能迎刃而解。要是一看,本书倒是不适合初学者。可是,这本书的叙述方式,又相当的适合初学者。 ## 《演进式架构》 中文电子书已出版,中文纸质版正在出版中,英文名:Building Evolutionary Architectures 。 我关注我们公司几个大佬写的这本书很久了——从 2017 年出版的时间开始关注。虽然,我知道这是一本不错的架构书籍,但是我一直懒得(穷)看英语版。它和我过去关注的《浮现式设计:专业软件开发的演进本质》有一定的相似度,不过提出了更多优秀和可靠的意见和建议。 > 演进式架构是支持跨多个维度进行引导性增量变更的架构。 作为一本架构方面的书籍,它系统性的将我们所需要的要素整合起来: - **增量变更**。如何增量地构建软件和如何部署软件。 - **适应度函数**。架构的适应度函数为某些架构特征提供了客观的完整性评估。 - **适当的耦合**。如何确定哪些架构维度间应该相互耦合,以最小的开销和成本最大程度地获益。 《演进式架构》一书一直在强调一个观点,即**要长期的去规划架构**,架构是持续、迭代演进出来的。为此要时刻关注变化,添加演进能力,实施增量变更。 有意思的是,书中提到了我想要的另外一个答案:**可牺牲架构**——在概念验证成功后即被抛弃的架构(Martin Fowler)。换句话来说,在你实现 MVP(最小可行性产品)的时候,你可以用最快、最优的方式来实现系统,而非最适合的架构但是速度慢。 书中还提到了大量的架构反模式,诸如于:供应商为王——围绕供应商(如 ERP)构建架构、最后 10% 的陷阱——某些技术可以快速构建应用,但是无法完成最后 10% 功能(诸如于 React 的 CRA,也许你并不需要这 10%)、代码复用和滥用。以及陷阱:原始抽象泄露——底层抽象错误导致系统出现意外的灾难、**简历驱动开发**——不要为了架构而构建架构,构建架构是为了解决问题。笑~ (PS:这部分可能是这本书另外一个亮点,反正我觉得它非常精彩——就是案例不够丰富。) 书中最后一部分介绍了如何去实践演进式架构,哈哈哈。不过,这本书有一个问题就是写得太抽象了。以致于没有相关经验的情况下,可能有些地方难以理解——我也是听了公司的架构导读分享之后,才对这本书有了一个更新的认识。 ## 《数据密集型应用系统设计》 这本书我是在公司的邮件里,看到同事的推荐买的。反正,O'Reilly 的动物书不会太差。买完之后,发现这本书相当的不错,又弥补了我缺少的一些知识点。在写这篇文章的时候,我意识到这么好的书,在豆瓣上应该有很好的评分——果不其然,评分 9.7 (187 人评价)。 这是一本不可多得的关于数据方面的好书——特别是对于缺少数据方面的开发来说,比如非科班出生的程序员。不论你是前端工程师,还是后端工程师,或者是 DevOps 工程师——只要你是个工程师,都建议你去读一读。 书的第一部分是数据系统相关的基础部分。这部分的内容非常棒,它整理了一系列关于数据模型与查询语言、 数据编码与演化、数据存储与检索相关的知识。它是一个相当不错的知识索引和概括,帮助你重新梳理数据系统相关的整个体系的内容。**哪怕是你对分布式系统没有兴趣,你也应该读完一章**。 书的第二部分是**分布式数据系统**,也就是我们在各种讲后端架构常会看到的内容。也因此,这一部分的内容对于后端来说,它会更加有价值——诸如于各种分布式系统的基本问题,以及对应的解决方案。 书的第三部分是**派生数据**,包含了『批处理系统』、『流处理系统』以及『 数据系统的未来 』三个章节,我还没看……。因为关于批处理和流处理相关的内容,我已经有了一些实践。而且相关的内容,并非一两个章节能讲完的,笑~。 不得不提及的是,这本书每一个章节的结尾处都有几十个、几百个引用——我的意思不是说,这本书在浪费我辛辛苦苦赚的钱,而是这本书真的很细致、严谨。 ## 《前端架构:从入门到微前端》 经过无数次的微信群聊证明了:这本书是一本给前端的装逼利器。它可以用于打击绝大部分的后端开发人员,无往不利。笑~ 你需要前端微服务,这里有。你需要前端的规划,这里有。你需要更好的前端架构,这里有。 ## 其它 综上所述,你应该**优先看**《零信任网络》第一、第七和第九章,《架构整洁之道》的第 5 部分,《数据密集型应用系统设计》 的第一部分,《演进式架构》的第六和第七章。对了,还有《前端架构:从入门到微前端》全书,笑~。 不过,适合我的不一定适合你,但是多读书总是没错的。

在科技公司,诸如于 ThoughtWorks,当你在等待项目 / 两个项目之间 / 没有项目的时候,这段时间称为 bench (冷板凳,笑) 又或者是称为 beach。

应用脚手架是一个项目的重中之重,决定了整个项目的基调。

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

存档

分类

标签

作者