All Projects → dprovodnikov → Complex Redux Project Architecture

dprovodnikov / Complex Redux Project Architecture

Redux architecture extended with a layer of services.

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Complex Redux Project Architecture

Kotlin Mvvm Architecture
Android Architecture Design Patterns using Kotlin, MVVM, Dagger2, LiveData, Room, MediatorLiveData, NetworkBoundResources, Retrofit, AndroidX, ViewModels, Dependency Injection using Dagger2, Repository pattern.
Stars: ✭ 126 (+137.74%)
Mutual labels:  architecture, dependency-injection
Ioc
🦄 lightweight (<1kb) inversion of control javascript library for dependency injection written in typescript
Stars: ✭ 171 (+222.64%)
Mutual labels:  services, dependency-injection
Xaml Code Experiences
A collection of the experiences I have collected during days of Xamarin and Wpf, while following the MVVM design pattern.
Stars: ✭ 114 (+115.09%)
Mutual labels:  architecture, dependency-injection
Danf
Danf is a Node.js full-stack isomorphic OOP framework allowing to code the same way on both client and server sides. It helps you to make deep architectures and handle asynchronous flows in order to help in producing scalable, maintainable, testable and performant applications.
Stars: ✭ 58 (+9.43%)
Mutual labels:  architecture, dependency-injection
Pluggableapplicationdelegate
Smallest AppDelegate ever by using a decoupled-services based architecture. 🛠
Stars: ✭ 536 (+911.32%)
Mutual labels:  services, architecture
movie-booking
An example for booking movie seat, combined of Android Data Binding, State Design Pattern and Multibinding + Autofactory. iOS version is: https://github.com/lizhiquan/MovieBooking
Stars: ✭ 80 (+50.94%)
Mutual labels:  dependency-injection, architecture
Dry System
Organize your code into reusable components
Stars: ✭ 228 (+330.19%)
Mutual labels:  architecture, dependency-injection
Snowball
Android Clean Code Sample Project
Stars: ✭ 26 (-50.94%)
Mutual labels:  architecture, dependency-injection
ddd-example-ecommerce
Domain-driven design example in Java with Spring framework
Stars: ✭ 73 (+37.74%)
Mutual labels:  services, architecture
devonfw4flutter-mts-app
Large-Scale Flutter Reference Application. An Extension of DevonFw's My Thai Star Project
Stars: ✭ 54 (+1.89%)
Mutual labels:  dependency-injection, architecture
Apk Dependency Graph
Android class dependency visualizer. This tool helps to visualize the current state of the project.
Stars: ✭ 675 (+1173.58%)
Mutual labels:  architecture, dependency-injection
Cookbook
🎶 Cookbook for Nette Framework (@nette) & Contributte (@contributte). Read it while its HOT!
Stars: ✭ 30 (-43.4%)
Mutual labels:  services, dependency-injection
Vue.js With Asp.net Core Sample
This provides a sample code using vue.js running on ASP.NET Core
Stars: ✭ 44 (-16.98%)
Mutual labels:  dependency-injection
Cleanarchitecturetemplate
A template for a dotnet core api / mvc "clean architecture" project.
Stars: ✭ 50 (-5.66%)
Mutual labels:  architecture
Super Simple Architecture
🧩 Super Simple Architecture in Swift
Stars: ✭ 44 (-16.98%)
Mutual labels:  architecture
Zenject Hero
Zenject 7 - Game example (WIP)
Stars: ✭ 44 (-16.98%)
Mutual labels:  dependency-injection
Factory
Factory for object creation and dependency injection. Works with normal C# apps or under Unity3d
Stars: ✭ 50 (-5.66%)
Mutual labels:  dependency-injection
Dijs
JavaScript dependency injection for Node and browser environments.
Stars: ✭ 49 (-7.55%)
Mutual labels:  dependency-injection
Awesome Cto
A curated and opinionated list of resources for Chief Technology Officers, with the emphasis on startups
Stars: ✭ 10,834 (+20341.51%)
Mutual labels:  architecture
System Design And Architecture
Learn how to design large-scale systems. Prep for the system design interview.
Stars: ✭ 1,005 (+1796.23%)
Mutual labels:  architecture

Redux with services

