Expedia Group Inc.

10/27/2021 | Press release | Distributed by Public on 10/27/2021 07:20

Applying the Single Responsibility Principle to a FE/BFF Layered Arc... (Opens in new window)

The Use Case

For this discussion, we focus on a single use case for our hypothetical app:

As an admin user, I would like to see a list of configured hours of operation for my stores, so that I can better manage my staff in different time zones.

To support this use case, we'll need a table view showing business-hour records with the attributes/columns shown below. To keep it simple for now, we won't worry about sorting, filtering or similar features.

The View Model

When designing frontend architectures, it helps to think about a "view model" (at least when it comes to web or mobile apps) as a message contract with the backend services (the BFF service in our case). This is especially true if the Single Responsibility Principle (SRP) is part of the guiding principles. Often, we think about entity models at the UI level, but that tends to conflate responsibilities, so it's better to stick to a view model. Hence, the first step is to define a view model based on the UI needs.

State Management

The next concern is handling API calls and global state (we'll rely on React to implement our views). We already have a way to handle local state in React via component state, but having a robust solution for global state becomes almost imperative for scaling React apps. Because of all the advantages that it provides, we'll go with Redux by way of . This will also allow us to handle API (and any ) calls in this layer, using Redux Thunk.

Empowered by the view model previously designed, we can store the API (successful) response in a Redux slice that represents our page. We can then subscribe a Redux selector in our React component that reacts to state changes in this slice to update the view.

Rendering

The final concern in the frontend is obviously rendering the UI. As mentioned earlier, we'll use to do that job. Once we have the view model worked out, and the corresponding state management logic, we implement our components. For the corresponding global state changes, we'll just need selectors that select the data that our component(s) care about. By hooking up these s in our components, our UI can react to these state changes. Any other local state changes will be kept in local state.

As illustrated in the above diagram, our component(s) will communicate with in 2 ways:

  1. Via s to send events to the Redux pipeline.
  2. Via s to react to changes in global state.

The below diagram shows these concepts in isolation.

Wrapping up

At this point, we have the basis to start implementing a maintainable frontend codebase. To summarize of our frontend architecture, we have 2 distinct responsibilities:

Keep these concerns/responsibilities in mind as your codebase evolves, and respect them throughout your code. This should help you keep it maintainable as it grows.

Come back tomorrow for Part 3 of this three-part series, where we'll look in more detail at the backend details of using this approach.