Codify your business logic
Inspired by the Interactor gem, this library provides a pattern for encapselating business logic into small, testable units.
npm install @ianwremmel/tracks-interactor
Define your interactor. You'll need to tell it about the shapes of the data it
will accept and produce. Then, you'll need to define call
, which does the
interactor's work. call
accepts a Context<T>
and returns a Context<R>
. (In
the following example, T
is AuthToken
and R
is Model<User>
).
import {Interactor} from 'interactor';
type AuthToken = string;
type User = Model<User>;
class Authorize extends Interactor<AuthToken, User> {
call() {
const user = await User.findByToken(this.context.data);
if (!user) {
this.context.fail('could not find user for specified token');
}
return user;
}
}
Then, use interact
to invoke your Interactor (e.g., in an express route).
import {Authorize} from './interactors/authorize';
import {interact} from 'interactor';
import express from 'express';
const router = express.Router();
router.use(async (req, res, next) => {
const context = await interact(
Authorize,
new Context(req.headers.authorization)
);
if (context.failure) {
next(401);
}
});
router.get('/account', (req, res) => {
res.render('account');
});
call
method. TypeScript
doesn't make type arguments visible to static methods, so we use the bare
method interact
as a stand-in for Interactor.call()
.after
, before
, and around
don't exist. A previous version of this
library included them, but having not used that portion of the library for
over a year, they're continued maintencance didn't seem worth it.ÎA previous version of this package expected a services
object to be passed
to interact
. You should just put your services on the context.
Instead of
interact(services, MyInteractor, {...args});
do
interact(MyInteractor, {services, ...args});
PRs Welcome
MIT © Ian Remmel 2019 until at least now