RxJS and Facades

Angular: The Full Gamut Edition

Charlie Greenman
February 24, 2020
9 min read

The Point of this Article

The point of this article is to discuss how you can create your own “light” internal state, using RxJS and Facades. However, the end result is to make you strongly consider using Ngrx/store in the first place, without using Facades + RxJS. In addition, proving to the developer, creating an internal service for a component that interacts with an HTTP service, likewise should use @ngrx/store in the first place.

I think this is important to read through because there is going to be a time in your career already, where you have decided, @ngrx/store might be too much for what you are working on. Likewise, the point of this article is to push back on that urge, and how @ngrx/store will actually decrease the time it takes to build the application long term.

Also, shout out to Thomas Burleson for being the person behind bringing the facade pattern to the Angular ecosystem. Thomas Burleson might not know it, but he gave me the greatest compliment of all time. We were once sitting in a hotel lobby together(must have been sometime like 7:30 am in the morning before grabbing breakfast), and he told me I had the greatest bed hair of all time. Thomas Burleson, I just want you to know, you will forever be the coolest in my eyes for saying that.


Recap: Facade Pattern

I want this piece of information to be as authentic as possible. Therefore, I would like to present the facade pattern in the way that I first learned how to do so. There is a fundamental book on computer science called ”Design Patterns: Elements of Re-usable Object-Oriented Software” otherwise known as the GoF(Gang of Four) book due to its four authors. It’s a bit of a classic in software, and I would say analogous to, ”How to Win Friends and Influence People” by Dale Carnegie, if you are familiar. In the GoF book, it discusses the idea of the facade pattern. Paraphrasing it:

  1. Implements a singular interface that contains multiple interfaces. Those interfaces work through the singular interface. 1a. This helps readability by making the name more straight to the point. For instance, in our scenario, UpdateTodo, vs. this.store.dispatch(new TodoUpdated(TodoPayload)). 1b. Usability by removing need of dependencies, and truncated form of functionality. For instance, in our scenario, we are using UpdateTodo, there is no need to include a dependency for the store. In addition, the developer can now just type in UpdateTodo instead.
  2. Provide context-specific interface — This is non-relevant within an @ngrx/store setting, as front end services by default, without state, is generally very, very specific.
  3. Serves as a launching point for a broader re-factor, or a tightly coupled system, in favor a more loosely coupled code. For instance, in our scenario, by tying all of our state underneath a singular facade, if at a later date we want to swap out the tech needed to manage state, we can do that by simple changing the logic within a particular location.

This is a great Wikipedia page on the facade pattern, and here is a link to the GoF book on Amazon if you haven’t bought it already.


Recap: Ngrx/store and Facades

Just to re-iterate, let’s say that we have a todo app, and we create a todo.facade.ts file:

@Injectable ({}) 
class TodoFacade {
  constructor(private store: Store<any>) {}
  todos$: Observable <Todo[]> = this.store.pipe(select(getTodos));
  idOfTodos$: Observable <Todo[]> = this.store.pipe(select(getTodosIds)); 
  loaded$: Observable <boolean > = this.store.pipe(select(getTodosLoading));
  UpdateTodo(TodoPayload): void { 
    this.store.dispatch(new TodoUpdated(TodoPayload));
  } 
}

Our facade within an ngrx/store setting is doing three unique things.

  1. Handling the store constructor for us. (With regards to unit testing this makes life a lot easier.)
  2. It allows us to have to only put the selector in one location(our facade), and trickle it down to all other areas.
  3. Create a simpler to use interface for our actions, that can be reused time and time again. Especially if we decide to change the logic or dynamics of how the action works, we only have to update in one particular area.

Progressing Idea of Facades over to RxJS

Similarly, the idea of facades is incredibly valuable within RxJs. Let’s imagine the most heavily used RxJS use case(at least the most heavily used one I’ve come across). Perhaps, because there isn’t a single application that doesn’t use it, “Search”.

Some of the business use cases with our particular company search includes: 1. Search by typing text into the search bar(let’s say here searching for companies) 2. The route between search, company, or company details view 3. Company details

Similarly, the idea of facades is incredibly valuable within RxJs. Let’s imagine the most heavily used RxJS use case(at least the most heavily used one I’ve come across). Perhaps, because there isn’t a single application that doesn’t use it, ”Search”.

Some of the business use cases with our particular company search includes: 1. Search by typing text into the search bar(let’s say here searching for companies) 2. The route between search, company, or company details view 3. Company details

Our particular RxJS code

@Component({
  selector: 'search-companies',
  templateUrl: './search-companies.component.html',
  styleUrls: ['./search-companies.component.scss']
})
export class SearchCompaniesComponent implements OnInit {
  companies$: Observable<Company[]>;
  searchCriteria = new FormControl();
  constructor(private companyService: CompanyService) {}
  
ngOnInit() {
  // Observable stream to input control values
  const searchBy$ = this.searchTerm.valueChanges;
  this.companies$ = searchBy$.pipe(
    debounceTime(300),
    distinctUntilChanged(),
    startWith(''),
    switchMap((criteria:string) => {
      const request$ = this.companyService.searchCompanies(criteria);
      return !criteria.length ? of([]) : request$
}));
  }
}

