Published January 3, 2022
Over the last few years, I have seen many developers struggle with state management and understanding the Redux Pattern. By implementing the Facade Pattern, you can add a layer of abstraction over your state management and decouple the knowledge of stores from your component. In this blog, we will discuss the Facade Pattern with NgRx and the benefits of implementing them together.
NgRx is a reactive state management framework for Angular applications that is based on Redux. NgRx provides libraries for managing state, isolation of side effects, entity collection management, Angular router integration, and developer tooling.
Visit the NgRx website for more resources about the framework. The state management lifecycle below shows how we select and update our read-only state from our store.
Actions
describe events that are emitted (dispatched) to change state.reducers
, which are pure functions that describe how our state is to be transformed.Selectors
are pure functions that derive and compose a read-only piece of state.store
using selectors
.Dive into what the Facade Pattern is and learn how to implement it with NgRx.
In software, a facade provides a public interface to mask the usage of something more complex. In this instance, it will neatly wrap our NgRx interactions and allow one point of contact for our component and state management without our component having any knowledge of NgRx.
Before jumping in to the facade example, here is a component using NgRx without a facade.
export class AccountListComponent implements OnInit {
accountList: Observable<Account[]>;
constructor(private store: Store<AccountListState>) {}
ngOnInit() {
this.accountList = this.store.select(getAccountList);
this.loadAccountList();
}
loadAccountList() {
this.store.dispatch(new LoadAccountList());
}
addAccount(account: string) {
this.store.dispatch(new AddAccount(account));
}
editAccount(id: string, account: string) {
this.store.dispatch(new EditAccount({ id, account }));
}
deleteAccount(id: string) {
this.store.dispatch(new DeleteAccount(id));
}
}
This implementation is very store
-dependent and makes the component more complex. By adding a facade for the component to interact with, we can simplify the access to our store and decouple the knowledge of NgRx in our component. By performing that separation of duties, we do not have to refactor our component should we choose to remove NgRx in the future.
Now let's create an AccountService
that will act as our facade for NgRx.
@Injectable({
providedIn: 'root',
})
export class AccountService {
accountList = this.store.select(getAccountList);
constructor(private store: Store<AccountListState>) {}
loadAccountList() {
this.store.dispatch(new LoadAccountList());
}
addAccount(account: string) {
this.store.dispatch(new AddAccount(account));
}
editAccount(id: string, account: string) {
this.store.dispatch(new EditAccount({ id, account }));
}
deleteAccount(id: string) {
this.store.dispatch(new DeleteAccount(id));
}
}
After moving everything related to the store into our service, we can then inject that service into our component.
export class AccountListComponent implements OnInit {
accountList: Observable<Account[]>;
constructor(private accountService: AccountService) {}
ngOnInit() {
this.accountList = this.accountService.accountList;
this.accountService.loadAccountList();
}
addAccount(account: string) {
this.accountService.addAccount(account);
}
editAccount(id: string, account: string) {
this.accountService.editAccount( id, account);
}
deleteAccount(id: string) {
this.accountService.deleteAccount(id);
}
}
What exactly did we gain after implementing the facade pattern?
accountList
instead of duplicating code.While this may violate some of the standards and recommendations of the Redux pattern, I do not think the gains your team can have in development speed are outweighed. If you need to move fast, there is a clear upside to using facades, but I would make sure the developers on the team also gain a solid understanding of NgRx. By understanding NgRx and the potential issues that using a facade may bring, you can write better code as a team and avoid those problems.