My Little World

合理组织项目文件结构

按领域组织文件夹结构

每增加一个新的功能,整个应用程序的复杂度不应该明显上升。这样才能保证我们的应用程序始终可扩展,可维护。

软件复杂度的根源完全来自复杂的依赖关系

从业务功能去理解,依赖可以分为两种。
第一种是硬依赖。如果功能 A 的实现必须基于功能 B,也就是说没有功能 B,功能 A 就是不可运行的,那么我们可以说 A 硬依赖于 B。
比如对于博客系统,评论功能肯定是基于文章功能的,因为评论的对象就是文章,脱离了文章,评论功能就没有意义。

1
2
3
4
5
6
7
8
9
import CommentList from './CommentList';
function ArticleView() {
return (
<div className="article-view">
<MainContent />
<CommentList />
</div>
);
}

第二种是软依赖。如果功能 B 扩展了功能 A,也就是说,没有功能 B,功能 A 自身也是可以独立工作的,只是缺少了某些能力。
同样对于博客应用,文章管理是主要的功能,而评论功能则可以认为是增强了文章的功能。
照此来看,即使没有评论功能,文章功能也是可以独立运行的。这样就可以认为文章功能软依赖于评论功能。
我们应该让文章功能相关的代码,不要硬依赖于评论功能的代码。

在业务功能上是一个软依赖,但是在代码实现层面,却往往做成了硬依赖。
这就导致随着功能的不断增加,整个应用变得越来越复杂,最终降低了整体的开发效率

让模块之间的交互不再通过硬依赖。
解决办法就是
扩展点机制:在任何可能产生单点复杂度的模块中,通过扩展点的方式,允许其它模块为其增加功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在需要扩展的地方定义扩展点
function ArticleView({ id }) {
const { article } = useArticle(id);
return (
<div className="article-view">
<MainContent article={article} />
{/* 定义了一个名为 article.footer 的扩展点 */}
<Extension name="article.footer" args={article} />
</div>
);
}

// 在被扩展的组件里,将自己挂到扩展点上,这样就算被扩展组件直接删除,对需要扩展的功能组件来说也毫无影响
extensionEngine.register('article.footer', article => {
return <CommentList article={article} />
});

软件复杂度产生的根源,来自复杂的依赖关系。
随着功能的增加,系统复杂度也在不断增加,那么整个项目就会到达一个不可维护的状态。
所以我们首先需要从项目结构层面,去对复杂度做物理上的隔离,确保业务模块相关的代码都能在独立的文件夹中。
其次,我们要妥善地处理业务模块之间的依赖关系。
不仅需要在业务上区分硬依赖和软依赖。
同时呢,在技术的实现层面也要能做到模块的松耦合。
当然,上面的所有介绍要落实到实际的项目,还有很多细节问题需要考虑,
比如如何避免在单点模块定义所有的路由,如何避免一个导航菜单组件包含了所有业务功能的导航逻辑,等等。
总结来说,这里是整个隔离复杂度的思路,可以根据实际场景进行有针对性的思考,进而解决复杂度的问题。
同时更为重要的是,在进行实际项目开发,尤其是大型项目的架构设计时,一定要时刻有管理系统复杂度的意识,
不能只考虑功能是否实现,而不管复杂度,那样终究会导致系统越来越复杂,不断降低开发和维护的效率,甚至导致项目失败。

一个扩展点引擎