๐Ÿ”ท Core

coldbox-di

Use this skill when working with dependency injection inside a ColdBox application -- the ColdBox injection DSL namespaces (coldbox:, logbox:, cachebox:, wirebox:, model:), module settings injection, injecting ColdBox services (interceptors, flash, router, scheduler), configuring the WireBox binder in ColdBox context, the enhanced ColdBox binder helpers, or wiring handlers/interceptors/models via ColdBox-aware WireBox.

$ npx skills add coldbox/skills/coldbox/wirebox-di
$ coldbox ai skills install coldbox/skills/coldbox/wirebox-di
๐Ÿ”— https://skills.boxlang.io/skills/raw/coldbox/skills/coldbox~wirebox-di

ColdBox โ€” Dependency Injection

When to Use This Skill

Use this skill when working with DI inside a ColdBox application. It covers the ColdBox-specific injection DSL namespaces and integration features that are not available in standalone WireBox.

For WireBox fundamentals (binder config, scopes, providers, lazy properties, child injectors, object populator), consult the wirebox skill first.

Language Mode Reference

Examples use BoxLang (.bx) syntax by default.

ConceptBoxLang (.bx)CFML (.cfc)
Class declarationclass [extends="..."] {component [extends="..."] {
Property DI annotation@inject above property name="svc";property name="svc" inject="svc";

How WireBox Integrates with ColdBox

ColdBox automatically bootstraps a WireBox injector at startup. Key integration points:

  • Binder location: config/WireBox.cfc (optional โ€” ColdBox works without one and auto-maps models/)
  • Injector access: Available at controller.getWireBox() or via inject="wirebox"
  • Auto-scan: The models/ folder is automatically registered; every CFC is accessible by its name
  • ColdBox objects: Handlers, interceptors, and scheduled tasks are automatically wired by the framework
  • Module isolation: Each ColdBox module gets its own WireBox namespace and can declare its own mappings

ColdBox Binder

// config/WireBox.bx  (or .cfc)
class extends="coldbox.system.ioc.config.ColdBoxBinder" {

    function configure() {
        // getAppMapping() returns the app root dotted path
        // mapDirectory auto-registers all CFCs under models/
        mapDirectory( getAppMapping() & ".models" )

        // Override specific mappings
        map( "EmailService" ).to( getAppMapping() & ".models.email.SendGridService" ).asSingleton()
    }

    function onLoad() {
        // onLoad fires after WireBox is fully online โ€” safe for mapDirectory()
        mapDirectory( getAppMapping() & ".models.plugins" )
    }
}

Note: ColdBox already registers models/ automatically, so an explicit mapDirectory is only needed for additional scan paths or non-convention folders.


Getting Instances

// In handlers, interceptors, scheduled tasks โ€” built-in helper
getInstance( "UserService" )
getInstance( name="UserService", initArguments={ dsn: "myDB" } )

// Via injection
property name="userService" inject;                   // by convention from property name
property name="userService" inject="UserService";     // by explicit ID
property name="userService" inject="id:UserService";  // explicit namespace

Full Injection DSL Namespace Reference

Default / Model Namespace

DSLDescription
(empty inject)Inject by property / arg / setter name
id:{name}Inject named mapping
model:{name}Alias for id:
id:{name}:{method}Inject result of calling method on the mapping
property name="userService"  inject;                        // by convention
property name="authService"  inject="SecurityService";      // by ID
property name="adminRoles"   inject="id:RoleService:getAdminRoles";

WireBox Namespace

DSLDescription
wireboxThe WireBox injector
wirebox:binderThe configuration binder
wirebox:propertiesAll application settings (merged app + coldbox)
wirebox:property:{name}Single property value
wirebox:populatorObject Populator utility
wirebox:scope:{name}Direct scope object reference
wirebox:asyncManagerThe Async Manager
wirebox:eventManagerWireBox's internal event manager
wirebox:parentParent injector (if any)
wirebox:child:{name}Child injector by name
wirebox:child:{name}:{id}Instance from a named child injector
property name="injector"     inject="wirebox";
property name="populator"    inject="wirebox:populator";
property name="appSettings"  inject="wirebox:properties";
property name="apiKey"       inject="wirebox:property:stripe.apiKey";

ColdBox Namespace (ColdBox apps only)

Single-Level

DSLDescription
coldboxThe ColdBox controller

Two-Level

DSLDescription
coldbox:asyncManagerGlobal Async Manager
coldbox:appSchedulerGlobal application scheduler
coldbox:configSettingsApplication configuration settings struct
coldbox:coldboxSettingsInternal ColdBox framework settings struct
coldbox:dataMarshallerData marshaller (JSON/XML/WDDX/plain serialization)
coldbox:flashFlash scope object
coldbox:handlerServiceHandler service
coldbox:interceptorServiceInterceptor service
coldbox:moduleServiceModule service
coldbox:rendererRenderer object (transient)
coldbox:requestContextCurrent request context (transient)
coldbox:requestServiceRequest service
coldbox:routerApplication router
coldbox:routingServiceRouting service
coldbox:schedulerServiceScheduler service
coldbox:loaderServiceLoader service

Three-Level

DSLDescription
coldbox:setting:{key}Application setting by key
coldbox:setting:{key}@{module}Setting from a specific module
coldbox:coldboxSetting:{key}Internal ColdBox framework setting
coldbox:interceptor:{name}Named interceptor reference
coldbox:moduleSettings:{module}Entire module settings struct
coldbox:moduleConfig:{module}Entire module configuration struct

Four-Level

DSLDescription
coldbox:moduleSettings:{module}:{key}Single key from module settings
// ColdBox namespace examples
property name="controller"         inject="coldbox";
property name="flash"              inject="coldbox:flash";
property name="moduleService"      inject="coldbox:moduleService";
property name="asyncManager"       inject="coldbox:asyncManager";
property name="appScheduler"       inject="coldbox:appScheduler";
property name="renderer"           inject="coldbox:renderer";
property name="router"             inject="coldbox:router";

// Settings
property name="dsn"                inject="coldbox:setting:datasource";
property name="apiUrl"             inject="coldbox:setting:apiUrl@myModule";
property name="stripeKey"          inject="coldbox:setting:stripe.key";

// Module config
property name="shopSettings"       inject="coldbox:moduleSettings:shop";
property name="taxRate"            inject="coldbox:moduleSettings:shop:taxRate";

// Named interceptor
property name="securityInterceptor" inject="coldbox:interceptor:Security";

LogBox Namespace

DSLDescription
logboxLogBox instance
logbox:rootRoot logger
logbox:logger:{category}Logger by category string
logbox:logger:{this}Logger using current CFC dotted path as category
property name="log"    inject="logbox:logger:{this}";  // recommended for models
property name="root"   inject="logbox:root";

CacheBox Namespace

DSLDescription
cacheboxCacheBox instance (CacheFactory)
cachebox:defaultDefault cache provider
cachebox:{name}Named cache provider
property name="cacheFactory"  inject="cachebox";
property name="cache"         inject="cachebox:default";
property name="sessionCache"  inject="cachebox:sessions";

Provider Namespace

// Inject a provider to defer construction or avoid scope widening
property name="cart"   inject="provider:SessionCart";

// Provider over a DSL string
property name="logger" inject="provider:logbox:logger:{this}";

// Use .get() to retrieve the actual object
variables.cart.get().addItem( product )

Complete Model Example โ€” Service

// models/UserService.bx
class singleton {

    // Standard model injection
    @inject
    property name="userRepository";

    // App setting
    @inject "coldbox:setting:maxLoginAttempts"
    property name="maxLoginAttempts";

    // Logger scoped to this CFC
    @inject "logbox:logger:{this}"
    property name="log";

    // Cache for performance
    @inject "cachebox:default"
    property name="cache";

    // Module-level setting
    @inject "coldbox:moduleSettings:security:lockoutMinutes"
    property name="lockoutMinutes";

    function login( required string email, required string password ) {
        var cacheKey = "login_attempts_#arguments.email#"
        var attempts = variables.cache.get( cacheKey ) ?: 0

        if ( attempts >= variables.maxLoginAttempts ) {
            log.warn( "Account locked: #arguments.email#" )
            throw( type="AccountLockedException" )
        }

        var user = variables.userRepository.findByEmail( arguments.email )
        if ( isNull( user ) || !user.checkPassword( arguments.password ) ) {
            variables.cache.set( cacheKey, ++attempts, variables.lockoutMinutes )
            throw( type="InvalidCredentialsException" )
        }

        variables.cache.clear( cacheKey )
        return user
    }
}

Complete Model Example โ€” Handler

// handlers/Users.bx
class extends="coldbox.system.EventHandler" {

    // Inject service by convention
    @inject
    property name="userService";

    // Inject a transient provider (scope-widening safe)
    @inject "provider:UserForm"
    property name="userFormProvider";

    // Module setting
    @inject "coldbox:setting:itemsPerPage"
    property name="itemsPerPage";

    function index( event, rc, prc ) {
        prc.users = variables.userService.paginate(
            page  : rc.page  ?: 1,
            limit : variables.itemsPerPage
        )
        event.setView( "users/index" )
    }

    function create( event, rc, prc ) {
        prc.form = variables.userFormProvider.get()
        event.setView( "users/create" )
    }
}

Module DI Configuration

Each ColdBox module has its own models/ auto-scanned and can declare a WireBox.cfc:

// modules/shop/config/WireBox.bx
class extends="coldbox.system.ioc.config.ColdBoxBinder" {

    function configure() {
        // Override the default PaymentGateway for this module
        map( "PaymentGateway" ).to( "modules.shop.models.StripeGateway" ).asSingleton()
    }
}

Inject across module boundaries:

// Inject from another module using @module suffix
property name="authService" inject="id:AuthService@security";

// Via coldbox:setting from another module
property name="apiKey" inject="coldbox:setting:apiKey@payments";

ColdBox Application Settings Injection

Define settings in config/ColdBox.cfc:

// config/ColdBox.bx
class {
    function configure() {
        coldbox = {
            appName : "MyApp"
        }
        settings = {
            maxItems     : 25,
            featureFlags : { darkMode: true, beta: false }
        }
    }
}

Inject them anywhere:

property name="maxItems"   inject="coldbox:setting:maxItems";

// Nested key injection
property name="darkMode"   inject="coldbox:setting:featureFlags.darkMode";

Common Patterns

Environment-Aware Service Registration

// config/WireBox.bx
class extends="coldbox.system.ioc.config.ColdBoxBinder" {

    function configure() {
        if ( getInjector().getBinder().getProperties().environment == "testing" ) {
            map( "EmailService" ).to( "models.email.MockEmailService" ).asSingleton()
        } else {
            map( "EmailService" ).to( "models.email.SendGridService" ).asSingleton()
        }
    }
}

Inject the Request Context (Transient)

class {
    // Must use provider โ€” RequestContext is transient (scoped per request)
    property name="event" inject="provider:coldbox:requestContext";

    function process() {
        var rc = variables.event.get().getCollection()
    }
}

Flash Scope Access in a Service

class singleton {
    @inject "coldbox:flash"
    property name="flash";

    function notify( required string message ) {
        variables.flash.put( "notice", arguments.message )
    }
}

Anti-Patterns to Avoid

Anti-patternSolution
Using application.wirebox.getInstance() instead of injectionUse inject annotation; fallback to getInstance() helper in handlers
Injecting coldbox:requestContext directly into a singletonUse provider:coldbox:requestContext โ€” request context is transient
Hardcoding strings in models instead of injecting settingsUse coldbox:setting:{key} injection
Creating module services via new in handlersUse getInstance() or property injection; services need DI
Re-declaring mappings already auto-scanned from models/Trust auto-scan; only override when you need a different implementation