Last active
May 17, 2022 15:20
-
-
Save danharper/87269a0628ed97fb026e to your computer and use it in GitHub Desktop.
JavaScript Command Bus
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const handlers = Symbol('handlers'); | |
// adapts a handler class to a common interface | |
// (technically this one isn't required - the handler class already implements the same interface) | |
class ClassDispatchable { | |
constructor(theClass) { | |
this.theClass = theClass; | |
} | |
handle(command) { | |
return (new this.theClass).handle(command); | |
} | |
} | |
// adapts a handler function to a common interface | |
class FuncDispatchable { | |
constructor(func) { | |
this.func = func; | |
} | |
handle(command) { | |
return this.func(command); | |
} | |
} | |
// the command bus - where the magic happens | |
class Bus { | |
constructor() { | |
this[handlers] = {}; | |
} | |
// register a class as the handler for a command | |
register(command, theClass) { | |
this[handlers][command.__COMMAND__] = new ClassDispatchable(theClass); | |
return this; | |
} | |
// register a function as the handler for a command | |
registerFunc(command, func) { | |
this[handlers][command.__COMMAND__] = new FuncDispatchable(func); | |
return this; | |
} | |
// given a command instance, dispatch to the registered handler | |
dispatch(command) { | |
var handler = this[handlers][command.constructor.__COMMAND__]; | |
if (handler) { | |
return handler.handle(command); | |
} | |
} | |
} | |
export default Bus |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// given a class, adds a unique __COMMAND__ Symbol which we can | |
// use to identify handlers for the command within the Bus | |
export default function(command) { | |
command.__COMMAND__ = Symbol(); | |
return command; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import command from '../core/command' | |
// a command is just a plain DTO class | |
class AddUnitCommand { | |
constructor(name) { | |
this.name = name; | |
} | |
} | |
export default command(AddUnitCommand) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// a class-based handler | |
// when given a Command to handle(), performs the relevant actions | |
export default class AddUnit { | |
handle(command) { | |
console.log(`adding unit with name ${command.name}`, command) | |
return ':)' | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import command from '../core/command' | |
class RemoveUnitCommand { | |
constructor(id) { | |
this.id = id; | |
} | |
} | |
export default command(RemoveUnitCommand) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// a function-based handler | |
// when called with a Command, performs the relevant actions | |
export default function(command) { | |
console.log(`removing unit with id ${command.id}`) | |
return ':(' | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Bus from './core/Bus' | |
import AddUnitCommand from './commands/AddUnitCommand' | |
import AddUnit from './handlers/AddUnit' | |
import RemoveUnitCommand from './commands/RemoveUnitCommand' | |
import RemoveUnit from './handlers/RemoveUnit' | |
// AppBus is an instance of the bus with all Commands & Handlers registered | |
export default (new Bus) | |
.register(AddUnitCommand, AddUnit) | |
.registerFunc(RemoveUnitCommand, RemoveUnit) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AppBus from './AppBus' | |
import AddUnitCommand from './commands/AddUnitCommand' | |
import RemoveUnitCommand from './commands/RemoveUnitCommand' | |
// dispatching "AddUnitCommand" executes the "AddUnit" class | |
var a = AppBus.dispatch(new AddUnitCommand('Foo Bar')); | |
console.log('a', a); // :) | |
// dispatching "RemoveUnitCommand" executes the "RemoveUnit" function | |
var b = AppBus.dispatch(new RemoveUnitCommand(2)); | |
console.log('b', b); // :( |
Note to self: Need to swap out the __COMMAND__
properties with a symbol for private access - already done in a side project, need to add back into this gist.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Handlers then communicate with a Store/Repository/whatever-you-want-to-call it to retrieve, add, persist and remove entities. That Store is responsible for emitting
change
events which Views listen for to update.