企业项目的角度项目结构

时间:2019-07-08 15:23:43

标签: angular

我将要开始一个相当大的项目,该项目将在其前端使用Angular,并且我有一个问题,即如何管理Angular的增长以实现最大的可维护性。

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

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

我最近听说要在每个页面和/或每个组件创建模块。这种方法似乎有很多前期开销,但是在测试创建和长期维护方面节省了很多。

我没有在angular.io样式指南中看到任何关于一种或另一种方法的指导,但是我想从那些已经构建了企业规模的角度应用程序的人们那里找到了一种适合自己的特定方法,他们。

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

有人还建议我阅读Nrwl在做什么,所以我做到了,并且被他们与应用程序分开使用库而感到好奇。 Nrwl MonoRepo Pattern Book (Free)。他们有很多很好的建议,与大家在这里所说的相提并论,除了将跨平台的通用功能抽象到库中之外。由于我确定要构建的应用程序将需要定位到网络世界之外的移动设备,因此这似乎也是一个好主意。

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

4 个答案:

答案 0 :(得分:3)

我将在每个域中使用一个模块,例如身份验证模块,其中有注册,登录和忘记密码,产品模块,付款模块等。每个视图一个模块是总开销。 Angular文档强烈支持通用服务的核心模块,以及跨App的通用组件和指令的共享模块。每个模块都应根据需要具有组件,指令和服务目录。您也可以使用barrel,因为目录中的模块中有许多用于干净导入的元素。强烈建议不要嵌套组件目录。

为具有多个视图的模块创建单独的路由模块是一个好主意。

// barrel 
import { firstComponent, .... ,lastComponent } from './components'; 

const routs = [ ... ];

export class FeatureRoutingModule { 
    const static components = [
       firstComponent,
             .
             .
             .
       lastComponent
    ]
};

然后在功能模块中可以用这种方式导入它。如果您分开布线,这将帮助您避免多次导入组件。

import { FeatureRoutingModule } from './freature-rotuing.module';
@NgModule({
  imports: [
    SharedModule, // if you have one
    FeatureRoutingModule
  ],
  declarations: [FeatureRoutingModule.components]
  })
  export class FeatureModule { }

祝您好运,更好地组织代码。

答案 1 :(得分:3)

首先,我在构建应用程序时尝试使用LIFT指南:

  • 定位我们的代码很容易
  • 一目了然地识别代码
  • 只要可以的话,就采用扁平结构
  • 尝试保持干燥(不要重复自己)

然后我的文件夹结构如下:

app/
  core/
    models/             // All models
    not-found/          // A core feature (not-found component for example)
    ...
    core.module.ts
  feature1/             // Feature 1 folder
    sub-feature1-1/     // A sub feature for the feature 1
    sub-feature1-2/     // A sub feature for the feature 1
    feature1.service.ts
    feature1.module.ts
  feature2/             // Feature 2 folder
    sub-feature2-1/     // A sub feature for the feature 2
    sub-feature2-2/     // A sub feature for the feature 2
    feature1.service.ts
    feature1.module.ts
  shared/
    card/               // A shared feature (card component for example)
    ...
    shared.module.ts
  app-routing.module.ts
  app.component.html
  app.component.scss
  app.component.spec.ts
  app.component.ts
  app.module.ts

在这种结构中,我总是尝试在子功能上仅保持一个级别。所有功能都应尽可能独立,并且仅应承担一项责任。

答案 2 :(得分:3)

首先:多年来,我已经使用Angular构建了一些Enterprise应用程序,并且看到了行之有效的方法以及我从未尝试过的方法。好消息是:如今,重构/开发工具非常好,当您发现项目结构变得不规则时,可以在项目中切换项目结构。唯一的坏消息是:它将创建合并梦night,以测试您的git-fu。

样式指南确实提到了folders by feature,听起来像您已经在做。我会坚持您当前正在做的事情,并且应该按比例缩放。您已经有使用它的经验,它可以正常工作,并且第一次运行更大的应用程序时尝试完全不同的尝试听起来像是灾难的秘诀。

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

  

执行结构化应用程序,以便您可以快速定位代码,一目了然地识别代码,保持最扁平的结构并尝试变干。

     

执行,定义结构以遵循这四个基本准则,并按重要性顺序列出。

     

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

接着提到要尽可能保持平面文件夹结构:

  

要做,请尽可能保持平面文件夹结构。

     

考虑,当文件夹包含七个或更多文件时,创建子文件夹。

     

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

     

为什么?没有人想要通过七个级别的文件夹搜索文件。扁平结构易于扫描。

对于大型项目,这是最关键的要点之一。当您使用具有20个模块的应用程序时,不必要的复杂文件夹结构会带来一点麻烦。当您使用150个模块时,本能地在打开IDE时畏缩。 overall structural guidelines是项目的一个很好的起点,它演示了何时保留/feature/和何时使用子功能文件夹。

关于每个组件的模块:

  

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

     

为什么? NgModules使延迟加载可路由功能变得容易。

     

为什么??NgModules使隔离,测试和重用功能更加容易。

您可以扩展为说应该为每个组件创建一个模块,但是实际上我会避免使用它,除非您对给定的模块有特定的需求。再一次-根据我的经验,为您自己创建开销会变得更大,而您的项目会变得更多。在小型项目中那些看起来有些烦人的事情变成了大型项目中的噩梦。

最后说明:可以更改。您和您的同事可以花一个星期的时间来规划您的项目结构,而一旦真正开始使用它,就会发现它是错的。第一次尝试就很难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.

再次-与style guide非常一致。

答案 3 :(得分:3)

这里有很多很好的答案,我发现有一些细微的差别,我发现它们有助于构建到目前为止尚未提及的大型应用程序,这是功能与资源之间的区别。功能是您的应用程序执行的事情,资源是您的应用程序使用的事情。一个功能可能正在使用一个或多个资源,我认为这一点很重要,以反映到您的项目结构中。

通常我会遇到类似的东西:

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 ...

注释:

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