Falling Into the Pit of Success with Web Components
Being able to build components (AKA “directives” if you’re in the Angular camp) has changed the way we structure web applications on the front end. This is a big move forward and provides a lot of power in addition to some much needed encapsulation.
However, even with the drastically improved tools we have at our disposal for building client side applications, I still see projects going down the wrong path. Code is tightly coupled and maintenance quickly becomes an issue.
The best way I’ve found to mitigate this is to separate concerns as much as possible. I’m going to say that again just because I think it’s that important: separate concerns of your components as much as possible.
We all start out having our components do too much, myself included. It’s hard to get into the habit, but our goal should be to make components as small and dumb as possible. This ties right in with other software development best practices like DRY and SRP, but it’s not something I see people doing a lot in client side applications.
Here are two big revelations I had about developing components that have made my life a lot easier, along with a an example from a recent project where I broke apart a component that was doing too much.
Pass In Your Data
When I started building components I often made them responsible for managing their own data. After they were instantiated, they would be responsible for making API calls to fetch and manipulate their data. This works for a while, but becomes difficult to maintain over time.
Passing data in to a component provides a lot of the same wins as dependency injection. It allows your components to be very small and focused which makes them easier to maintain and test.
This can be accomplished by creating containers, also components, that are solely responsible for managing data injecting it into their children. I’ve also done this by instantiating data through a router and exposing that to the view where the components are rendered.
If you’re coming from a framework like Angular, I’m not just talking about separating things from directives and controllers, either. It will be tempting to just toss some additional logic into your controller for managing data, but this should actually be injected into the component’s controller, so it needs to come from another source.
The main goal here is to make our components as dumb as possible so they become more reusable, testable and maintainable.
There’s No Such Thing As A Component That’s Too Small
At first it’s going to feel weird to break things down into smaller and smaller pieces. It’s common to think that it isn’t worth the overhead of creating a new component when evaluating where you might be able to break things apart. However, this is where you are able to set yourself up for success.
Let’s take a look at an example from a recent project I was working on. We had some widgets that stood alone as individual components.
We had a single controller and view backing this entire widget. It worked fine for a couple weeks, until we had requests to see this same data represented in similar but different forms throughout our app.
It was hard to decide whether to write a new component or try to modify the design to make our existing components fit. Neither of these options were ideal. In the end, this widget was broken out into 7 different components and composed of 10 total. I’ve annotated another screenshot to show the breakdown.
We ended up breaking things out into a bunch of pieces that are very easy to maintain and test individually. Now we have all the building blocks we need to develop other complex interfaces with these same components. Another big win here is that we are passing all of our data in to the components so they can just worry about rendering. The data comes from the top level component, which is not annotated in the screenshot since it just handles data.
Breaking things down into small pieces that have a single responsibility is always a win. The story is no different for web components. Don’t forget, managing data is a responsibility.
If you have other techniques for writing maintainable components, please share them in the comments.