slorber / scalable-frontend-with-elm-or-redux

An attempt to make Redux and Elm applications scale
http://sebastienlorber.com/
MIT License
361 stars 29 forks source link

Solution Feedback: redux-subspace #41

Open mpeyper opened 6 years ago

mpeyper commented 6 years ago

I have submitted a PR with a solution using redux-subspace.

Any feedback is welcome.

ghola commented 5 years ago

The proposed solution breaks encapsulation on multiple occasions.

To start with, there is this action creator which belongs to the randomGif component, but knows about an action from a different component (increment() from counter):

export const getGif = () => (dispatch, getState, api) => {
  dispatch(loading())

  api.getRandomGifUrl()
    .then((url) => dispatch(setGifSrc(url)))
    .then(() => dispatch(increment()))
}

How can randomGif hope to be publishable as an independent NPM package when it explicitly depends on counter?

Then if I look at the counter reducer, I see it reducing the TOGGLE action from button:

export default (state = initialState, action) => {
  switch (action.type) {
    case TOGGLE:
      return { ...state, useMultiplier: !state.useMultiplier }
    case INCREMENT: {
      const increment = state.count >= 10 && state.useMultiplier ? 2 : 1
      return { ...state, count: state.count + increment }
    }
    default:
      return state
  }
}

Again, it's dependent on another component and cannot be published independently.

Fixing it would start by removing dispatch(increment())) from the randomGif action creator and also remove the handling of TOGGLE from the counter reducer. Then you create a new saga which listens to:

  1. TOGGLE actions -> sets it's internal state to the corresponding value isEnabled = true/false;
  2. SET_SRC actions -> depending on the isEnabled flag it dispatches 1 or 2 INCREMENT actions.

The saga is the glue that makes it all work. If you will, in DDD terms, it listens to events from two bounded contexts (button and randomGif) and converts them to a command for the third bounded context (counter). None of these bounded contexts know or depend on the others, nor should they. This saga would not be part of any component, or if you must place it in one, it would be part of the application component (or whichever parent contains these participating components). Of it could be part of a CounterWrapper HOC.