83

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.

  1. Why do I need two modules?
  2. When should I import each one?
  3. Which imports, exports, declarations should each one have?
Jordi
  • 20,868
  • 39
  • 149
  • 333
  • I know it's been already 7 months since you asked this, but since the other answers were not complete and/or misleading and you didn't accept any of them, I have tried to answer it in details. Please accept it if you think it's being correctly answered to help other people that might have the same doubt. Cheers! – mattarau Oct 07 '17 at 17:21
  • I wrote an article to make this two concepts clear, you can find it here: https://medium.com/@benmohamehdi/angular-best-practices-coremodule-vs-sharedmodule-25f6721aa2ef – Mehdi Benmoha Dec 26 '18 at 20:56

4 Answers4

199

TLDR:

  1. CoreModule should have only services and be imported only once in the AppModule.
  2. 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.


RTFM:

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:

SharedModule

  • Create a SharedModule with the components, directives, and pipes that you use everywhere in your app. This module should consist entirely of declarations, most of them exported.
  • The SharedModule may re-export other widget modules, such as CommonModule, FormsModule, and modules with the UI controls that you use most widely.
  • The SharedModule should not have providers 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.

CoreModule

  • Create a CoreModule with providers for the singleton services you load when the application starts.
  • Import CoreModule in the root AppModule only. Never import CoreModule in any other module.
  • Consider making CoreModule a pure services module with no declarations.

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!


Why do I need two modules?

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:

Why do people organize their projects into multiple modules?

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:

  • The root module starts to become cluttered, with a code that is difficult to read and work with, since it must import all dependencies (e.g. 3rd party Modules), provide all services and declare all components, directives, and pipes. The more the app needs, the bigger this module will be.
  • The different functionalities don't have a clear boundary between them, making it more difficult, not just to understand the app's structure, but also to have different responsibilities on a team.
  • You can start having to solve conflicts between different parts of your app. For example, if you have directives or components that do the same thing in two different parts of your app, you'll either have to start using longer names to differentiate them or to rename them when importing.

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:

  • The Service and Widget groups are the only ones that have completely different values between them in each column.
  • The Domain and Routed groups are basically the same as the Widget group, only with limited or none exports.
  • The 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!

mattarau
  • 2,484
  • 1
  • 16
  • 20
  • Cool that you took the time to write all this. Can you comment on the following? In https://angular.io/guide/styleguide#core-feature-module they write: `Do gather application-wide, single use components in the CoreModule. Import it once (in the AppModule) when the app starts and never import it anywhere else. (e.g. NavComponent and SpinnerComponent).`. Do you see disadvantages with putting components like NavBarComponent in CoreModule? I upvoted your answer; but still, please comment on navbar in CoreModule, please :). – KarolDepka Apr 22 '18 at 23:21
  • 2
    I bet (and hope) the guy who downvoted this answer did not do it on purpose – Flavien Volken Jul 20 '18 at 11:33
  • @mattarau If you need a component like "main menu" in some of the pages, should it go in shared module? Please note that main menu is not needed always but in some of the pages including home page. – Eres Sep 08 '18 at 12:01
  • 1
    @KarolDepka Unfortunately, the docs and the guideline are written by different people. But as mentioned in both of them, they are just suggestions and each project and developer might feel more comfortable deviating a bit from them. I personally would put a NavBarComponent directly into the AppModule. – mattarau Sep 11 '18 at 04:33
  • 1
    @EresDev I'm not sure what you mean by 'page', but if each page has it's own module and just some of them will use the component, you clearly have a shared component there. But if you have a single PagesModule, where all pages components are declared, and this "main menu" is not used by any other module, you can simply import it in the PagesModule. – mattarau Sep 11 '18 at 04:38
  • how do lazy feature modules get bundled? What if my shred module has 10 UI components. Feature 1 module uses only one of them and feature 2 uses another one. Will bundles for these feature modules include all the components from the shared module or just these that are used by them? – Dmitry Efimenko Jan 31 '19 at 02:13
  • 10
    All references to `CoreModule` have been removed from the Angular Docs; it's no longer a recommended practice. The Singleton Services section using `providedIn: 'root'` seems to be the alternative https://angular.io/guide/singleton-services – Drenai May 06 '19 at 09:16
4

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.

maxime1992
  • 22,502
  • 10
  • 80
  • 121
  • that is not what StyleGuide for Angular says. Unless I understood it wrong – DAG Jun 22 '17 at 13:29
  • Then what did you understand ? – maxime1992 Jun 22 '17 at 15:46
  • "Do import all modules required by the assets in the CoreModule (e.g. CommonModule and FormsModule).". The StyleGuide also says to import CommonModule into SharedModule. What is the difference then? – DAG Jun 22 '17 at 17:43
  • I've made a small starter for ngrx that has this kind of setup, take a look https://github.com/maxime1992/angular-ngrx-starter/tree/master/src/app – maxime1992 Jun 22 '17 at 17:50
  • but for modules I want to use across my app, I only really want to import them once WHICH IS WHERE MY CONFUSION LIES. KISS! – jenson-button-event Jul 21 '17 at 14:18
  • In the styleguide, you can read "Do import all modules required by the assets in the CoreModule (e.g. CommonModule and FormsModule)." AND "Do import all modules required by the assets in the SharedModule; for example, CommonModule and FormsModule." ... so I am still confused. Where should go CommonModule for example ? – M'sieur Toph' Aug 17 '17 at 06:48
  • @Maxime Your explanation can be a bit misleading since you say to import ALSO the providers in the CoreModule, while it should have only providers (Your starter project follows the docs though). Plese, check my answer for more details. – mattarau Oct 07 '17 at 17:17
