Skip to content

Latest commit

 

History

History
439 lines (310 loc) · 15.3 KB

README.md

File metadata and controls

439 lines (310 loc) · 15.3 KB

feature-redux-logic (feature-u redux-logic integration)

feature-redux-logic is your feature-u integration point to redux-logic! It promotes the logicAspect (a feature-u plugin) that facilitates redux-logic integration to your features.

Backdrop:

    feature-u is a utility that facilitates feature-based project organization for your react project. It helps organize your project by individual features. feature-u is extendable. It operates under an open plugin architecture where Aspects integrate feature-u to other framework/utilities that match your specific run-time stack.

Build Status Codacy Badge Codacy Badge Known Vulnerabilities NPM Version Badge

Overview:

    feature-redux-logic configures redux-logic through the logicAspect (which is supplied to feature-u's launchApp()). This extends feature-u's Feature object by adding support for the Feature.logic property, referencing an array of feature-based logic modules.

    It is important to understand that you continue to use redux-logic the same way you always have. It's just that now you are dealing with a smaller context ... within the boundaries of your feature!

Let's see how this all works together ...

At a Glance

Install

  • peerDependencies ... you should already have these, because this is our integration point (but just in case):

    npm install --save feature-u
    npm install --save redux-logic
  • the main event:

    npm install --save feature-redux-logic

SideBar: Depending on how current your target browser is (i.e. it's JavaScript engine), you may need to polyfill your app (please refer to Potential Need for Polyfills).

Usage

  1. Within your mainline, register the feature-redux-logic logicAspect to feature-u's launchApp() (see: **1** below).

    Please Note: redux must also be present in your run-time stack, because redux-logic is a middleware agent of redux (see: **2** below).

    src/app.js

    import {launchApp}           from 'feature-u';
    import {createReducerAspect} from 'feature-redux';       // **2**
    import {createLogicAspect}   from 'feature-redux-logic'; // **1**
    import features              from './feature';
    
    export default launchApp({
    
      aspects: [
        createReducerAspect(), // **2**
        createLogicAspect(),   // **1**
        ... other Aspects here
      ],
    
      features,
    
      registerRootAppElm(rootAppElm) {
        ReactDOM.render(rootAppElm,
                        getElementById('myAppRoot'));
      }
    });
  2. Within each feature that maintains logic, simply register the feature's logic modules through the Feature.logic property (using feature-u's createFeature()) ... see: **3** below.

    src/feature/myXyzFeature/index.js

    import {createFeature}  from 'feature-u';
    import logic            from './logic';
    
    export default createFeature({
    
      name:     'myXyzFeature',
    
      logic,  // **3** myXyzFeature logic[]
    
      ... snip snip (other aspect properties here)
    });
  3. As a convenience, feature-redux-logic auto injects the feature-u Fassets object as a dependency in your logic modules. This promotes full Cross Feature Communication.

    The following example, demonstrates the availability of the fassets named parameter:

    import {createLogic}      from 'redux-logic';
    
    export const someLogic = createLogic({
    
      ... snip snip
    
      transform({getState, action, fassets}, next, reject) {
        ... fassets may be used for cross-feature-communication
      },
    
      process({getState, action, fassets}, dispatch, done) {
        ... fassets may be used for cross-feature-communication
      }
    
    });

Well that was easy!! At this point redux-logic is completely setup for your application!

In the nutshell, that's a highlight of most everything you need to know to use feature-redux-logic! Go forth and compute!

A Closer Look

Any feature that maintains business logic promotes it's own set of logic modules through the Feature.logic property (using feature-u's createFeature()). While these logic modules are truly opaque assets (internal to the feature), they are of interest to feature-redux-logic to the extent that they are needed to configure redux-logic.

Because logic modules may require access to feature-u's Fassets object during code expansion, this property can also be a feature-u expandWithFassets() callback (a function that returns the logic modules) ... please refer to feature-u's discussion of Managed Code Expansion.

It is important to understand that your interface to redux-logic has not changed in any way. In other words, you continue to use redux-logic the same way you always have. It's just that now you are dealing with a smaller context ... within the boundaries of your feature!

As always, you should strive to keep each feature isolated, so it is truly plug-and-play.

Single Source of Truth

The featureName (i.e. Feature.name) is a critical item that can be used throughout your feature implementation to promote a consistent feature identity.

A key aspect of the featureName is that feature-u guarantees it's uniqueness. As a result, it can be used to qualify the identity of several feature aspects.

A best practice in consideration of feature-based logic modules, is to prefix all logic module names with featureName. While this is not required, it helps (in diagnostic logs) to identify where that module lives.

