如何以可扩展的方式构建您的应用程序。
我发现自己正在处理的最好的代码库的文件夹是围绕应用程序提供的功能构建的。
有些人可能会说它非常接近领域驱动设计的有界上下文原则。
我们将构建的应用程序
将应用程序视为包含功能的功能可以创建一个强大的心理模型,该模型可以轻松映射到项目的文件夹结构。
以下示例将引用用 JavaScript 编写的单页应用程序,该应用程序由以下构建块组成:
- 路由 - 使用 URL 时显示的根组件,
- 组件——处理状态和渲染的逻辑单元,
- 查询 - 调用 HTTP API 的函数,
- 样式 - 与特定组件绑定的 CSS,
- 服务——处理总体问题的逻辑单元
请记住,此模式可以应用于任何编程语言、框架和问题领域。
例如,游戏可以使用着色器、预制件、实体和系统作为自己的构建块。
我的目标是以一种易于理解的方式呈现这个想法。因此,在讲解演进步骤时,我会做一些简化。
创世纪
我们的应用程序将以登录和注册功能开始。
它应该能够获取输入数据并与 API 通信。
当用户登录时,他们将能够看到某种消息,表明他们有一个活动会话。
最简单的方法是从一个文件开始。从这里开始,我们将采取几个步骤。
src/
├─ index.js
├─ style.css
当有人打开index.js
文件时,这些特征就会显现出来。
水桶
现在,假设企业希望应用程序能够实现更多功能。他们希望用户登录后能够看到包含重要数据图表的仪表板。
你开始编写代码,在某一时刻,你会感到内疚......index.js
文件变得太大,你认为作为一名优秀的工程师,你应该更好地组织你的代码。
通常,人们将代码组织到我喜欢称之为存储桶的地方,最终得到类似这样的结果。
src/
├─ services/
│ ├─ session.service.js
├─ components/
│ ├─ button.component.js
│ ├─ input.component.js
│ ├─ piechart.component.js
│ ├─ linechart.component.js
├─ routes/
│ ├─ login.route.js
│ ├─ register.route.js
│ ├─ dashboard.route.js
├─ styles/
│ ├─ input.component.css
│ ├─ button.component.css
│ ├─ piechart.component.css
│ ├─ linechart.component.css
│ ├─ dashboard.route.css
│ ├─ login.route.css
│ ├─ register.route.css
├─ queries/
│ ├─ login.query.js
│ ├─ register.query.js
│ ├─ dashboard.query.js
├─ index.js
├─ style.css
目前来看,这是否存在客观问题?没有。感觉可能还好,因为每个概念都有各自的存储桶。功能性不强,但随着它的发展,你的感觉可能会改变。
更多功能
现在,业务部门建议我们添加一些报告,让用户能够查看关键信息,例如,他们赚了多少钱,亏了多少钱。这些报告预计将包含表格数据和图表。
让我们在桶中添加更多内容。
src/
├─ services/
│ ├─ session.service.js
├─ components/
│ ├─ button.component.js
│ ├─ input.component.js
│ ├─ data-table.component.js
│ ├─ piechart.component.js
│ ├─ linechart.component.js
│ ├─ barchart.component.js
├─ routes/
│ ├─ login.route.js
│ ├─ register.route.js
│ ├─ dashboard.route.js
│ ├─ loses-report.route.js
│ ├─ gains-report.route.js
├─ styles/
│ ├─ input.component.css
│ ├─ button.component.css
│ ├─ data-table.component.css
│ ├─ piechart.component.css
│ ├─ linechart.component.css
│ ├─ barchart.component.css
│ ├─ dashboard.route.css
│ ├─ login.route.css
│ ├─ register.route.css
│ ├─ loses-report.route.css
│ ├─ gains-report.route.css
├─ queries/
│ ├─ login.query.js
│ ├─ register.query.js
│ ├─ dashboard.query.js
│ ├─ gains-report.query.js
│ ├─ loses-report.query.js
├─ index.js
├─ style.css
有很多文件散落在各处。
问自己以下问题。
您是否能立即看出该应用程序包含哪些功能?
哪些特征相互依赖是否清楚?
功能驱动的文件夹结构
让我们退一步,写下该应用程序涵盖的功能和关注领域。
- 登录
- 接收数据输入
- 关注当前会话
- 登记
- 接收数据输入
- 关注当前会话
- 仪表板
- 通过图表进行可视化
- 关注当前会话
- 损失报告
- 通过数据表进行可视化
- 通过图表进行可视化
- 关注当前会话
- 收益报告
- 通过数据表进行可视化
- 通过图表进行可视化
- 关注当前会话
将整个应用程序视为一个功能。
另外,将每个要点视为一个单独的功能。
每个特征都专门针对一个问题领域。
某些功能在功能之间是共享的。
让我们将其映射到文件夹结构。
请记住,结构可能会因从事代码库工作的人员和团队而异!
src/
├─ shared/
│ ├─ session/
│ │ ├─ session.service.js
│ ├─ data-table/
│ │ ├─ data-table.component.js
│ │ ├─ data-table.component.css
│ ├─ data-input/
│ │ ├─ button.component.js
│ │ ├─ button.component.css/
│ │ ├─ input.component.js/
│ │ ├─ input.component.css
│ ├─ charts/
│ │ ├─ piechart.component.js
│ │ ├─ piechart.component.css
│ │ ├─ linechart.component.js
│ │ ├─ linechart.component.css
│ │ ├─ barchart.component.js
│ │ ├─ barchart.component.css
├─ login/
│ ├─ login.route.js
│ ├─ login.route.css
│ ├─ login.query.js
├─ register/
│ ├─ register.route.js
│ ├─ register.route.css
│ ├─ register.service.js
│ ├─ register.query.js
├─ dashboard/
│ ├─ dashboard.route.js
│ ├─ dashboard.route.css
│ ├─ dashboard.query.js
├─ gains-report/
│ ├─ gains-report.route.js
│ ├─ gains-report.route.css
│ ├─ gains-report.query.js
├─ loses-report/
│ ├─ loses-report.route.js
│ ├─ loses-report.route.css
│ ├─ loses-report.query.js
├─ style.css
├─ index.js
再次问自己以下问题。
您是否能立即看出该应用程序包含哪些功能?
哪些特征相互依赖是否清楚?
根据我的经验,如果开发人员需要修改代码,他们可以立即知道应用程序具有哪些功能以及他们必须去哪里。
功能的功能...功能的功能?
我在应用此模式时遇到的问题是共享程序扩展到难以管理的大小,从而产生了与“桶”方法类似的问题。
有一个技巧可以解决这个问题。
看一下上面的结构,并尝试说出哪些共享特征与所有内容无关?
...
图表和*数据表功能。
要记住的重要一点是,特征驱动模式对于结构的深度没有限制。
它应该尽可能深或尽可能平整以确保舒适度,这是主观的。
查看以下示例,了解如何使结构更好地表示特征之间的关系。
src/
├─ shared/
│ ├─ session/
│ │ ├─ session.service.js
│ ├─ data-input/
│ │ ├─ button.component.js
│ │ ├─ button.component.css/
│ │ ├─ input.component.js/
│ │ ├─ input.component.css
├─ login/
│ ├─ login.route.js
│ ├─ login.route.css
│ ├─ login.query.js
├─ register/
│ ├─ register.route.js
│ ├─ register.route.css
│ ├─ register.service.js
│ ├─ register.query.js
├─ reporting/
│ ├─ data-table/
│ │ ├─ data-table.component.js
│ │ ├─ data-table.component.css
│ ├─ charts/
│ │ ├─ piechart.component.js
│ │ ├─ piechart.component.css
│ │ ├─ linechart.component.js
│ │ ├─ linechart.component.css
│ │ ├─ barchart.component.js
│ │ ├─ barchart.component.css
│ ├─ dashboard/
│ │ ├─ dashboard.route.js
│ │ ├─ dashboard.route.css
│ │ ├─ dashboard.query.js
│ ├─ gains-report/
│ │ ├─ gains-report.route.js
│ │ ├─ gains-report.route.css
│ │ ├─ gains-report.query.js
│ ├─ loses-report/
│ │ ├─ loses-report.route.js
│ │ ├─ loses-report.route.css
│ │ ├─ loses-report.query.js
├─ style.css
├─ index.js
现在,当您遍历代码库时,您可以清楚地看到您正在查看的内容以及您考虑的依赖关系。
这样,您可以根据需要添加任意数量的功能,并且结构复杂性应该与应用程序试图解决的实际问题成正比。
最后的话
请记住,以功能驱动的方式组织代码时有很大的空间,人们可以提出不同的结构。
不存在客观正确的结构。
您还可以混合使用“存储桶”和功能驱动的方法。
这是因为有时将共享的单个组件放入组件文件夹中可能更容易避免出现许多单个文件夹。
重要的是定义你自己的经验法则并坚持它们。
随着代码库的发展,您可以随时反思并重构结构。
文章来源:https://dev.to/pietmichal/how-to-struct-your-app-in-a-way-that-scales-bkf