3

As per style guide of Angular and my observations:

CoreModule(core.module.ts) Core feature module

All services that should be singleton should be provided here. For example HelperService, LoggerService.

Application wide component should be declared in CoreModule like header, footer.

CoreModule provides one or more singleton services. Angular registers the providers with the app root injector, making a singleton instance of each service available to any component that needs them, whether that component is eagerly or lazily loaded.

Only the root AppModule should import the CoreModule.

SharedModule(shared.module.ts) Shared feature module

declare components, directives, and pipes in a shared module when those items will be re-used and referenced by the components declared in other feature modules

It is suggested to avoid using services in shared modules. Services are usually singletons that are provided once for the entire application or in a particular feature module.

Modules required by all feature modules should be imported in SharedModule like CommonModule & FormsModule.

Declaration of sharable component/pipe/directive should be in SharedModule. If these component/pipe/directive needs to be used by other feature module, they must exported.

If using Material, it's best place to import and re-export Angular Material components.

References: CoreModule, SharedModule

Anshuman Jaiswal
  • 5,352
  • 1
  • 29
  • 46
0

I think this question is too general and for general question, you'll have general answers ... For example, shared module is meant to keep what several components/modules use :

import { NgModule }            from '@angular/core';
import { CommonModule }        from '@angular/common'; 
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

//import { AwesomePipe }         from './awesome.pipe';
//import { HighlightDirective }  from './highlight.directive';

@NgModule({
  exports:      [ /*AwesomePipe, HighlightDirective,*/
                  CommonModule, FormsModule, ReactiveFormsModule ]
})
export class SharedModule { }

CoreModule is more like what you consider to be the core of your page (Nav, Spinner, Alert...). This is very suggestive and depend on your feeling I think. For example:

import { NgModule, Optional, SkipSelf } from '@angular/core';

import { NavComponent } from './nav/nav.component';
import { SpinnerComponent } from './spinner/spinner.component';
import { SpinnerService } from './spinner/spinner.service';
import { AlertComponent }     from './alert/alert.component';
import { AlertService }       from './alert/alert.service';

import { SharedModule }       from '../shared/shared.module';

import { CoreRoutingModule } from './core-routing.module';


@NgModule({
  imports: [ 
    SharedModule, 
    CoreRoutingModule,
  ],
  exports: [//Other modules use these components, so we export them
    AlertComponent, 
    NavComponent, 
    SpinnerComponent,

    ],
  declarations: [ //we use them in CoreModule, so we declare them
    AlertComponent, 
    NavComponent, 
    SpinnerComponent,

    ],
  providers: [
    SpinnerService, 
    AlertService,

  ]
})
export class CoreModule {
    constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
      if (parentModule) {
        throw new Error(
          'CoreModule est déjà chargé. Importer le uniquement dans AppModule');
      }
    }
}
mickdev
  • 2,765
  • 11
  • 15
  • This is not exactly true. Check my answer, where I explain why. – mattarau Oct 07 '17 at 17:10
  • You should not import the SharedModule inside the CoreModule – andreasonny83 May 17 '18 at 11:54
  • @andreasonny83 why? – KarolDepka Sep 12 '18 at 11:35
  • @KarolDepka is best practice to import the CoreModule inside your application's root module (AppModule in AngularCLI). All the reusable modules, to be installed inside your features modules, should reside inside a SharedModule. In this way, your root application won't need to load all the shared modules for bootstrapping your app. Importing the shared modules inside the CoreModule neutralize the benefit of having a SharedModule as they will be already installed inside your core application module. – andreasonny83 Sep 13 '18 at 10:27
  • @andreasonny83 Thank you. Could you clarify: for the end-user, is there a benefit of speeding up bootstrapping itself? Wouldn't the user have to wait anyway, for anything to be displayed, till SharedModule gets loaded anyway? – KarolDepka Oct 01 '18 at 22:23
  • Sure @KarolDepka. This has nothing to do with performance or lazy-loading. It is just the suggested pattern to keep your code organized following best practices. Your CoreModule should contain your singleton services, single-instance components, and export any third-party modules needed in AppModule. The SharedModule should contain common components/pipes/directives and also export commonly used Angular modules (like CommonModule from @angular/common for *ngIf directive). – andreasonny83 Oct 17 '18 at 14:45
  • 1
    how do you get to make the NavComponent routerLink to work if there is no RouterModule imported in the CoreModule – Melvin Otieno Mar 22 '19 at 13:41