许多软件架构方法都假设架构在一开始就被规划好了。不幸的是,以这种方式规划的架构以后很难更改。函数式编程可以帮助实现松散耦合,从而可以将提前规划保持在最低限度,并且可以在以后更改架构决策。
Michael Sperber 在OOP 2023 Digital上谈到了软件架构和函数式编程。
Sperber 举了将系统代码划分为构建块的示例。这是一种特别重要的架构决策,可能与不同的团队分别处理不同的构建块。一种方法是对粗粒度构建块使用领域驱动设计 (DDD) - 限界上下文:
DDD 说你应该在一开始就通过上下文映射来识别有界上下文。然而,如果你弄错了上下文之间的界限,你就会失去很多好处。你会弄错它们,至少是轻微的——然后就很难再移动它们了。
根据 Sperber 的说法,与 OOP 相比,函数式编程支持后期架构并减少耦合。Sperber 认为,为了推迟宏观架构决策,我们必须始终解耦。他说,函数式编程中的组件本质上只是数据类型和函数,这些函数在没有可变状态的情况下工作。这使得依赖关系显式并且耦合比典型的 OO 组件松散得多。这反过来又使我们能够构建独立于宏体系结构的功能,Sperber 说。
Sperber 明确表示函数式编程“不只是没有可变状态的 OOP”。它有自己的领域建模、抽象和软件构建方法和文化。通过在 OO 项目中采用不变性,您可以获得一些好处。要获得所有这些,您需要更深入地研究,并使用适当的函数式语言,正如 Sperber 解释的那样:功能架构广泛使用高级抽象来实现可重用的组件,更重要的是,灵活的领域模型可以预测未来。在探索和开发这些领域模型时,函数式程序员经常使用数学提供的丰富词汇。由此产生的抽象从根本上是由功能语言提供的高级抽象工具实现的。
InfoQ 采访了Michael Sperber,了解我们当前的架构技术工具箱如何使我们容易做出错误的决定,这些决定以后很难撤消,以及如何解决这个问题。
InfoQ:在项目开始时定义宏架构有哪些挑战?
Michael Sperber:软件架构的一个流行定义是,它是以后很难改变的决定。一开始就这样做意味着在你掌握的信息最少的时候就这样做。因此,决策很可能是错误的。
InfoQ:是什么让在上下文之间移动边界如此困难?
Sperber:在架构社区中,我们似乎忘记了如何在有界上下文或单体中实现模块化,这就是为什么有这个新术语“模块化”,这意味着常规单体在默认情况下是非模块化的,它的内部是紧密耦合。
InfoQ:所以你是说我们不知道如何在单体中实现松散耦合?
斯珀伯:是的。这是因为 OO 体系结构的基础是使用可变状态进行编程,即就地更改对象。这些状态变化会导致难以看到的不可见依赖关系,并且会扰乱您的构建块。这不仅会影响项目的功能方面,还会影响其他质量目标。
InfoQ:你能举个例子吗?
Sperber:假设您选择并行作为实现高性能的策略:您需要选择聚合根,并通过互斥保护对这些根的访问。这是一项繁琐的工作,容易出错,难以快速完成,并且会显着增加耦合。
InfoQ:如果架构师和开发人员想要改进他们做出架构决策的方式,您对他们有什么建议?
Sperber:即使您不能在项目中使用函数式语言,也可以尝试使用函数式编程的基础知识来感受其中的差异和机会。如果您是 FP 的新手,我建议您使用如何设计程序的方法来入门 - 或者DeinProgramm供德语使用者使用。