redux-entity-manager
v0.0.3
Published
Easy domain entity managenent with Redux
Downloads
2
Readme
Redux Entity Manager
A framework-agnostic library for easy domain entity management
Demo
Coming soon: demo and examples using React with redux-observable and Angular with ngrx.
Prerequisites
- Any Redux library such as Redux, ngrx or custom implementation.
- Any side-effect library such as Redux Thunk, redux-observable, ngrx or custom implementation.
- Domain entities must have a primary key.
Installation
yarn add redux-entity-manager
or
npm install redux-entity-manager
Motivation
In some Redux tutorials you have probably seen code that looks like this:
const initialState = {
selectedId: null,
todos: [
{
id: 1,
name: 'Foo',
},
],
};
While this works fine for a simple project, in real life applications it will cause problems because:
- the state is not normalized
- client state and domain entities are mixed together.
A better state structure would look like this:
{
"entities": {
"todo": {
"1": {
"id": 1,
"name": "Foo"
}
}
},
"pages": {
"account": {
"todos": {
"selectedId": null
}
}
}
}
To create our root reducer for this state structure we'd probably write something like this:
import { combineReducers } from 'your-favourite-redux-library';
const rootReducer = combineReducers({
entities: {
todo: todoReducer,
user: userReducer,
// Other entity reducers
},
// Other reducers
});
Notice that we have to write a separate reducer with its own actions and selectors for each entity. Again, this is fine for simple projects, but what if we have dozens of domain entities? A possible solution would be using a reducer factory:
import { combineReducers } from 'your-favourite-redux-library';
const rootReducer = combineReducers({
entities: {
todo: makeEntityReducer('todo'),
user: makeEntityReducer('user'),
// Other entity reducers
},
// Other reducers
});
This approach has one more serious advantage: we can now create reusable components such as data tables, forms, etc.
React example:
<DataTable entityName="todo" columns={['id', 'name']} />
<DataTable entityName="user" columns={['id', 'name', 'address', 'phone']} />
Angular example:
<app-data-table entityName="todo" [columns]="['id', 'name']"></app-data-table>
<app-data-table entityName="user" [columns]="['id', 'name', 'address', 'phone']"></app-data-table>
Basics
Redux Entity Manager provides a reducer factory, action creators and selectors.
All action creators and selectors have two mandatory parameters: entityName
and query
which are probably best explained by examples:
const entityName = 'user';
const query = null;
const action = makeReadRequestAction(entityName, query);
const users = entitiesSelector(state, { entityName, query });
const entityName = 'user';
const query = {
id: 1,
};
const action = makeReadRequestAction(entityName, query);
const users = entitiesSelector(state, { entityName, query });
const entityName = 'user';
const query = {
page: 1,
limit: 10,
};
const action = makeReadRequestAction(entityName, query);
const users = entitiesSelector(state, { entityName, query });
const entityName = 'user';
const query = {
banned: true,
};
const action = makeReadRequestAction(entityName, query);
const users = entitiesSelector(state, { entityName, query });
Think of how you write a SQL query:
SELECT * FROM users WHERE id = 1
or
SELECT * FROM users LIMIT 10
In Redux Entity Manager, entityName
corresponds to table name in FROM
clause, and query
to everything else.
In fact, much like most RDBMSs, Redux Entity Manager will create an index for each query
to avoid storing duplicate entities.
Please note that Redux Entity Manager only handles data in Redux store. It is your responsibility to write side effects, i.e. http requests. This is an intentional design decision that allows using
- any side effects library (e.g. Redux Thunk, redux-observable, ngrx, etc...)
- any architecture (REST, GraphQL, etc...)
- any additional logic (pagination, cache invalidation, undo/redo, etc...)
Tutorials and Examples
Contributing
Improvements and bugfix PRs are welcome!
If you have an idea for a feature that would break API or tests, please open a discussion before submitting a PR.