Dissecting our RxJS Code

  1. debounceTime(300) — Only after 300 milliseconds, will the request go through. If the user decides to type once again, within 300 milliseconds, the 300 millisecond count time, restarts again.
  2. distinctUntilChanged() — Let’s say the string the user inputs is ”Apple”. Then they decide to delete the ”Appl”, but then add it again, ”Apple”, this will not trigger the HTTP request. This is because this is the exact same word that it was initially.
  3. startWith('') — Not completely necessary, but will make sure that is an empty string every time we initialize this page again.
  4. switchMap — Powerful, because it has an internal project function, which allows us to break from the observables and use our ”transformed” value. Here we are passing it to the service, which calls our companyService, based on search criteria user passed in.

Addressing Architectural Concerns in Above Code

Concern One — Business Logic in View Layer

Obviously the logic here is contained within the view layer of the actual component. Best practices dictate logic pertaining to the view itself should be contained within the component. Business logic, such as here, pertaining to when the service should fire should be moved to a different service.

Concern Two — State

Primarily around caching. In addition, updating the search criteria. For instance, if we want to search our application by companies, use one service. If we want to search by CEO’s, then another function for CEOs. If we want to use another search criteria, let’s say a particular category, then we will need another facade for controlling that. This ends up having alot of internal logic, and it greatly simplifies our app, by having the state be external. Especially, if I would like to separate search from the data-table. Having the logic be re-usable, and be able to be used in multiple places, is useful.


Creating State Using RxJS and Facades

I would like to jump straight to this one. I don’t want to jot down here the interface, rather the internal logic that we can create within our SearchFacade. It would look something like this:

export enum CompanySearchActionTypes {
UPDATE_CRITERIA = 'Update Search Criteria',
UPDATE_RESULTS = 'Update Search Results'
}
export interface SearchAction {
  readonly type: SearchActionTypes;
  readonly payload: SearchCriteria | SearchResult[];
}
export class CompanySearchFacade {
  searchResults$: Observable<SearchResult[]> = this.dispatch.asObservable()
.pipe(map(state => state.companies), startWith([] as SearchResult[]));
searchCriteria$ = Observable<SearchCriteria> = this.dispatch.asObservable().pipe(map(state => state.criteria))
constructor(private companyService: CompanyService, private userService: UserService)
searchCompanies(companyName: string): Observable<SearchResult[]> {
// more code potentially goes here
pendingCompanies$.subscribe(companies => {
  const type = SearchActionTypes.UPDATE_RERULTS;
  const action = { payload: tickets, type};
  this.dispatch.next((this.state = reduce(this.state, action)));
});
}
updateCompanyCriteria(companyName: string, categoryName: string) {
  const type = SearchActionTypes.UPDATE_CRITERIA;
  const payload = { user, ticket };
  const state = reduce(this,state, {type, payload});
  
this.dispatch.next((this.state = state));
}
}
function reduce(state: SearchState, action: SearchAction): SearchState {
  switch (action.type) {
//the return within case such a signature TB move
  case SearchActionTypes.UPDATE_CRITERIA: return {
    ...state,
    criteria: action.payload as SearchCriteria
  };
  case SearchActionTypes.UPDATE_RESULTS: return {
    ...state,
    companies: action.payload as SearchResult[]
  }
}
}

Don’t think too much into the above code. However, what we’ve proven is that we can maintain a relatively simple store within our Facade simply using RxJS. That being said, I would like to make the following case for not doing the above and sticking with @ngrx/store.


Why you should stick to ngrx/store

he assumption with doing the above is that the code is light enough, to where we don’t need to use @ngrx/store. However, let’s say within our code, we want to allow users to move companies over to a sort of company cart. Within this company cart, the user can decide to move them into the product analytics platform. Here, we would be able to combine search and our product cart under one store. Using the above facade, it would fracture our store between many different things. We might:

  1. Want to create an @ngrx/effect, so that whenever a user searches, it resets the product cart(who knows, maybe product wants it).
  2. We want to modify our data to use @ngrx/entity.
  3. Maybe we want a history of all searches, to be used for internal analytics periodically.
  4. Our reducers and actions bloat out of control, wherein it makes sense to put them into two separate files. While we are at it, we might as well be using state.
  5. We have similar state logic elsewhere(let’s say for our influencer search), and want to use that our company logic as well. It might be easier to keep them all in the same sort of structure.
  6. It might make our code more brittle. Who’s to say now that we have @ngrx/store and RxJS/facades, that the team won’t break up into more patterns. For instance, a one-off internal service?

For this reason, and many others, once you are building an enterprise application, it makes sense to use @ngrx/store. I should add:

I’ve worked with many startups, and I’ve seen them work really quickly with @ngrx/store as well. When done correctly, I would argue that @ngrx/store speeds up team development, due to its cookie-cutter style of front end architecture.

More articles similar to this

footer

Razroo is committed towards contributing to open source. Take the pledge towards open source by tweeting, #itaketherazroopledge to @_Razroo on twitter. One of our associates will get back to you and set you up with an open source project to work on.