Angular state facades

Improving state management one facade at a time

Angular state facades

If you've got a frontend in your application, chances are big you've encountered at least some use for state in it. Whether it's retrieving backend information to display it on the screen or making separate components work well together: state is your friend. In this blog I'd like to focus on Angular applications because that's where most of my knowledge is. However, I'm quite sure that the other frameworks use state in a similar fashion. In my examples I will be using NgRx as a state management framework. Don't let this stop you, as state facades come in handy with any state framework.

Do you need state?

Let's say you have a component to display a list of books:

@Component({
  selector: 'welko-book-list',
  template: `
    <div *ngFor="let book of books">
      {{book.title}} - {{book.author}}
    </div>
    <button (click)="addBook()"></button>
  `,
})
export class BookListComponent implements OnInit {
  public books = [];

  constructor(private store$: Store<BookState>) {}

  ngOnInit() {
    this.store$.select(BooksSelector)
      .subscribe(books => this.books = books);
  }

  public addBook() {
    this.store$.dispatch(
      AddBook({title: 'The Martian', author: 'Andy Weir')
    );
  }
}

What we have here is a component that uses the store directly to

  • retrieve a part of the application state;
  • trigger some action to manipulate application state.

So what's wrong with that?

The big problem here is that in your component you have a dependency on your state framework. If said state framework changes how it retrieves parts of the application state - let's say that in a newer version of NgRx the select function gets renamed to something like get - you will have breaking changes in all your components that use state.

Also, if you happen to change your action arguments you will need to make changes in your components as well. If you can add books in five components and change the author property of the action to authorName, you'll need to perform this change in all five components. You can ask yourself whether your components need to know these action internals. Is it their responsibility to construct a well-defined action, or just to trigger that action? Is it the responsibility of the components to know where the desired elements exist in the state? In my opinion, the answer is no.

Bring in the facade

So, what if we add a facade to hide this from the components?

@Component({
  selector: 'welko-book-list',
  template: `
    <div *ngFor="let book of books">
      {{book.title}} - {{book.author}}
    </div>
    <button (click)="addBook()"></button>
  `,
})
export class BookListComponent implements OnInit {
  public books = [];

  constructor(private bookFacade: BookFacade) {}

  ngOnInit() {
    this.bookFacade.books$
      .subscribe(books => this.books = books);
  }

  public addBook() {
    this.bookFacade.addBook(
      'The Martian', 
      'Andy Weir'
    );
  }
}

The facade itself would look something like this:

@Injectable
export class BookFacade {
  public books$ = this.store$.select(BooksSelector);

  constructor(private store$: Store<BookState>) {}

  public addBook(title: string, author: string) {
    this.store$.dispatch(
      AddBook({title, author}
    );
  }
}

Changes in the state framework are now only needed in the facade(s). While you might still have a lot of facades, there will always be fewer than you have components needing state. So you have to do less changes in total, and the files where you need to perform those changes are more trivial. These days, our IDEs are smart enough to point us to the files in need for changes. But if you put your mind to it: does it really make sense to update the BookListComponent because NgRx decides to rename their select function? Not to me.

But isn't this more code?

More lines of code means more complex software, right? Not necessarily. I've found it easier to explain a state framework to new developers when a facade is in place. The reason is simple: the single responsibility of the facade is to communicate with your state framework. This means that the parts of your application that use the state framework are very limited.

Having facades that list your functions to trigger state change actions means you have a nice overview of which state changes you have. Looking at it from that angle, facades are a way to document your state entry points. Without facades, you'd have to search all your components to get a clear overview.

I will admit: using facades means more lines of code. But are it difficult lines of code? If your facade functions dispatch multiple actions, you're most likely using your framework wrong. You should only trigger one action at a time. These actions can then of course trigger a new action, but that should be handled differently than by dispatching them in the facade. NgRx has effects just for this purpose.

Can you imagine the one-liner functions being hard to test? Of course they're not! Unit testing your facade is easy, which means testing your state interactions is easy. Sounds like a serious win, right?

A hidden extra advantage

Big applications hardly ever change their state management framework. Most likely the framework is chosen in the beginning of the project by the Developers, or maybe by some Architect. Nobody ever thinks about changing it. In the backend however, we tend to use microservice architectures where we sell that each microservice can be rewritten within two weeks if necessary. Why can't we do the same with frontends?

Speaking from experience, facades allow us to rewrite state management in large frontend applications (500+ components) in two weeks or less. Changing from a framework like ngRedux to NgRx is easy, because changes are located in the facades and their surrounding files. You don't necessarily need them in smaller applications, but that is entirely up to you. For me, the benefits outweigh the disadvantages for even tiny applications (max 10 components).

You might not even need state at all. Sometimes you want to store things in LocalStorage rather than application state. It's still a good idea to add a facade in front of your LocalStorage, as it allows you to easier change your storage to state at a later stage of development.

So are state facades the silver bullet?

No. Facades can help you improve your application in the following aspects:

  • lower coupling on external frameworks;
  • higher cohesion on state related classes/interfaces;
  • single responsibility for components as well as for facades.

But as is always the case in software development: you still need common sense. If you have a facade with many functions, chances are big you need to remodel your state. Maybe you need to split up some parts and create separate facades. For example, it makes sense for a book facade to:

  • retrieve books
  • add a book
  • update book data (with one function only)
  • remove a book
  • mark a book as the current item you are viewing

If your facade is doing more things like keeping a list of books that you want to loan or like downloading a specific e-book, you might want to split your facade (and your state) into separate parts. Maybe you can do with a book facade, a shopping cart facade and a current book facade.

Conclusion

State management facades can make your (future) life (a lot) easier. They allow you to transition from one state management framework to another in a more easy way. They also improve general code quality aspects like low coupling on external frameworks, high cohesion and single responsibility. Even parts of your application that do not currently use application state but might need it in the future, can benefit from facades. Last but not least, you'll have a clear overview of how you can change your state. That tops it!