企业项目的 Angular 项目结构

Jak*_*ova 3 angular

我即将开始一个相当大的项目,它将使用 Angular 作为它的前端,我有一个关于如何管理它的增长以最大化可维护性的问题。

到目前为止,对于每个 angular 项目,我都有一个模拟路由层次结构的视图文件夹,我的模块结构基于业务功能的分组。这对于新开发人员的入职以及这些中小型应用程序的维护非常有效。

我的问题更多地集中在何时在大型企业应用程序中使用模块。

我最近听说过为每个页面和/或每个组件创建一个模块。这种方法看起来像是大量的前期开销,但在测试创建和长期维护方面节省了很多。

我在 angular.io 风格指南中没有看到任何关于一种或另一种方式的指导,但我想从构建企业级 angular 应用程序的人那里知道他们是否找到了一种适合他们的特定方式。

更新 我在这里得到了很多很好的答案。我相信所有人都强调了模块的共同点,以及核心、共享(以及 bryan 提到的资源)的额外模块。我还将我的路由视图移动到它们各自的功能文件夹中,而不是我当前使用的“视图”文件夹中。阅读下面的答案后,随着应用程序的增长,“views”文件夹可能会变得难以管理。

有人还建议我阅读 Nrwl 正在做什么,所以我这样做了,并且对他们使用与应用程序分开的库很感兴趣。 Nrwl MonoRepo 模式书(免费)。他们有很多很好的建议,这些建议与这里每个人所说的相似,将跨平台的通用功能抽象到库中。因为我确信我正在构建的应用程序需要针对网络世界之外的移动设备,所以这似乎也是一个好主意。

感谢所有花时间详细回答的人。

Dea*_*ean 7

首先:多年来,我已经使用 Angular 构建了一些企业应用程序,并且看到了一些可行的东西以及我希望从未尝试过的东西。好消息是:重构/开发工具现在非常好用,当您发现项目结构变得不规则时,您可以在项目中切换项目结构。唯一的坏消息是:它会造成合并噩梦,从而测试你的 git-fu。

风格指南确实按功能提到了文件夹,这听起来像是您已经在做的事情。我会坚持你目前正在做的事情,并且它应该能够扩展。您已经有了使用它的经验,它正在工作,并且在您第一次运行更大的应用程序时尝试完全不同的东西听起来像是灾难的秘诀。

要避免的主要事情是拥有不必要的复杂文件夹结构,该结构实际​​上并不反映应用程序的结构。例如,如果您有一个配置文件管理部分,请不要将其放入/dashboard/user/components/profile/edit或类似的任意内容中,除非它实际上模仿您的应用程序结构。这似乎是显而易见的,但人们总是这样做,这使得您的代码更不容易被发现。我认为LIFT概念涵盖了这一点:

构建应用程序,以便您可以快速找到代码,一目了然地识别代码,尽可能保持最扁平的结构,并尽量保持干燥。

请务必定义结构以遵循这四个基本准则(按重要性顺序列出)。

为什么?LIFT 提供一致的结构,可良好扩展、模块化,并且可以通过快速查找代码更轻松地提高开发人员效率。要确认您对特定结构的直觉,请询问:我可以快速打开并开始处理此功能的所有相关文件吗?

它还提到尽可能保持扁平的文件夹结构:

尽可能长时间地保持扁平的文件夹结构。

当文件夹达到七个或更多文件时,请考虑创建子文件夹。

考虑配置 IDE 以隐藏分散注意力的不相关文件,例如生成的 .js 和 .js.map 文件。

为什么?没有人愿意通过七层文件夹来搜索文件。扁平结构易于扫描。

这是大型项目中最关键的一点之一。当您开发具有 20 个模块的应用程序时,不必要的复杂文件夹结构会带来轻微的烦恼。当你达到 150 个模块时,当你打开 IDE 时,你会本能地感到畏缩。总体结构指南是项目的良好起点,并演示了何时保留/feature/、何时使用子功能文件夹。

关于每个组件的模块:

为每个功能区域创建一个 NgModule。

为什么?NgModules 可以轻松地延迟加载可路由功能。

为什么?NgModule 使隔离、测试和重用功能变得更加容易。

您可以扩展它,说您应该为每个组件创建一个模块,但实际上我会避免它,除非您对给定模块有特定需求。再说一遍——根据我的经验,项目越大,为自己创造开销就越麻烦那些在小项目中看似有点烦人的事情,在大项目中却变成了噩梦。

最后一点:对改变持开放态度。您和您的同事可能会花一周的时间来规划您的项目结构,但一旦开始实际使用它,就会发现它感觉不对。第一次尝试很难做到 100% 正确。慢慢迭代直到达到近乎完美的效果会更容易。

我的项目通常看起来像这样:

