Service Guide
This guide explains how services work in Vona within the Cabloy monorepo.
Why services matter
A service is usually the business-oriented layer that coordinates controllers, models, validation-related flows, and cross-module behavior.
In practice, the service guide is also the most useful page for understanding backend access patterns.
Create a service
Example: create a service named student in module demo-student.
npm run vona :create:bean service student -- --module=demo-studentService definition
Representative pattern:
@Service()
export class ServiceStudent extends BeanBase {}The three main access styles
Vona backend code commonly uses three access styles:
- dependency injection
- dependency lookup
- direct bean access
A practical rule is:
- prefer dependency lookup as the default for concise business code
- use injection when the surrounding code already follows explicit wiring
- use direct bean access when you need container-level behavior or a fresh bean instance
Dependency injection
By class type
import { ServiceStudent } from '../service/student.ts';
class ControllerStudent {
@Use()
serviceStudent: ServiceStudent;
}By bean identifier
import type { ServiceStudent } from '../service/student.ts';
class ControllerStudent {
@Use('demo-student.service.student')
serviceStudent: ServiceStudent;
}This is the clearest place where bean identifiers become visible in ordinary backend code.
Dependency lookup
Dependency lookup is usually the best default because it keeps the code concise and follows the module resource facade.
Within the current module
class ControllerStudent {
findOne() {
return this.scope.service.student.findOne();
}
}Across modules
class ControllerStudent {
findOne() {
return this.$scope.demoStudent.service.student.findOne();
}
}A useful distinction is:
this.scopemeans local module resourcesthis.$scope.<module>means cross-module resources
That same access model also appears for model, entity, config, locale, and other scope resources.
Direct bean access
Direct bean access patterns are also available.
Global container access: _getBean
const serviceStudent = this.bean._getBean('demo-student.service.student');Request-scoped access
const serviceStudent = this.ctx.bean._getBean('demo-student.service.student');Fresh bean creation: _newBean
const serviceStudent = this.bean._newBean('demo-student.service.student');A practical split is:
this.bean._getBean(...)reaches the app-level container more explicitlythis.ctx.bean._getBean(...)uses request-scoped accessthis.bean._newBean(...)creates a fresh bean instance when the workflow should not reuse the ordinary resolved bean
A practical “when to use which” rule is:
- use
this.scope.service.studentwhen the dependency belongs to the current module and ordinary business code is enough - use
this.$scope.demoStudent.service.studentwhen the dependency clearly belongs to another module - use
this.bean._getBean(...)when you need explicit app-container access by bean identifier - use
this.ctx.bean._getBean(...)when the workflow should resolve through request-scoped access - use
this.bean._newBean(...)when you need a fresh bean instance instead of the ordinary resolved one
Relationship to scope and bean scenes
Services are one bean scene inside the larger backend essentials model.
That means service access should be understood together with:
- bean identifiers such as
demo-student.service.student - local module scope vs cross-module scope
- other scope resource categories such as model and entity
For deciding whether a backend base class should stay a helper, move into service-scene, or remain part of the global bean shorthand surface, also see Class Placement Rule.
Read this guide together with:
Practical implications for service-layer implementation
When creating business logic in Vona, avoid flattening everything into controllers.
A better default is:
- use the CLI to create the service shell
- keep controllers thin
- place business-oriented orchestration into services
- choose deliberately among local scope, cross-module scope, injection, and direct bean access
That keeps backend access patterns aligned with Vona’s actual architecture instead of using one access style everywhere by habit.