-
Notifications
You must be signed in to change notification settings - Fork 6
Project Structure
This article shows how an Angular project can be structured. The actual structure of our example application can be viewed in the repository. The basic structure is shown here:
/src
/app
/domain1
/model
/shared
/domain2
/domain2.1
/domain3
/shared
/environments
/model
/business
/data
/support
/store
/testing
In the last section a more detailed basic structure is shown.
Prior to Angular 15 all applications have been structured by using Angular modules for the domains and packages. Since version 15 the concept of Standalone Components became stable. Together with several Standalone API's this concept became a simple and lightweight alternative to the module-based approach, which fits bets for most scenarios. Therefore the Standalone Components are the preferred approach for the structure definition shown here.
Nevertheless, there are scenarios where the module-based approach is better suited. That's not a problem, both concepts can be integrated in both directions flawlessly. This is also demonstrated in the example application of this styleguide.
Scenarios for better using modules:
- When a module has internal components which are not intended to be re-used by other components
- A module does not exports internal components
- However, a package of standalone components can use EcmaScript barrels for allowing re-use of the exported components only
- When tightly integrated components of a complex package need to use their internals across the entire module
- But that might be caused by design problems of missing separation then
- When having a package of similar components to avoid redundant definitions of providers etc. by using one module definition
- During migration of existing Angular applications to the Standalone approach (just a temporary usage of modules)
Subject-related domains are substructured into subdomains, components, services and Redux-store elements. Additionally, they encapsulate child elements and packages in a domain package. Within a domain, shared or cross-domain elements are kept in a shared folder within the domain and encapsulated in a separate package. This allows other domains to use shared elements via the separate shared package without having to load the remaining non-shared elements of the packages. In our example application a subject-related domain would be the catalog with its subdomain product.
Reason:
Since UIs are usually structured according to the needs of the user, a good relationship between UI and code can be established by analogous structuring of the code.
Subject-related domains represent subject-related epics. This means that user stories are limited to the domain-specific parts of the code, making it easier to work in parallel on different branches and then merge and integrate.
Agile teams are usually organized on a technical and interdisciplinary basis, not on a technical level. Accordingly, the parallel work of several teams in large projects is easier by thinking and structuring in "subject-related packages" aka domains.
Subject-related, application wide elements (src/app/shared)
Within the shared-folder on app-level there are technically independent components like controls, utils (pure functions, pipes, etc.) and services. This is also a good place to define global types, that are often used. These are encapsulated in a package and loaded directly via the app configuration.
Reason:
Subject-related elements must be structured in a technically independent manner.
Model (src/model)
In the model folder all data types (objects) and possible business logic are kept. Data types (objects) can be organized in packages.
Reason:
Data types and their structure already represent a certain subject-related logic and are decoupled from the UI elements by a separate structure. In addition, they very often intersect several domains and are therefore difficult to assign to a domain.
Support (src/support)
In the support folder the root-store for the whole application is stored as well as testing files that are relevant for the whole application. In general, the support folder contains technical and infrastructural components and configuration.
It is allowed to put multiple data types in a single file if they are tightly connected to each other. In this case it is adviced that the ordering of types in a file is done in a way that supports reading and understanding the types and their interconnection.
Typeguards have to be put in the same file as the type itself.
Pure functions that don't have side effects must be put in a separate file in the domain folder. It is allowed to have multiple files to separate independent sets of functions from each other.
Functions that have side effects and/or use Angular's dependency injection mechanism must be put in Angular services.
/src
/app
/domain1
/components
/component1
- component1.component.css
- component1.component.html
- component1.component.ts
- component1.component.spec.ts
/component2
/services
/service1
- service1.service.ts
- service1.service.spec.ts
/service2
/store
- actions.ts
- reducers.ts
- selectors.ts
- epics.ts
/substore1
- actions.ts
- reducers.ts
- selectors.ts
- epics.ts
/substore2
/model
- domain-specific-logic1.ts
/shared // <-- provided by the domain with subject-related reference
/components
/services
- module1.module.ts // <-- just in case of a module-based domain
- module1.routing-module.ts // <-- just in case of a module-based domain
/domain2
/domain3
/shared // <-- application wide without subject-related reference
/components
/component1
- component1.component.css
- component1.component.html
- component1.component.ts
- component1.component.spec.ts
/component2
/component3
/services
/service1
- service1.service.ts
- service1.service.spec.ts
/service2
- app.component.css
- app.component.html
- app.component.ts
- app.component.spec.ts
- app.config.ts
- app.routes.ts
/assets
/environments
/model
/business
- business-logic1.ts
- business-logic1.spec.ts
/data
/package1
/subpackage1
- subdata-type1.ts
/subpackage2
- data-type1.ts
- data-type2.ts
/package2
/package3
/support
/store
- navigation.actions.ts
- root.actions.ts
- root.epics.ts
- root.reducer.ts
/testing
- observable.matchers.ts
- main.ts
- package.json
- ...