We have a medium sized Angular 4 application (+-150 components).
Many of these components require the injection of service classes and require the declaration of other components in the app.
An approach we have been experimenting with, and have found to be much more developer friendly, is to create a module per component. The module imports the child component modules and provides (or imports) all the services needed by the component. It also exports the component itself so that other components can reference it through the module.
It makes the composition of components a breeze and the setup of the test fixture of a component very simple (this is where there was previously a lot of repetition of dependencies and child component tree dependencies). This approach seems to match the component based architecture and allows for a form of encapsulation around what a components dependencies are. It feels just too good to be true ;)
My question is, what is the performance (or other) impact of having so many modules?
I consider one module per component the best way to design your Angular apps.
If it depends on other components, you can include only the component modules related to each component that is a direct dependency and don't need to care about indirect dependencies.
It may seem more work at first, but it will pay you back will less maintenace problems.
If ComponentA
depends on ComponentB
that depends on ComponentC
create a:
ModuleC
related to ComponentC
ModuleB
related to ComponentB
that imports ModuleC
ModuleA
related to ComponentA
that imports ModuleB
(it doesn't need to import directly ModuleC
)
If ComponentB
now depends on ComponentD
(like including <my-component-d>
in the template) and stops depending on ComponentC
you just change ModuleB
and all components that depend on ComponentB
will work fine, if you are using this approach.
Think now about hundreds of components. How many components will depend on ComponentB
?
I consider this problem similar with the old way (or not too old) of including js files in script tags:
<script src="build/a.js"></script>
<script src="build/b.js"></script>
<script src="build/c.js"></script>
And if you change b
to stop depending on c
and starts depending on d
, all pages that import b
have now to import d
, and so on, leading to a maintenance nightmare. You may also forget to remove (the now unneeded) c.js
and import it needlessly in some pages, increasing your boot time (or worse, you remove it from all files that import b.js
, but some page imports e.js
that depends on c.js
and you break some functionality of that page).
Instead I consider it much better to import:
<script src="build/bundle.js"></script>
or
<script src="build/vendor.js"></script>
<script src="build/main.js"></script>
and the dependencies are handled by a module bundler, like webpack
.
There is the approach of making a module with lots of components and just import it, but you end up importing several components that you don't use and if you use lazy loading, your modules may become huge, unless you import that module in AppModule, making your boot time increase.
You can still use the approach of one component per module with feature modules. Just import the component module into the feature module (instead of the component itself):
feature.module.ts:
imports: [
ComponentAModule,
ComponentBModule,
ComponentCModule,
]
(You may want to export them too).
I also consider this approach the best way when creating libraries, otherwise you would force the consumers of the library to import all the components, even if they only use 1 or 2 components in the library.
I'm experiencing this issue with ionic3
: the minified ionic-angular
library has 437KB
of which 292KB
are the components (hopefully it will change with ionic4
). I use just a few ionic
components, there was no need to import all of them, but I have no choice (unless I fork the repo or stop using it).
I have an app with 176 components and this was the best approach in my opinion. Before, I included several components in a feature module and it gave me some headaches later on. Also, it was harder when changing a component from a feature module to another (What about its dependencies? They were all mixed together).
I haven't found a satisfactory approach with services (@Injectable()
) tough.
In the end it's up to you the decision. This is my opinion based on my experience.
Success story sharing
npm run aot
: the total size of chunks has decreased by 7.6%. So thank you for your advises :-)