Reputation: 1142
Hey guys currently i am working in an application with Lazy features modules architecture implemented. Below you can see how the project is structured.
Since the application is keep on growing we decided to migrate it to Ngrx.
As is a new pattern for me i am searching for migration guidelines but i can only find ngrx guidelines when creating a project from scratch.
Could you please give me some hints, guidelines,where should i be careful , and possibly some steps summary?
Thanks you.
Upvotes: 0
Views: 1741
Reputation: 8032
guidelines
It's possible to lazy load your store but this has caused me more issues than benefits. For example getting a selected project based on the router url and loaded project entities which mixes two feature stores. The following article has provided me a nice way to split up the store whilst allow any part of the application access to store data:
https://itnext.io/ngrx-best-practices-for-enterprise-angular-applications-6f00bcdf36d7
For posterity the application structure looks like:
├── app
│ ├── app-routing.module.ts
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── components
│ ├── containers
│ │ └── my-feature
│ │ ├── my-feature.component.css
│ │ ├── my-feature.component.html
│ │ └── my-feature.component.ts
│ ├── models
│ │ ├── index.ts
│ │ └── my-model.ts
│ │ └── user.ts
│ ├── root-store
│ │ ├── index.ts
│ │ ├── root-store.module.ts
│ │ ├── selectors.ts
│ │ ├── state.ts
│ │ └── my-feature-store
│ │ | ├── actions.ts
│ │ | ├── effects.ts
│ │ | ├── index.ts
│ │ | ├── reducer.ts
│ │ | ├── selectors.ts
│ │ | ├── state.ts
│ │ | └── my-feature-store.module.ts
│ │ └── my-other-feature-store
│ │ ├── actions.ts
│ │ ├── effects.ts
│ │ ├── index.ts
│ │ ├── reducer.ts
│ │ ├── selectors.ts
│ │ ├── state.ts
│ │ └── my-other-feature-store.module.ts
│ └── services
│ └── data.service.ts
├── assets
├── browserslist
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── tslint.json
where should i be careful
Make sure your reducer returns the state unamended for undefined actions. You can test for this. There's no excuse not to test your reducers. These are pure functions and easy to test.
import * as fromProjects from './project.reducer'
import * as fromProjectState from './project.state'
describe('ProjectReducer', () => {
describe('undefined action', () => {
it('should return the default state', () => {
const { initialState } = fromProjectState
const action = {} as any
const state = fromProjects.reducer(initialState, action)
expect(state).toBe(initialState)
})
})
})
Take the time to ensure your actions types are correct - the errors are hard to debug. Due to boilerpate you'll likely copy and paste alot of code here. Again this can be tested.
describe('LoadProjectsFail', () => {
it('should create an action', () => {
const payload = { message: 'Load Error ' }
const action = new fromProjects.LoadProjectsFail(payload)
expect({ ...action }).toEqual({
type: fromProjects.LOAD_PROJECTS_FAIL,
payload,
})
})
})
Keep to the NgRx documentation - there's been a few changes and tutorials are normally at least one version behind. e.g.
this.store.pipe(select(projectSelectors.getProjectsLoading))
// versus
this.store.select(projectSelectors.getProjectsLoading)
steps summary
Pretty much the same as the link but a different order:
Root Store
root-store.module.ts
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { EffectsModule } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools'
import { environment } from 'src/environments/environment'
// import { MyFeatureStoreModule } from './my-feature-store/';
// import { MyOtherFeatureStoreModule } from './my-other-feature-store/';
@NgModule({
imports: [
CommonModule,
// MyFeatureStoreModule,
// MyOtherFeatureStoreModule,
StoreModule.forRoot({}),
EffectsModule.forRoot([])
// Store devtools
!environment.production
? StoreDevtoolsModule.instrument({
name: 'My App',
})
: [],
],
declarations: []
})
export class RootStoreModule {}
Add the following files which will start of pretty much empty:
index.ts
import { RootStoreModule } from './root-store.module'
import * as RootStoreState from './state'
import * as RootStoreSelectors from './selectors'
//export * from './employee'
//export * from './project'
//export * from './router'
export { RootStoreState, RootStoreSelectors, RootStoreModule }
state.ts
import { routerState } from './router'
import { employeeState } from './employee'
import { projectState } from './project'
export interface State {
router: routerState.State
employees: employeeState.State
projects: projectState.State
}
selectors.ts
import { createSelector } from '@ngrx/store'
import { Project } from './project/project.model'
import { routerSelectors } from './router'
import { projectSelectors } from './project'
export const getSelectedProject = createSelector(
projectSelectors.getProjectsEntities,
routerSelectors.getRouterState,
(entities, router): Project => {
return router.state && entities[router.state.params.id]
}
)
RootStoreModule
into app.module.ts
Feature Store
index.ts
, add to root index.ts
Upvotes: 3