This is an architecture built ontop of the default redux data flow with an additional layer of business logic. It brings a layer to place your services at and increases testability of redux thunks/reducers up to 100%.

Intent

Redux does not give us a defined place to store business logic. This architecture brings a new layer for that purpose and increases testability of redux pieces (actions, reducers) by introducting dependency injection.

Idea

The business logic layer is presented by services. It's kind of similar to what we have in Angular. It's the place where components and redux actions move the logic to, in order to focus on their main purpose.

Architecture

It consists of three main concepts: modules, services and context. Let's go through all of them to get a better understanding.

Modules

Modules are a gathering of redux-related stuff. A module consists of two main pieces: actions and reducers. Each module is a result of a function, which brings a posibility to inject services into them. Modules can depend on other modules as well.

There's a module entry point.

// src/modules/module/index.js
import initActions from './actions';
import initReducer from './reducer';
import types from './types';

const configureAuthModule = (services) => {
  const actions = initActions(types, {
    auth: services.authService,
  });

  const reducer = initReducer(types, {
    auth: services.authService,
  });

  return { actions, reducer, types };
};

export default configureAuthModule;

This is a function where services get injected into actions and reducers. You can see that types are injected too. Such injection makes it easier to test actions/reducers after all as it's possible to mock everything.

Actions are returned from the initActions function here:

// src/modules/module/actions.js
const initActions = (types, services) => {
  // make use of services here

  return Object.freeze({
    // include all public action creators here
  });
};

Reducer is basically the same:

// src/modules/module/reducer.js
const initReducer = (types, services) => {
  const INITIAL_STATE = {
    // may make use of services
  };
  
  const reducer = (state = INITIAL_STATE, action) => {
    // switch over types here
  };

  return reducer;
};

export default initReducer;

Injection of types allows to merge the module's types with some other module's types. It's a pretty common situation when modules depend on each other.

As you see it's almost the same redux you are used to. The only difference is -> actions and reducers are returned from functions.

Services

It's completely up to you when it comes to organizing services. The only thing to consider here is that you have to provide a function that returns service instances that are going to be injected in modules after all.

// src/services/index.js
const configureServices = async () => {
  const userService = UserService();
  const authService = AuthService();

  return { userService, authService };
};

That is what services entry point looks like. Services get initialized here and then get returned for the further usage.

Context

Context is a place where all globally available objects can be access from all over the application. Services and actions get registered to context so components can access them as they need them.

Context consists of two (or more) registries. One for actions and one for services. Here's what context entry point looks like:

// src/context/index.js
import actions, { registerActions } from './actionRegistry';
import services, { registerServices } from './serviceRegistry';

export {
  actions, services,
};

export default {
  actions,
  registerActions,
  services,
  registerServices,
};

I used a third-party package for the registry implementation.

// src/context/actionRegistry.js
import createRegistry from 'mag-service-registry';
const registry = createRegistry();
export const registerActions = registry.register;
export default registry.exposeRegistered();

It's pretty much the same code for services as well.

Context allows components to access actions like this:

// src/components/TodoList/index.js
import { actions } from '../../context';
...
class TodoList extends Component {
  ...
}
...
export default connect(..., { fetchTodos: actions.todos.fetch })(TodoList);

This is how services and actions get registered into context. It happens in application's entry point.

// src/index.js

import context from './context';
import configureServices from './services';
import configureModules from './modules';
...
(async function init() {
  const services = await configureServices();
  // inject services into modules here
  const { actions } = await configureModules(services);
  
  context.registerServices(services);
  context.registerActions(actions);
  ...
})();

Delaying DOM rendering

There is something else you need to understand. Since services are configured asynchronously we have to wait until they're done before we can render anything. This is just to prevent components from using services that aren't ready yet. It's not enough just to delay the ReactDOM.render call. We should delay the import of the Root component. Dynamic imports is the solution here.

const loadRoot = async () => {
  const module = await import('./components/Root');
  return module.default;
};

const render = async () => {
  const target = document.getElementById('root');
  const Root = await loadRoot();

  ReactDOM.render(<Root />, target);
};

(async function init() {
  ...
  render();
})();

That is how I integrated services into redux flow. I am grateful for any feedback. You are welcome to contribute!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].