Could somebody explain what's a SharedModule
and a CoreModule
stand for?
I've been watching several project out there are using this approach in order to build its angular projects.
imports
, exports
, declarations
should each one have?While you can do everything within the root module, feature modules help you partition the application into focused areas. A feature module collaborates with the root module and with other modules through the services it provides and the components, directives, and pipes that it shares.
The Core Module is where we want to put our shared singleton services. So the services that we want only one instance of while having them shared among multiple modules should live here. The Angular injector creates a new instance of a service for each lazily loaded module it is provided.
Your core module should contain the code that is specific to your application and implements the Cross-Cutting Concerns of your applications — that is the “plumbing, electricity, and heating” of your house when comparing building an app to building a house if you will.
CoreModule
should have only services
and be imported only once in the AppModule
.SharedModule
should have anything but services
and be imported in all modules that need the shared stuff (which could also be the AppModule
).But:
Real-world modules are often hybrids that purposefully deviate from the [above] guidelines. These guidelines are not laws; follow them unless you have a good reason to do otherwise.
So, after going through the whole NgModules' docs (plus some other stuff to understand the context), I easily found the answer for your 2nd and 3rd questions there. Here are they:
- Create a
SharedModule
with thecomponents
,directives
, andpipes
that you use everywhere in your app. This module should consist entirely ofdeclarations
, most of them exported.- The
SharedModule
may re-export other widget modules, such asCommonModule
,FormsModule
, and modules with the UI controls that you use most widely.- The
SharedModule
should not haveproviders
for reasons explained previously. Nor should any of its imported or re-exported modules have providers. If you deviate from this guideline, know what you're doing and why.- Import the
SharedModule
in your feature modules, both those loaded when the app starts and those you lazy load later.
- Create a
CoreModule
withproviders
for the singleton services you load when the application starts.- Import
CoreModule
in the rootAppModule
only. Never importCoreModule
in any other module.- Consider making
CoreModule
a pure services module with nodeclarations
.
Although these are more detailed than the TLDR
version above and you now can go on and keep coding, it mentions some things that you have to read the docs to understand (i.e. 'widget module", "reasons explained previously", "pure service modules"), and it also doesn't answer your 1st question.
So let's try to do that!
You don't need two modules. This is what is in the docs:
The root module is all you need in a simple application with a few components.
With that said, it's important to make a different question first:
The basic explanation for this comes right after the above statement in the docs:
As the app grows, you refactor the root module into feature modules that represent collections of related functionality. You then import these modules into the root module.
But you asked "why?" and this requires more than a basic explanation. So let's start explaining what are some problems that can happen if your app starts to grow and you don't separate functionalities into feature modules:
Although you may think that the examples above are not exactly problems (maybe you work alone and can live with your own mess or all of your teammates are also messy), keep in mind that other people will certainly not agree with you and that's why you "have been watching several project[s] out there ... using this approach".
Considering that you agree with that and want to organize your growing app into feature modules, please note the following statements from the docs:
The root module and the feature module share the same execution context. They share the same dependency injector, which means the services in one module are available to all.
The modules have the following significant technical differences:
- You boot the root module to launch the app; you import a feature module to extend the app.
- A feature module can expose or hide its implementation from other modules.
Information to never forget: "services in one module are available to all [modules]", while other things, like Components, Directives, and Pipes must be injected in each module that wants to use them.
With this information we can now go closer to what you want to know by answering the following question:
Are all feature modules equal?
NO! At least it is suggested that they shouldn't be equal, as it is suggested that you shouldn't have just the root module, but you can do whatever you want. But you shouldn't.
The docs have a table showing the differences between the suggested feature module groups. Let's take a look on an excerpt of it:
FEATURE SHOULD HAVE SHOULD HAVE SHOULD HAVE MODULE DECLARATIONS PROVIDERS EXPORTS Domain Yes Rare Top component Routed Yes Rare No Routing No Yes (Guards) RouterModule Service No Yes No Widget Yes Rare Yes
ATTENTION! They make it pretty clear that this is a...
...preliminary guidance based on early experience using NgModules in a few applications.
So, again, you're not stuck to this guidelines and deviations can happen, but you should know what you're doing and why.
Now let's analyze this table:
Service
and Widget
groups are the only ones that have completely different values between them in each column.Domain
and Routed
groups are basically the same as the Widget
group, only with limited or none exports.Routing
group is basically a Service
group, with an export exception and limited to a specific provider.So, let's consider the Domain
, Routed
and Routing
groups just variants of Service
or Widget
and focus on these last two.
Services should call your attention. Remember that you should never forget that "services in one module are available to all [modules]"? Well, if they call a feature module group Services
, that's because it must be isolated from the other modules so it can be imported only once. As an example, you can have an UserModule
that consists of services like SignUpService
, SignInService
, SocialAuthService
and UserProfileService
. Wherever you import that UserModule
, all of its services will be available app-wide. And as per the above table, it should not have declarations nor exports, just the providers.
Widgets sounds more generic, but it should tell you something. Remember that you should also never forget that "other things, like Components, Directives, and Pipes must be injected in each module that wants to use them."? So this is the type of module you will use for those. For example, you can have an UIModule
with ButtonComponent
, NavComponent
, SlideshowComponent
, HighlightLinkDirective
, CtaPipe
. And every time you need to use one or all of its exported elements, you import just the UIModule
.
So, basically, due to how Angular deals with Services, when you start splitting functionalities into feature modules, you have to isolate the services into their own module(s), while the other stuff can be organized between them as you wish.
How do CoreModule
and SharedModule
fit into this?
To keep it simple, the CoreModule
is a Service
module and the SharedModule
is a Widget
module. And that's why you should import the first only once in the AppModule
and the latter in all modules that need it. From my examples above, the UserModule
would be imported by the CoreModule
and the UIModule
by the SharedModule
.
But, as stated before, these are guidelines and deviations can happen, even in their own examples they declare components in the CoreModule
, but with an observation:
This page sample departs from that advice by declaring and exporting [in the
CoreModule
] two components that are only used within the root AppComponent declared by AppModule. Someone following this guideline strictly would have declared these components in the AppModule instead.
Personally, I think that the biggest confusion is regarding the naming choices. In general, people will think that everything that is part of your app's core (i.e. User stuff, NavBar, load bar, toasters etc) will go into the CoreModule
and everything that's shared across multiple features would go into the SharedModule
.
That's not actually true and a bit misleading, since all services are shared between all modules "by nature" and no service should be included in the SharedModule
, as well as a NavbarComponent
is part of the core of your app and no component should be included in the CoreModule
.
In any case, the recommendation is to follow the guidelines until you find a reason not to do so.
And here is the rest of the table above to help better understanding the guidelines:
FEATURE CAN BE SOME MODULE IMPORTED BY EXAMPLES Domain Feature, AppModule ContactModule (before routing) Routed Nobody ContactModule, DashboardModule, Routing Feature (for routing) AppRoutingModule, ContactRoutingModule Service AppModule HttpModule, CoreModule Widget Feature CommonModule, SharedModule
Cheers!
I do use this approach myself and here's why/how :
(it's one approach and maybe other people will have != ideas which is fine)
I like to keep the app.module
as clean as possible. If you want to use universal or build your project with AOT (when not using angular-cli) you may need to have a duplicated app.module with small changes in all those files.
So if you import many modules into your app.module
, you'll have to maintain that list up to date in different files.
Here comes the core.module
:
Put every module you only want to import once here. Mainly, modules with forRoot
methods (the ones that exports their providers and that should be imported only once).
Import also your providers here. (if you use ngrx for example, declare your store here).
Then, the shared.module
:
Put every module you'll have to reuse across your app (CommonModule, HttpModule, RouterModule, MaterialModule, FlexLayoutModule, etc).
Finally, app.module
:
Import in app.module
your core.module
ONLY HERE. CoreModule should be loaded only once. In all your submobules, you can load the SharedModule
.
With that configuration, if you need to create another app.module for universal or others, you don't have to copy and maintain the whole module list in different files. Simply import the core.module
and you're good to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With