2.2.1 • Published 5 years ago

message-in-a-bottle v2.2.1

Weekly downloads
3
License
MIT
Repository
gitlab
Last release
5 years ago

message-in-a-bottle

message-in-a-bottle is a small JavaScript library for organizing business logic. It's an extension of Node's EventEmitter that adds a dispatcher and a plugin system.

Goals

  • Easily tested business logic
  • Easy communication
  • Reusable code (plugins)
  • Centralized error handling
  • Easy debugging

Install

npm install message-in-a-bottle@next

Overview

message-in-a-bottle is a dispatcher that provides inversion of control and dependency injection and allows you to write code in a testable way. It is similar to front-end state management libraries, except that it has no opinion on how you save state. You can expose your persistence API to your functions via dependency injection. On the back-end, this might be a database client. On the front-end, this could be an observable.

message-in-a-bottle is a full-stack JavaScript communication library. It can be used on the front-end , the back-end, or both. If used in the front-end and back-end, plugins can connect the two. Plugins can also enable microservices to talk to each other.

Usage

Create the dispatcher

import { Dispatcher } from 'message-in-a-bottle'
// const { Dispatcher } = require('message-in-a-bottle')

export const dispatcher = new Dispatcher({
	dependencies: {
		db: myDbClient
	}
})

dispatcher.on('error', err => {
	console.error(err)
})

Group related business logic as roles

todo role

export function todoRole (ctx) {
	ctx.addCommand('todo:create', createTodo)
}

export async function createTodo (ctx, todo) {
	const user = await ctx.do('user:get', todo.userId)
	if (!user.verified) throw new Error('Unauthorized')
	return ctx.db.todos.create(todo)
	...
}

user role

export function userRole (ctx) {
	ctx.addCommand('user:get', getUser)
}

export function getUser (ctx, userId) {
	return ctx.db.users.get(userId)
}

Add roles as plugins

dispatcher.use(userRole)
dispatcher.use(todoRole)

Recap

Plugin functions (todoRole) and command handlers (createTodo) have the same signature. The first argument is a JS Proxy (more on that below). The rest of the arguments are custom, and are passed when the plugin is added or the command is dispatched.

dispatcher.use(pluginFunction, arg1, arg2, arg3)
dispatcher.do('command', arg1, arg2, arg3)

About ctx

ctx from the examples above is a JS Proxy. It gives preference first to any dependencies that you've passed in, then to the dispatcher's API. Naming it ctx is just the convention in this documentation.

API

new Dispatcher(options)

  • options Object - depencencies Object - Properties are available to you on the ctx proxy.

addCommand(command, handler)

command argument

The command argument can be a string or object. If it's an object, it must have a $command property. Passing an object allows you to add dependencies and options. Options are prefixed with $. Options $catch and $throw are exclusive. If $catch is present, $throw will be disregarded. Options and dependencies passed here will override those passed in the constructor.

command object

  • $command String
  • $catch String - When this command handler throws an error, catch the error and emit the event indicated by the string value of the $catch option.
  • $throw String - When this command handler throws an error, emit an event indicated by the value of the $throw option, and then re-throw the error. (default is 'warn')
  • $logArguments Boolean - attach the invocation arguments to the error (default is true)
  • myDep - any custom dependency that should be available on the proxy in the handler

broadcast(eventName, ...args)

Same API as Node.js EventEmitter.emit. The difference is that broadcast will emit the events to other machines (via plugins). Use dispatcher.emit to emit events on the same machine. Use broadcast to emit events locally and to other machines.

do(command, ...args)

Pass a string to call a command added with addCommand. Pass a function to invoke that function with the proxy object as the first argument. Pass an object to call a command added with addCommand, while also passing options or dependencies. See the command object from the addComand API. Options and dependencies passed here will override those set in addCommand or in the constructor.

use(plugin, ...args)

plugin return value

A plugin can optionally return an Object that adheres to the interface for a plugin's API.

claimCommand(cmd)

When dispatcher.do(command) is called with a string argument, and the command hasn't been added with addCommand, the dispatcher will loop through the plugins, allowing a plugin to claim the command. The claimCommand function receives the command (String) and returns a handler Function. To not claim a command, the plugin should not return anything. Claiming a command allows plugins to dispatch commands to other machines. Which commands a plugin should claim is configured when adding the plugin. If a handler is on the same machine, a plugin should use addCommand and not claimCommand.

emit(eventName, ...args)

If a plugin's return value has an emit function, it will be called by the dispatcher.broadcast method.

Plugin ecosystem

This repository has some plugin reference implementations. These are not yet tested and will be moved to individual repositories as they mature.

See src/plugins/README.md

Contributing

V2 of message-in-a-bottle is a work in progress. The core library is complete, but the API may change slightly. Developing the plugin ecosystem is where most work remains. Open an issue for any bug reports or feature requests.

3.0.0-alpha.4

5 years ago

3.0.0-alpha.3

5 years ago

2.2.1

5 years ago

3.0.0-alpha.2

5 years ago

3.0.0-alpha.1

5 years ago

2.2.0

5 years ago

2.1.1

5 years ago

2.1.3

5 years ago

2.1.0

5 years ago

2.0.2

5 years ago

2.0.1

5 years ago

2.0.0

5 years ago

2.0.0-alpha.3

5 years ago

2.0.0-alpha.1

5 years ago

2.0.0-alpha.2

5 years ago

1.0.0

5 years ago

0.1.1

5 years ago

0.0.7

5 years ago

0.0.5

5 years ago

0.0.6

5 years ago

0.0.2

6 years ago

0.0.1

6 years ago