We often claim caution when it comes to refactoring a tech stack.
But there always comes a time when it needs to get done.
When that happens, you want to pick the right tools:
Unless you’ve got stupid amounts of VC money, you can’t afford a complete refactoring every quarter.
For our own shopping cart v3.0 rewrite, we picked Vue.js and Redux.
Weird mix you say? Not quite! In this post, I’ll show you how and why we strapped Vue.js on top of Redux.
More specifically, I’ll cover:
I’m thrilled to finally share some of our work on this newest version of Snipcart with you guys!
Let’s start with a bit of context.
In the last few months, the whole team at Snipcart has been hard at work crafting a new version of our shopping cart for developers.
Read our docs to understand how our HTLM/JS-powered cart works.
The first thing we had to settle on was the goals this revamped cart had to achieve:
These pushed us to carefully select our new tech stack.
It had to enable easy customization, sure. More importantly though: it had to empower seasoned AND junior developers to get shit done without getting in their way.
We picked Vue.js for the UI. Mainly because the team loves this JS framework. But also because it includes all the building blocks needed to add template customization to our cart.
This post is a companion piece to a talk our co-founder Charles gave at VueConf Toronto. We’ll cover our Vue.js experience more deeply in upcoming v3.0 posts.
You might think the next obvious choice for state management would be Vuex.
However, our goals forced us to think outside the box, where we found Redux. It became the most logical pick for us — we’ll tell you why soon enough.
Let’s make sure we’re on the same page as to what Redux is, first.
In a nutshell, Redux is a centralized state management library.
If what follows sounds like gibberish to you, I suggest reading Redux’s basics here.
It exposes a
store, the place where your app’s state is kept safe, which can be represented by this interface:
A state is an immutable object that gets updated by
reducers. When a change has to affect the state, we
action to the store, which gets handled by the reducers. Its consumers then receive a new instance of the updated state.
It’s this simple contract that allows us to use Redux with almost anything. For display purposes, only
subscribe are actually required, which makes integrating it with view libraries very straightforward.
Reducers are functions that receive the current state and actions, then return a new state. They are synchronous and cannot have any side effect:
The crunchy part of state changes is fully isolated in the reducers and an additional mechanism,
middlewares, makes it easier to augment the store with other functionalities. To keep it simple, we can see middlewares as plugins of the store.
That’s where redux-observable fits in the puzzle: it’s a Redux middleware that leverages RxJS to allow asynchronous operations on the store.
More about redux-observable later on!
A question you might still have in the back of your mind is: “Wasn’t Vuex built to do all of this with Vue?”
Yes, it was.
But our v3.0 guidelines were strict — easy customization and dev freedom above everything else. So we’ve decided to split the cart into two parts:
In that particular context:
→ Vuex became a no-go as it’s tightly coupled to Vue.js. It’d force Vue usage upon our users. Redux was different and had everything we needed.
→ Redux has a very simple interface that can be used with most frameworks. Unlike Vuex, it’s framework-agnostic and built as a standalone library. It gave us the state management library we needed for our SDK to be universal.
→ It has a mature community around it. We got to try and play with many libraries leveraging Redux. Which brings us to our final addition to the stack: redux-observable.
In redux-observables, asynchronous API calls or side effects can be achieved with help from ‘Epics’, a core concept of the library.
It’s a stream of actions from which we can handle specific actions we’re interested in and emit new ones for the reducers. We’ve simplified our epics to hide the complexity of RxJS for simple cases.
Here’s a basic example of an epic:
observable part of RxJS and its streams comes in handy for debouncing—when a user does multiple operations rapidly. A user clicking to increase the quantity of an item in the cart, for instance.
It wouldn’t make sense to fire as many API requests, so with ReactiveX’s operator we can batch these actions and commit them once.
Rx is a powerful but complex beast; you can learn more about it here.
Devs familiar with Node.js Express or other server-side frameworks with a middleware system shouldn’t be lost in Redux. It’s exactly the same — code can run before and after the call to the reducers.
Epics run after the reducers which allows to react to a single action both in the reducers and the epics:
So we’ve built simple middlewares to expose an API that returns
Promises and can be used with async/await, and another one that emits events that can be subscribed to.
With the immense communities behind both Redux and Vue, a cross-over was bound to happen.
As of now though, the choice of library is limited to:
redux-vuex for its similarity with
As you can see, plugging Vue over Redux is effortless.
We want our base Vue theme for the new cart to be a slim layer over the Redux store. A place where all the beefy logic lives.
This way, anybody can make their own custom theme with the tech they love.
I hope this clarifies our choice of stack for the cart’s rewrite. In all honesty, we would have loved to have this kind of resource when we started working on it. ;)
This is only the foundation of the Snipcart’s v3.0. We’ll be back with more entries on the blog about the process of building it. Template overriding, packaging & distribution of the SDK, and exposing a promise-based API around a reactive core are all subjects we want to discuss on the blog in the next few weeks.
So hang around!
For now, if you need any more info about any section of this specific post let us know in the comments below.
If you’ve enjoyed this post, please take a second to 👏 + share it on Twitter. Got comments, questions? Hit the section below!