app/
| core/ 
| | constants/         // Keep all constants in a single place and avoid magic IDs/strings.
| | |-http-status-codes.enum.ts
| | guards/            // I like to group my guards in a single place
| | http-interceptors/ // Same with interceptors
| | pipes/             // Some pipes might be section-specific but they are usually core
| | services/          // Core services. Utilities, error handling, etc.
| | |-error-handler.service.ts
| | validators/
| section1/
| | models/
| | sub1/              // I try not to nest too deeply
| | |-sub1.component.ts|html|css|spec.ts
| |-section1-routing.module.ts // Routing by section
| |-section1.component.ts|html|css|spec.ts
| |-section1.module.ts  // Module per section for lazy loading, etc.
| |-section1.service.ts // Section-specific service
| shared/
| | models/
| | app-modal-dialog/
| | my-awesome-widget/
| | some-custom-input/
|-app.component.ts|html|css|spec.ts
|-app.module.ts
|-app-routing.module.ts
assets/             // Static content
environments/
|-environment.x.ts  // Stripe public keys, etc.
Run Code Online (Sandbox Code Playgroud)

再次强调——这与风格指南非常一致。


bry*_*n60 5

这里有很多很好的答案,我发现在构建更大的应用程序时有一些细微的区别,到目前为止我没有看到提到,这是功能和资源之间的区别。功能是您的应用程序所做的事情,而资源是您的应用程序使用的内容。一个功能可能会使用一个或多个资源,我认为这对于反映在您的项目结构中很重要。

通常我会有类似的东西:

app/
  core/
    ... core stuff like nav bars and single use components or core services...
  app-shared/ (prefix it!!!!)
    ... shared app utilities like tables, accordions, validators, form helpers, pipes etc ...
  resource1/ <- represents some backend resource usually
    resource1.model <- the models
    resource1-model.service <- API interaction layer, single http calls
    resource1-domain.service <- abstraction for everything I can do with this resource (think combinations of multiple model service calls or model service calls with common defaults)
    views/
      ... here we have all the components (data views, forms etc) that concern only this resource and the needed view services ...
  ... rinse repeat for all app resources ...
  feature1/ <- this is an application feature that combines multiple resources or possibly only uses a single resource. this is pretty much primarily a page of your app
    feature1-application.service <- this combines the various resources needed for this feature
    feature1-container.component <- the prime container for this feature. does the service layer interactions and holds the views of this feature or the needed resource views
    views/ <- maybe not needed depending on the feature
      ... here is where we have components and view services that are part of this feature that combine multiple resources, these can contain resource views if needed ...
  ... rinse repeat for all app features ...
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 模型服务是专门针对单个资源的 API 交互,它们在 API 上调用一个且仅一个操作
  2. 领域服务采用模型服务并使它们对开发人员有用且易于使用。可以注入一个紧密相关的子资源模型服务。
  3. 应用服务结合多个领域服务
  4. 视图服务从域或应用程序服务中获取数据并为特定视图构建视图模型(请不要将后端模型直接传递给模板!如果/当后端模型被重构时,您会讨厌自己.. . 与此相关,使模板尽可能简单。不要使用复杂的模板表达式来确定按钮是否应该显示,构建一个具有“showButton”属性的视图模型并将其分配到那里!)
  5. 资源通常不会有路由,除非你喜欢这种路由模式。功能实际上是带有路由的应用程序结构,主要部分将指向主要容器,然后视图将是子视图(如果需要),但它们使用资源视图。
  6. 如果你做得对,理论上你应该能够直接将你的资源提升到另一个使用相同资源的应用程序中(当然还有一些样式)。
  7. 您可能不需要功能中的任何子视图的原因是因为容器可能就足够了。假设您有一个带有数据列表组件的教师资源和一个带有数据视图组件的学校资源,并且您想要一个并排显示这些组件的功能,这里的容器只需要定位这两个资源视图并通过以下方式促进它们之间的任何交互要素应用服务。容器不显示数据或具有 UX,它们只包含执行这些操作的视图。
  8. 为了让您的功能更好地匹配您真正的应用程序结构,并且还为将来向该功能添加更多资源提供灵活性,您经常也最终获得单一资源功能。
  9. 在共享子资源的情况下,紧密相关的资源视图可能以不同的资源视图结束。想想是否有一个标签资源具有特殊的多选自动完成表单。这是一个紧密耦合的共享子资源,可以出现在多个其他资源上,因此为了方便起见,它当然可以最终出现在资源视图中,尽管它们通常是每个资源视图的单个资源。
  10. 最重要的是:当您开始构建时,其中很多看起来就像您只是在构建到其他服务的奇怪的一对一传递(即,您的域可能看起来只是包装了您的模型,而您的应用程序可能看起来像是包装了您的域)但随着应用程序的发展,您会感谢您为构建这些层预先进行了一次性投资。我对企业前端开发人员的了解是,需求几乎每天都在变化,而且业务在看到实际应用之前永远无法确定他们想要什么。这些抽象层提供了快速响应不断变化的环境所需的灵活性,同时仍然可以轻松推理代码库。

  • 我倾向于将共享视为应用程序本身的本地内容。我犹豫是否要在那里使用术语“资源”,因为我严格使用它来定义实际模型以及我的 api 公开的或我的应用程序使用的内容。从某种意义上说,它们是外在的。但是我认为共享下的东西或者更多的是你可以放入库中的东西,例如表之类的东西(最近倾向于用于此目的的库),或者可能是应用程序特定的管道和验证器。我发现将任何“共享”资源(例如标签或代码类型对象)放入共享中会使它变得过于臃肿。 (2认同)