When starting new projects very often developers use starter kits, generators, or boilerplate templates to kick off new projects which focus on getting a new app up and running as quickly as possible, often with highly opinionated architectures. These tools are very helpful because they let developers try out new technologies and lower the barrier to entry.

A common pitfall I’ve seen is that often projects grow from these templates which are not optimized for large scale applications, and projects that remain in these structures become unwieldy over time. A core side effect that I’ve seen is that often these templates don’t implement (or mention/refer to) common abstractions which which encapsulate enterprise logic, allowing re-use across frameworks such as Angular or React.

With the JavaScript ecosystem evolving and new frameworks coming out all the time, I think a good practice for developers to adopt is to vigilantly abstract their core business logic from the view layer to prevent being married to a library and not being able to evolve with the ecosystem.

An example from the wild of how this makes transitions smoother across frameworks can be seen from an angularjs upgrade guide from 1.5 => 2 (vastly different, for those who are unfamiliar). “Most services should require minimal to no change.” Source

From this, I emphasize that the vast majority business logic in code should live outside of (view) library specific code such as angular directives, react components, etc. AngularJS popularized these patterns by enforcing the use of these patterns in their documentation through the use of Services and related constructs. Among other aspects (specific syntax), angular has received a lot of criticism for the high learning curve associated with the use of its framework. I think overall, the use framework helps guide client applications to adopt a clean architecture early on which pays high dividends later in the development process by reducing technical debt.

To provide a more concrete context, I think developers should always maintain a clean separation between three sections of code.

  1. View Components:
    The display of content/data and user inputs
  2. State Management:
    The bridge between data access (api calls, local storage) and client-architecture specific data structures (Flux/Redux)
  3. Service / Domain Layer:
    Core business logic, calls to external services, infrastructure concerns

React is currently one of the most popular client side libraries for user interface rendering used in client side development today. React patterns often emphasize maximizing the use of “dumb” components which are stateless functional components, or more simply, components that receive a set of inputs and always return the same output causing no side effects.

React provides many facilities for state management built in through life-cycle methods, however, I would propose that they more often than not should not be used unless for very specific use cases.

Two popular state management frameworks within the React community are Flux, and Redux. Both leverage the use of events for state management and notify React to re-render the UI when data changes. For simplicities sake, the main difference between the two is storage of client-side state within one large tree (Redux) or distributed throughout respective state-containing entities (stores) in various formats.

Both of these frameworks agree that the state management mechanisms should not access UI components. They encapsulate a way to manage state so that it can be shared between components, and to reduce the burden of state management and communication between components. By introducing this layer you can effectively abstract business logic outside of react components and potentially re-use the same code with an alternative UI library such as Vue.js — a new competitor to React that is quickly gaining traction.

I suggest taking the abstraction an additional layer further by encapsulating infrastructure and api calls out of application state management by creating Services (or Client SDKs) for your API to enable the code to be re-used outside of your preferred state-management architecture.

These files effectively act as wrappers around your API calls, provide consistent client-side apis and validation, and allow the code to be re-used throughout many projects.

By doing this, among many other benefits (testability, isolation) you can build a scalable code base where many applications can leverage your API in a consistent way and you further insulate your core business logic from the rapidly changing ecosystem.

What do you think?

Thank you for reading! To be the first notified when I publish a new article, sign up for my mailing list!

Ben Lugavere is a Lead Engineer and Architect at Boxed where he develops primarily using JavaScript and TypeScript. Ben writes about all things JavaScript, system design and architecture and can be found on twitter at @benlugavere.

Follow me on medium for more articles or on GitHub to see my open source contributions!