DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 968,547 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Alfredo Perez
Alfredo Perez

Posted on

NGRX Workshop Notes - Folder Structure

Following the LIFT principle:

  • Locating our code is easy
  • Identify code at a glance
  • Flat file structure for as long as possible
  • Try to stay DRY - don’t repeat yourself

Key Takeaways

  • Put state in a shared place separate from features
  • Effects, components, and actions belong to features
  • Some effects can be shared
  • Reducers reach into modules’ action barrels


Folder structure followed in the workshop

β”œβ”€ books\
β”‚     actions\
β”‚         books-api.actions.ts
β”‚         books-page.actions.ts
β”‚         index.ts                  // Includes creating names for the exports
β”‚      books-api.effects.ts
β”‚     
β”œβ”€ shared\
β”‚       state\
β”‚          {feature}.reducer.ts     // Includes state interface, initial interface, reducers and local selectors
β”‚          index.ts
β”‚ 
Enter fullscreen mode Exit fullscreen mode
  • The index file in the actions folder was using action barrels like the following:
import * as BooksPageActions from "./books-page.actions";
import * as BooksApiActions from "./books-api.actions";

export { BooksPageActions, BooksApiActions };
Enter fullscreen mode Exit fullscreen mode
  • This made easier and more readable at the time of importing it:
import { BooksPageActions } from "app/modules/book-collection/actions";
Enter fullscreen mode Exit fullscreen mode


Folder structure followed in example app from @ngrx

β”œβ”€ books\
β”‚     actions\
β”‚         books-api.actions.ts
β”‚         books-page.actions.ts
β”‚         index.ts                  // Includes creating names for the exports
β”‚     effects\
|         books.effects.spec.ts 
|         books.effects.ts 
|     models\
|         books.ts 
β”‚     reducers\
|         books.reducer.spec.ts 
|         books.reducer.ts 
|         collection.reducer.ts 
|         index.ts 
β”‚     
β”œβ”€ reducers\
β”‚        index.ts  /// Defines the root state and reducers
β”‚ 
Enter fullscreen mode Exit fullscreen mode
  • The index file under the reducers folder is in charge of setting up the reducer and state
import * as fromSearch from '@example-app/books/reducers/search.reducer';
import * as fromBooks from '@example-app/books/reducers/books.reducer';
import * as fromCollection from '@example-app/books/reducers/collection.reducer';
import * as fromRoot from '@example-app/reducers';

export const booksFeatureKey = 'books';

export interface BooksState {
  [fromSearch.searchFeatureKey]: fromSearch.State;
  [fromBooks.booksFeatureKey]: fromBooks.State;
  [fromCollection.collectionFeatureKey]: fromCollection.State;
}

export interface State extends fromRoot.State {
  [booksFeatureKey]: BooksState;
}

/** Provide reducer in AoT-compilation happy way */
export function reducers(state: BooksState | undefined, action: Action) {
  return combineReducers({
    [fromSearch.searchFeatureKey]: fromSearch.reducer,
    [fromBooks.booksFeatureKey]: fromBooks.reducer,
    [fromCollection.collectionFeatureKey]: fromCollection.reducer,
  })(state, action);
}
Enter fullscreen mode Exit fullscreen mode
  • The index file under app/reducers/index.ts defines the meta-reducers, root state, and reducers
/**
 * Our state is composed of a map of action reducer functions.
 * These reducer functions are called with each dispatched action
 * and the current or initial state and return a new immutable state.
 */
export const ROOT_REDUCERS = new InjectionToken<
  ActionReducerMap<State, Action>
>('Root reducers token', {
  factory: () => ({
    [fromLayout.layoutFeatureKey]: fromLayout.reducer,
    router: fromRouter.routerReducer,
  }),
});
Enter fullscreen mode Exit fullscreen mode

Personally, I like how the example-app is organized. One of the things that I will add is to have all the folders related to ngrx in a single folder:

β”œβ”€ books\
β”‚     store\
β”‚        actions\
β”‚            books-api.actions.ts
β”‚            books-page.actions.ts
β”‚            index.ts                  // Includes creating names for the exports
β”‚        effects\
|            books.effects.spec.ts 
|            books.effects.ts 
|        models\
|            books.ts 
β”‚        reducers\
|            books.reducer.spec.ts 
|            books.reducer.ts 
|            collection.reducer.ts 
|            index.ts 
β”‚     
β”œβ”€ reducers\
β”‚        index.ts  /// Defines the root state and reducers
β”‚ 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

Visualizing Promises and Async/Await 🀯

async await

☝️ Check out this all-time classic DEV post