command-pack
v1.1.4
Published
cqs pattern for redux
Downloads
8
Readme
Command-Pack for redux
CQS implementation for redux https://martinfowler.com/bliki/CommandQuerySeparation.html
Installation
npm i -S command-pack
Counter Sample
Sample for Counter
First I need to explain what is command
and handler
. Basically a command is just a data dictionary that carries required parameters for the handler
.
To create a command it is needed to make a new brand class that extends command-pack Command
.
Commands:
For our counter sample, we need two commands: First, IncreaseCounter to increase total and DecreaseCounter.
- IncreaseCounter.js
import {Command} from "command-pack";
export default class IncreaseCounter extends Command {
amount;
constructor(amount) {
super();
this.amount = amount;
}
handle(state) {
return {
...state,
total: state.total + this.amount
};
}
}
- DecreaseCounter.js
import {Command} from "command-pack";
export default class IncreaseCounter extends Command {
amount;
constructor(amount) {
super();
this.amount = amount;
}
handle(state) {
return {
...state,
total: state.total - this.amount
};
}
}
Now we have commands... and they know how to handle this data parameters with handle method by given state
.
Command Registration
import { CommandContainer } from "command-pack";
const container = new CommandContainer("counterStore") // Store name
.setDefaultState({total: 0}) // Store default state
.register(IncreaseCounter) // our commands
.register(DecreaseCounter);
CommandExecutor
import { CommandContainer, CommandExecutor} from "command-pack";
CommandExecutor.add(new CommandContainer("counterStore")
.setDefaultState({total: 0})
.register(IncreaseCounter)
.register(DecreaseCounter)
);
CommandExecutor.createStore() for Redux-Provider
import React from 'react';
import ReactDOM from 'react-dom';
import Provider from "react-redux/src/components/Provider";
import { CommandContainer, CommandExecutor} from "command-pack";
import IncreaseCounter from "./components/counter/IncreaseCounter";
import DecreaseCounter from "./components/counter/DecreaseCounter";
import Counter from './components/counter/Counter';
CommandExecutor.add(new CommandContainer("counterStore")
.setDefaultState({total: 0})
.register(IncreaseCounter)
.register(DecreaseCounter)
);
ReactDOM
.render(
<Provider store={CommandExecutor.createStore()}>
<div>
<h2>COUNTER Sample</h2>
<Counter/>
</div>
</Provider>
, document.getElementById('root'));
registerServiceWorker();
Counter React Component
import React from 'react'
import {connect} from "react-redux";
import {CommandExecutor} from "command-pack";
import DecreaseCounter from "./DecreaseCounter";
import IncreaseCounter from "./IncreaseCounter";
class Counter extends React.Component {
inc() {
CommandExecutor.execute(new IncreaseCounter(1));
}
dec() {
CommandExecutor.execute(new DecreaseCounter(1));
}
render() {
const total = this.props.counterStore.total;
return (<div>Counter : {total}
<button onClick={this.dec.bind(this)}>decrease</button>
<button onClick={this.inc.bind(this)}>increase</button>
</div>)
}
}
export default connect(x => {
return {counterStore: x.counterStore}
})(Counter);
ASYNC example
To solve that problem, I tried with middlewares thunk and etc... But it just increases complexity. With command-pack it is really easy to implement an async flow.
Basically you need two commands:
StartDownload
to start flowDownloadCompleted
to get the results
- StartDownload.js
import {Command, CommandExecutor} from "command-pack";
export default class StartDownload extends Command {
url;
constructor(url) {
super();
this.url = url;
}
handle(state) {
fetch (this.url).then (content=>{
CommandExecutor.execute (new DownloadCompleted (content));
});
return {
...state,
downloading : true
};
}
}
- DownloadCompleted.js
import {Command} from "command-pack";
export default class DownloadCompleted extends Command {
content;
constructor(content) {
super();
this.content = content;
}
handle(state) {
return {
...state,
downloading : false,
content : this.content
};
}
}