Here is an example (see: **4**):

src/feature/myXyzFeature/logic/doSomething.js

import {createLogic}      from 'redux-logic';
import featureName        from './featureName';
import actions            from './actions';

export const doSomething = createLogic({

  name: `${featureName}.doSomething`, // **4**
  type: String(actions.some.action),

  transform({getState, action}, next, reject) {
    ... snip snip
  },

  process({getState, action, fassets}, dispatch, done) {
    ... snip snip
  }

});

Why redux-logic?

There are many ways to introduce logic in your react app. This article breaks down the various options: Where do I put my business logic in a React-Redux application? The article is an introduction (and motivation) for the development of redux-logic ... redux middleware for organizing all your business logic.

I have been using redux-logic since it's inception and believe it is the best approach to encapsulate your business logic. Prior to redux-logic, my business logic was spread out in a variety of different places, including:

  • component methods
  • thunks
  • and various middleware components

In addition, I relied heavily on batched actions, where logic entry points would stimulate multiple actions in one procedural chunk of code. Needless to say, this was less than ideal. Even tools like redux-dev-tools could not give me adequate insight into "what was stimulating what"!

All of these techniques were replaced with "true" business logic, organizing all my logic in one isolated spot, all orchestrated by redux-logic!

My business logic is now:

  • located in one logical discipline (i.e. dedicated "logic" modules)
  • making it testable in isolation (very nice)!!
  • has more concise and succinct goals
  • promotes modular reuse
  • provides traceable "cause and effects"
  • is greatly simplified!

Interface Points

feature-redux-logic accumulates all the logic modules from the various features of your app, and registers them to redux-logic. This is accomplished by creating the redux middleware which is in turn registered to redux. The Aspect Interface to this process (i.e. the inputs and outputs) are documented here.

Input

  • The input to feature-redux-logic are redux-logic modules. This is specified by each of your features (that maintain logic) through the Feature.logic property, containing a logic modules array.

Exposure

  • The output from feature-redux-logic is the redux middleware component, exposed through the Aspect.getReduxMiddleware() API (an "aspect cross-communication mechanism"). This middleware component must be consumed by yet another aspect (such as feature-redux) that in turn manages redux.

  • As a convenience, feature-redux-logic auto injects the feature-u Fassets object as a dependency in your logic modules. This promotes full Cross Feature Communication. Please refer to the Usage section for examples.

Error Conditions

When feature-redux-logic detects that no logic modules have been specified by any of your features, it will (by default) throw the following exception:

***ERROR*** feature-redux-logic found NO logic modules within your features
            ... did you forget to register Feature.logic aspects in your features?
            (please refer to the feature-redux-logic docs to see how to override this behavior).

Most likely this should in fact be considered an error (for example you neglected to specify the logic modules within your features). The reasoning is: why would you not specify any logic modules if your using redux-logic?

You can change this behavior through the following configuration:

logicAspect.config.allowNoLogic$ = true;

With this option enabled, when no logic modules are found, redux-logic will simply NOT be configured (accompanied with a WARNING logging probe).

You can also specify your own array of logic modules in place of the true value, which will be used ONLY in the scenario where no logic modules were specified by your features.

API

logicAspect: Aspect

    API: createLogicAspect([name='logic']): logicAspect

    The logicAspect is the feature-u plugin that facilitates redux-logic integration to your features.

    To use this aspect:

    • Within your mainline, register the feature-redux-logic logicAspect to feature-u's launchApp().

    • Within each feature that maintains business logic, simply register the feature's logic modules through the Feature.logic property (using feature-u's createFeature()).

    Please refer to the Usage section for examples of this process.

Potential Need for Polyfills

The implementation of this library employs modern es2015+ JavaScript constructs. Even though the library distribution is transpiled to es5 (the least common denominator), polyfills may be required if you are using an antiquated JavaScript engine (such as the IE browser).

We take the approach that polyfills are the responsibility of the client app. This is a legitimate approach, as specified by the W3C Polyfill Findings (specifically Advice for library authors).

  • polyfills should only be introduced one time (during code expansion of the app)
  • a library should not pollute the global name space (by including polyfills at the library level)
  • a library should not needlessly increase it's bundle size (by including polyfills that are unneeded in a majority of target environments)

As it turns out, app-level polyfills are not hard to implement, with the advent of third-party utilities, such as babel:

If your target JavaScript engine is inadequate, it will generate native run-time errors, and you will need to address the polyfills. Unfortunately, in many cases these errors can be very obscure (even to seasoned developers). The following Babel Feature Request (if/when implemented) is intended to address this issue.