I prefer to follow the one component per module design pattern, and use lazy modules for features with a single main module bootstrapping everything.
The main module should have the least dependencies necessary to start the application as this improves loading performance, and the top level route should be lazy loaded. So when the application starts the first feature lazy loaded is the main application. All other lazy modules are child routes.
A lazy module should only have the outlet components and services (providers) needed for that feature. All the other visual components come from a shared folder that contains one component per module directories.
This design pattern does follow the design guide but has a finer grain of modules. It does create more boilerplate code and many more files, but there are both maintainability benefits and compile time benefits (i.e. tree shaking is far more effective).
A typical project structure for me:
src/app
+-- lazy
+-- main
+-- shared
/src/app/lazy
contains subfolders of lazy modules.
/src/app/main
contains the AppModule
that is bootstrapped.
/src/app/shared
contains subfolders of one component per module directories
A typical lazy module would be structure like this
src/app/lazy/feature
+-- OutletFeature
+-- feature.module.ts
+-- feature-routing.module.ts
/src/app/lazy/feature
contains the module and routing
/src/app/lazy/feature/OutletFeature
is target component for the lazy route.
The lazy feature would contain guards, resolvers and services. You can also add additional components if they are unique to the feature, but I try to keep this to a minimal, because often they end up getting re-used when a similar feature is added later.
The shared folder contains the components.
src/app/shared/Example
+-- Example/example.component.ts
+-- example.module.ts
Above is an Example component with a module of the same name. Following the component directory pattern a folder of the same name holds the component. Shared modules are not limited to a single component, but the consumer of the component should only expect to see one component exported by the module. In the example of a table, the shared component would be a table, but the module might contain inner components for the header, rows and footer. The idea is that the module is of a single purpose.
Everything above is an opinion, and the style guide offers a good pattern for a small project. I've used the above on several large scale applications and feel that it scales very well.
Angular Material library uses the one component per module pattern as an example. They have a custom build process which makes the structure of their source code a little confusing at first, but once you get into the components section everything is a single component.
https://github.com/angular/components