Uncle Bob’s seminal post has been an inspiration for many software designers and has become increasingly relevant as JavaScript allows business logic code to be reused. There never was an excuse for spreading business logic out all over a code base, but there is even of one now that we can and then wherever we need it. Clean Architecture less npm publish core-logic npm install --save core-logic This article takes as its point of departure the as defined by Uncle Bob: Use Case The software in this layer contains business rules. It encapsulates and implements all of the use cases of the system. These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their business rules to achieve the goals of the use case. application specific enterprise wide Use cases are often described as: Post an image of ; or my cute cat Fetch a list of movies made by J.R. Bookwalter However, software is messy, abstractions are leaky and often times, if you’re like me, you find yourself dreaming up leviathan Use Cases that spin out of control or trivial Use Cases whose logic you’ve already coded into the UI and that you won’t be removing anytime soon. What I’d like to present below is some low-hanging fruit for Uncle-Bobbing your Redux app with Middleware. is a simple JavaScript state container that has become a pattern unto itself that is reproduced in libraries for and . I strongly recommend you use it — introducing it into your app is trivial and after ten minutes of working with a redux state you’ll fall in love and never turn back. Redux Kotlin Swift Motivating example — A Log In Screen As a motivating example, I’ll use the Log In screen that actually applies this pattern. As a side note, the whole app applies this pattern, so feel free to download it, poke around and reach out to us if you want to know how certain things are done. Meeshkan Let’s start with a simplified version of this log in screen: // login.jsimport React from 'react';import { reduxForm, Field } from 'redux-form';import { connect } from 'react-redux';import { Button, MyTextComponent } from 'ui-lib';import { Text, Alert } from 'react-native';import Actions from 'react-native-router-flux';import analytics from 'analytics-lib';import { login } from 'kludgy-implementation'; const onSuccess = () => {analytics.report('successfully logged in');Actions.main(); // this is our navigation action} const onFailure = () => {analytics.report('log in error');Alert.alert("Sorry...", "Something went wrong");} const submitter = ({email, password}) =>login(email, password, onSuccess, onFailure); const LogIn = ({handleSubmit}) => (<View><Field component={MyTextComponent} name="email" /><Field component={MyTextComponent} name="password" /><Button onPress={handleSubmit(submitter)}><Text>Log In</Text></Button></View>); export default connect()(reduxForm({form: 'LogIn'})(LogIn)); There are several problems with this implementation: Our function must bubble up to the UI to accept success and failure callbacks. What if we want to change how many steps are in the log in process or add more options than success and failure? Refactoring doom… login Ditto for our analytics function, and even worse, we now have to test that every component calls it correctly. What a mess! We have to remember the order of success and failure and why/how it matters, sprinkling logic all over the UI without any spec that tells us why this is the case. The day that we want to change navigation, analytics or alert libraries we are in for a week of refactoring every UI component. Hello subtle code breakage. Middleware to the rescue! Let’s rewrite the above example using middleware. To start, I’ll show our new and improved component straightaway: // login.jsimport React from 'react';import { reduxForm, Field } from 'redux-form';import { connect } from 'react-redux';import { Button, MyTextComponent } from 'ui-lib';import { Text} from 'react-native';import { loginAction } from 'better-implementation';import Ize, { navSuccessIze, alertIze, alertFailureIze } from 'ize'; const login = Ize(loginAction,navSuccessIze('main'),analyticsIze(),alertFailureIze("Sorry...", Something went wrong")); const LogIn = ({handleSubmit, login}) => (<View><Field component={MyTextComponent} name="email" /><Field component={MyTextComponent} name="password" /><Button onPress={handleSubmit(login)}><Text>Log In</Text></Button></View>); export default connect(null, {login})(reduxForm({form: 'LogIn'})(LogIn)); Some reasons to celebrate: Gone are the explicit calls to libraries like navigation or analytics! Gone is spreading logic all over and callbacks that we can’t unit test without triggering all sorts of side effects! onSuccess onFailure This is way shorter! This is way easier to read! This is way easier to test! Ok, but how do we get there through middleware? 1. Using Action Creator Creators via the Ize pattern We use to implement the Action Creator Creator pattern. Basically, all of that stuff takes an action and adds a bunch of useful metadata for middleware. [redux-ize](https://www.npmjs.com/package/redux-ize) Ize 2. Use a library like redux saga to handle async calls Check out to see how one may handle our async log in call and dispatch success or failure events. As an example: [redux-saga](https://redux-saga.js.org/) import { call, put } from 'redux-saga';import { loginSuccessAction, loginFailureAction } from 'actions';import Ize, { navIze, alertIze } from 'ize'; function* logInSideEffect({payload: {email, password},meta: {navSuccess, alertFailure}}) {try {call(login, email, password);put(Ize(loginSuccessAction, navIze(navSuccess)));} catch(e) {put(Ize(loginFailureAction, alertIze(alertFailure)));}} Note how we use the pattern again here to move and information to a normal and track. This will make sure it is picked up by the middleware when the success or failure action is dispatched. ize navSuccess alertFailure nav alert 3. Create some middleware to handle analytics, navigation and alerts Easy like Sunday morning… // analytics.jsimport analytics from 'my-awesome-analytics-provider'; export default store => next => action => {action.meta && action.meta.analytics && analytics(action.type);next(action);} // nav.jsimport Actions from 'react-native-router-flux'; export default store => next => action => {action.meta && action.meta.nav && Actions[action.meta.nav]();next(action);} // alert.jsimport Alert from 'react-native'; export default store => next => action => {action.meta &&action.meta.alert &&Alert.alert(...action.meta.alert);next(action);} We have even more reasons to celebrate! Want to change your analytics provider? No prob — two lines of code. Want to test your middleware? No prob — use a stub in one place. sinon Want to kill off certain navigations or alerts depending on the app’s state? You have access to the store now! Want to test the order of all this stuff? No prob — just write one test to verify your redux store configuration. Summing it all up — Redux is More than a State Container Redux markets itself as “a predictable state container for JavaScript apps.” This is true, but one thing you’ll notice is that I haven’t mentioned a single reducer that propagates these actions to a state. Of course, these actions be tied to a state, and in practice they usually are, but I wanted to keep this simple so that you see how powerful middleware is. could It’s easy to start using Redux Middleware (or any middleware, like composed Rx object transformations) to handle things like navigation, analytics and alerts. Your app more predictable, less work to code, easier to test, more human-readable and, most importantly, squeaky Clean. Who doesn’t want that? Thanks Uncle Bob!