The Nodevel Framework
Nodevel (v1.1, the core/ directory in each repo) is a lightweight,
in-house dependency-injection boot framework over Express + Mongoose + Redis. It
is shared by three services: backend,
ms-communication, and
ms-sessions. (ms-ai does not use it — it's
NestJS.)
If you've worked in one Nodevel service, the others are structurally identical.
The boot sequence
// index.js
require('./otel.js') // tracing/Sentry first
const Application = require('./core/application.js')
new Application().run()
core/application.js run() executes a fixed sequence:
loadEnvironmentSettings()— dotenv → a globalConfigsingleton.loadDependencySettings()— build the DI container; resolve models, services, and controllers.loadHookSettings()— runapp/hooks.jsboot()thenserver().loadRouteSettings()— load route files referenced fromapp/configs/routes.js/app/routes/api.js.startServer()— bind HTTP (and attach Socket.IO), unlessglobal.__skip_server = true(used by worker processes).
Core components
| Path | Role |
|---|---|
core/application.js | Boot orchestrator (the sequence above) |
core/dependency/resolver.js | DI container — maps app.models.UserModel → a singleton instance; caches in global.__resolver_cache |
core/http/router.js | Express wrapper (Router.get/post, middleware chaining) |
core/helpers/config.js | Config singleton — Config.get('redis.host') |
core/controller.js | Base controller + DI injection |
The app/ layout (every Nodevel service)
app/
├── configs/ service toggles, database, redis, routes, integrations
├── controllers/ request handlers (resolved + injected by the DI container)
├── models/ Mongoose models
├── routes/ route files; api.js is the root
├── services/ singletons loaded via app.loadService(...)
├── jobs/ cron jobs (node-cron, wrapped by a monitor)
├── lib/ helpers/ shared utilities
└── hooks.js boot(app) — init DB/services/middleware; server(app) — sockets, error handlers
Magic methods
The framework decorates the Express application with helpers:
app.loadService('redis', 'app.services.RedisService') // register a singleton service
app.dependOn('some-package') // ensure an npm dep is present
express_application.controller('UserController') // fetch a resolved controller
app/hooks.js boot() is where a service wires itself up — e.g. backend loads
Redis, Auth, Mongo, Scheduler, Mailer, the identity-graph subsystem, and the
Knox auto-engagement pipeline there.
Why it matters in practice
- Services lazy-load via
loadService()and are singletons — don'tnewthem yourself. - Workers (e.g. ms-sessions' recording worker) reuse the same app but set
__skip_server = trueso they don't bind a port — they just consume queues. - Adding a feature means: a model in
app/models/, a service inapp/services/, a controller inapp/controllers/, and a route file wired intoapp/routes/api.js— not free-form Express middleware.
Cron & queues
- Cron is node-cron, registered through a
SchedulerServiceand wrapped by aCronMonitor(Redis-backed run history, visible at/admin/cronin backend). It is not BullMQ. - BullMQ is used for heavier async work in ms-sessions (recording analysis)
and ms-ai. Bull Board is mounted at
/admin/queues.
This split (cron for scheduled sweeps, BullMQ for heavy pipelines) is a recurring pattern across the platform.