LogoLogo
HomeAPIBlog
3.0.0-SNAPSHOT
3.0.0-SNAPSHOT
  • INTRODUCTION
    • Getting Started
      • Running Local Development Servers
      • Beginner Tutorial: Hello World
      • Beginner Tutorial: Hello Database
      • Tutorial: Wheels, AJAX, and You
    • Frameworks and Wheels
    • Requirements
    • Manual Installation
    • Upgrading
    • Screencasts
  • Command Line Tools
    • CLI Overview
    • Quick Start Guide
    • Command Reference
      • Core Commands
        • wheels init
        • wheels info
        • wheels reload
        • wheels deps
        • wheels destroy
        • wheels watch
      • Code Generation
        • wheels generate app
        • wheels generate app-wizard
        • wheels generate controller
        • wheels generate model
        • wheels generate view
        • wheels generate property
        • wheels generate route
        • wheels generate resource
        • wheels generate api-resource
        • wheels generate frontend
        • wheels generate test
        • wheels generate snippets
        • wheels scaffold
      • Database Commands
        • wheels dbmigrate info
        • wheels dbmigrate latest
        • wheels dbmigrate up
        • wheels dbmigrate down
        • wheels dbmigrate reset
        • wheels dbmigrate exec
        • wheels dbmigrate create blank
        • wheels dbmigrate create table
        • wheels dbmigrate create column
        • wheels dbmigrate remove table
        • wheels db schema
        • wheels db seed
      • Testing Commands
        • wheels test
        • wheels test run
        • wheels test coverage
        • wheels test debug
      • Configuration Commands
        • wheels config list
        • wheels config set
        • wheels config env
      • Environment Management
        • wheels env
        • wheels env setup
        • wheels env list
        • wheels env switch
      • Plugin Management
        • wheels plugins
        • wheels plugins list
        • wheels plugins install
        • wheels plugins remove
      • Code Analysis
        • wheels analyze
        • wheels analyze code
        • wheels analyze performance
        • wheels analyze security
      • Security Commands
        • wheels security
        • wheels security scan
      • Performance Commands
        • wheels optimize
        • wheels optimize performance
      • Documentation Commands
        • wheels docs
        • wheels docs generate
        • wheels docs serve
      • CI/CD Commands
        • wheels ci init
      • Docker Commands
        • wheels docker init
        • wheels docker deploy
      • Deployment Commands
        • wheels deploy
        • wheels deploy audit
        • wheels deploy exec
        • wheels deploy hooks
        • wheels deploy init
        • wheels deploy lock
        • wheels deploy logs
        • wheels deploy proxy
        • wheels deploy push
        • wheels deploy rollback
        • wheels deploy secrets
        • wheels deploy setup
        • wheels deploy status
        • wheels deploy stop
    • CLI Development Guides
      • Creating Commands
      • Service Architecture
      • Migrations Guide
      • Testing Guide
  • Working with Wheels
    • Conventions
    • Configuration and Defaults
    • Directory Structure
    • Switching Environments
    • Testing Your Application
    • Using the Test Environment
    • Contributing to Wheels
    • Submitting Pull Requests
    • Documenting your Code
  • Handling Requests with Controllers
    • Request Handling
    • Rendering Content
    • Redirecting Users
    • Sending Files
    • Sending Email
    • Responding with Multiple Formats
    • Using the Flash
    • Using Filters
    • Verification
    • Event Handlers
    • Routing
    • URL Rewriting
      • Apache
      • IIS
      • Tomcat
      • Nginx
    • Obfuscating URLs
    • Caching
    • Nesting Controllers
    • CORS Requests
  • Displaying Views to Users
    • Pages
    • Partials
    • Linking Pages
    • Layouts
    • Form Helpers and Showing Errors
    • Displaying Links for Pagination
    • Date, Media, and Text Helpers
    • Creating Custom View Helpers
    • Localization
  • Database Interaction Through Models
    • Object Relational Mapping
    • Creating Records
    • Reading Records
    • Updating Records
    • Deleting Records
    • Column Statistics
    • Dynamic Finders
    • Getting Paginated Data
    • Associations
    • Nested Properties
    • Object Validation
    • Object Callbacks
    • Calculated Properties
    • Transactions
    • Dirty Records
    • Soft Delete
    • Automatic Time Stamps
    • Database Migrations
      • Migrations in Production
    • Using Multiple Data Sources
  • Plugins
    • Installing and Using Plugins
    • Developing Plugins
    • Publishing Plugins
  • Project Documentation
    • Overview
  • External Links
    • Source Code
    • Issue Tracker
    • Sponsor Us
    • Community
Powered by GitBook
LogoLogo
On this page
  • Overview
  • Architecture Diagram
  • Core Components
  • 1. Commands (/commands/wheels/)
  • 2. Services (/models/)
  • 3. Base Classes
  • Service Catalog
  • Core Services
  • Feature Services
  • Dependency Injection
  • Creating a New Service
  • 1. Create Service Component
  • 2. Register Service
  • 3. Use in Commands
  • Service Patterns
  • 1. Singleton Pattern
  • 2. Factory Pattern
  • 3. Strategy Pattern
  • Testing Services
  • Unit Testing Services
  • Mocking Dependencies
  • Best Practices
  • 1. Single Responsibility
  • 2. Dependency Injection
  • 3. Error Handling
  • 4. Configuration
  • Service Communication
  • Event-Driven
  • Direct Communication
  • Extending Services
  • Creating Service Plugins
  • Service Decorators
  • Performance Considerations
  • 1. Lazy Loading
  • 2. Caching
  • 3. Async Operations
  • Debugging Services
  • Enable Debug Logging
  • Service Inspection
  • Future Enhancements
  • See Also

Was this helpful?

Edit on GitHub
Export as PDF
  1. Command Line Tools
  2. CLI Development Guides

Service Architecture

Understanding the Wheels CLI service layer architecture.

Overview

The Wheels CLI uses a service-oriented architecture that separates concerns and provides reusable functionality across commands. This architecture makes the CLI more maintainable, testable, and extensible.

Architecture Diagram

┌─────────────────┐
│    Commands     │  ← User interacts with
├─────────────────┤
│    Services     │  ← Business logic
├─────────────────┤
│     Models      │  ← Data and utilities
├─────────────────┤
│   Templates     │  ← Code generation
└─────────────────┘

Core Components

1. Commands (/commands/wheels/)

Commands are the user-facing interface:

// commands/wheels/generate/model.cfc
component extends="wheels.cli.models.BaseCommand" {
    
    property name="codeGenerationService" inject="CodeGenerationService@wheels-cli";
    property name="migrationService" inject="MigrationService@wheels-cli";
    
    function run(
        required string name,
        boolean migration = true,
        string properties = "",
        boolean force = false
    ) {
        // Delegate to services
        var result = codeGenerationService.generateModel(argumentCollection=arguments);
        
        if (arguments.migration) {
            migrationService.createTableMigration(arguments.name, arguments.properties);
        }
        
        print.greenLine("✓ Model generated successfully");
    }
}

2. Services (/models/)

Services contain reusable business logic:

// models/CodeGenerationService.cfc
component accessors="true" singleton {
    
    property name="templateService" inject="TemplateService@wheels-cli";
    property name="fileService" inject="FileService@wheels-cli";
    
    function generateModel(
        required string name,
        string properties = "",
        struct associations = {},
        boolean force = false
    ) {
        var modelName = helpers.capitalize(arguments.name);
        var template = templateService.getTemplate("model");
        var content = templateService.populateTemplate(template, {
            modelName: modelName,
            properties: parseProperties(arguments.properties),
            associations: arguments.associations
        });
        
        return fileService.writeFile(
            path = "/models/#modelName#.cfc",
            content = content,
            force = arguments.force
        );
    }
}

3. Base Classes

BaseCommand (/models/BaseCommand.cfc)

All commands extend from BaseCommand:

component extends="commandbox.system.BaseCommand" {
    
    property name="print" inject="print";
    property name="fileSystemUtil" inject="FileSystem";
    
    function init() {
        // Common initialization
        return this;
    }
    
    // Common helper methods
    function ensureDirectoryExists(required string path) {
        if (!directoryExists(arguments.path)) {
            directoryCreate(arguments.path, true);
        }
    }
    
    function resolvePath(required string path) {
        return fileSystemUtil.resolvePath(arguments.path);
    }
}

Service Catalog

Core Services

TemplateService

Manages code generation templates:

  • Load templates

  • Variable substitution

  • Custom template support

MigrationService

Handles database migrations:

  • Generate migration files

  • Track migration status

  • Execute migrations

TestService

Testing functionality:

  • Run TestBox tests

  • Generate coverage reports

  • Watch mode support

CodeGenerationService

Centralized code generation:

  • Generate models, controllers, views

  • Handle associations

  • Manage validations

Feature Services

AnalysisService

Code analysis tools:

  • Complexity analysis

  • Code style checking

  • Dependency analysis

SecurityService

Security scanning:

  • SQL injection detection

  • XSS vulnerability scanning

  • Hardcoded credential detection

OptimizationService

Performance optimization:

  • Cache analysis

  • Query optimization

  • Asset optimization

PluginService

Plugin management:

  • Install/remove plugins

  • Version management

  • Dependency resolution

EnvironmentService

Environment management:

  • Environment switching

  • Configuration management

  • Docker/Vagrant support

Dependency Injection

Services use WireBox for dependency injection:

// In ModuleConfig.cfc
function configure() {
    // Service mappings
    binder.map("TemplateService@wheels-cli")
        .to("wheels.cli.models.TemplateService")
        .asSingleton();
    
    binder.map("MigrationService@wheels-cli")
        .to("wheels.cli.models.MigrationService")
        .asSingleton();
}

Creating a New Service

1. Create Service Component

// models/MyNewService.cfc
component accessors="true" singleton {
    
    // Inject dependencies
    property name="fileService" inject="FileService@wheels-cli";
    property name="print" inject="print";
    
    function init() {
        return this;
    }
    
    function doSomething(required string input) {
        // Service logic here
        return processInput(arguments.input);
    }
    
    private function processInput(required string input) {
        // Private helper methods
        return arguments.input.trim();
    }
}

2. Register Service

In /ModuleConfig.cfc:

binder.map("MyNewService@wheels-cli")
    .to("wheels.cli.models.MyNewService")
    .asSingleton();

3. Use in Commands

component extends="wheels.cli.models.BaseCommand" {
    
    property name="myNewService" inject="MyNewService@wheels-cli";
    
    function run(required string input) {
        var result = myNewService.doSomething(arguments.input);
        print.line(result);
    }
}

Service Patterns

1. Singleton Pattern

Most services are singletons:

component singleton {
    // Shared instance across commands
}

2. Factory Pattern

For creating multiple instances:

component {
    function createGenerator(required string type) {
        switch(arguments.type) {
            case "model":
                return new ModelGenerator();
            case "controller":
                return new ControllerGenerator();
        }
    }
}

3. Strategy Pattern

For different implementations:

component {
    function setStrategy(required component strategy) {
        variables.strategy = arguments.strategy;
    }
    
    function execute() {
        return variables.strategy.execute();
    }
}

Testing Services

Unit Testing Services

component extends="testbox.system.BaseSpec" {
    
    function beforeAll() {
        // Create service instance
        variables.templateService = createMock("wheels.cli.models.TemplateService");
    }
    
    function run() {
        describe("TemplateService", function() {
            
            it("loads templates correctly", function() {
                var template = templateService.getTemplate("model");
                expect(template).toBeString();
                expect(template).toInclude("component extends=""Model""");
            });
            
            it("substitutes variables", function() {
                var result = templateService.populateTemplate(
                    "Hello {{name}}",
                    {name: "World"}
                );
                expect(result).toBe("Hello World");
            });
            
        });
    }
}

Mocking Dependencies

function beforeAll() {
    // Create mock
    mockFileService = createEmptyMock("FileService");
    
    // Define behavior
    mockFileService.$("writeFile").$results(true);
    
    // Inject mock
    templateService.$property(
        propertyName = "fileService",
        mock = mockFileService
    );
}

Best Practices

1. Single Responsibility

Each service should have one clear purpose:

// Good: Focused service
component name="ValidationService" {
    function validateModel() {}
    function validateController() {}
}

// Bad: Mixed responsibilities
component name="UtilityService" {
    function validateModel() {}
    function sendEmail() {}
    function generatePDF() {}
}

2. Dependency Injection

Always inject dependencies:

// Good: Injected dependency
property name="fileService" inject="FileService@wheels-cli";

// Bad: Direct instantiation
variables.fileService = new FileService();

3. Error Handling

Services should handle errors gracefully:

function generateFile(required string path) {
    try {
        // Attempt operation
        fileWrite(arguments.path, content);
        return {success: true};
    } catch (any e) {
        // Log error
        logError(e);
        // Return error info
        return {
            success: false,
            error: e.message
        };
    }
}

4. Configuration

Services should be configurable:

component {
    property name="settings" inject="coldbox:modulesettings:wheels-cli";
    
    function getTimeout() {
        return variables.settings.timeout ?: 30;
    }
}

Service Communication

Event-Driven

Services can emit events:

// In service
announce("wheels-cli:modelGenerated", {model: modelName});

// In listener
function onModelGenerated(event, data) {
    // React to event
}

Direct Communication

Services can call each other:

component {
    property name="validationService" inject="ValidationService@wheels-cli";
    
    function generateModel() {
        // Validate first
        if (!validationService.isValidModelName(name)) {
            throw("Invalid model name");
        }
        // Continue generation
    }
}

Extending Services

Creating Service Plugins

// models/plugins/MyServicePlugin.cfc
component implements="IServicePlugin" {
    
    function enhance(required component service) {
        // Add new method
        arguments.service.myNewMethod = function() {
            return "Enhanced!";
        };
    }
}

Service Decorators

component extends="BaseService" {
    
    property name="decoratedService";
    
    function init(required component service) {
        variables.decoratedService = arguments.service;
        return this;
    }
    
    function doSomething() {
        // Add behavior
        log("Calling doSomething");
        // Delegate
        return variables.decoratedService.doSomething();
    }
}

Performance Considerations

1. Lazy Loading

Load services only when needed:

function getTemplateService() {
    if (!structKeyExists(variables, "templateService")) {
        variables.templateService = getInstance("TemplateService@wheels-cli");
    }
    return variables.templateService;
}

2. Caching

Cache expensive operations:

component {
    property name="cache" default={};
    
    function getTemplate(required string name) {
        if (!structKeyExists(variables.cache, arguments.name)) {
            variables.cache[arguments.name] = loadTemplate(arguments.name);
        }
        return variables.cache[arguments.name];
    }
}

3. Async Operations

Use async for long-running tasks:

function analyzeLargeCodebase() {
    thread name="analysis-#createUUID()#" {
        // Long running analysis
    }
}

Debugging Services

Enable Debug Logging

component {
    property name="log" inject="logbox:logger:{this}";
    
    function doSomething() {
        log.debug("Starting doSomething with args: #serializeJSON(arguments)#");
        // ... logic ...
        log.debug("Completed doSomething");
    }
}

Service Inspection

# In CommandBox REPL
repl> getInstance("TemplateService@wheels-cli")
repl> getMetadata(getInstance("TemplateService@wheels-cli"))

Future Enhancements

Planned service architecture improvements:

  1. Service Mesh: Inter-service communication layer

  2. Service Discovery: Dynamic service registration

  3. Circuit Breakers: Fault tolerance patterns

  4. Service Metrics: Performance monitoring

  5. API Gateway: Unified service access

See Also

PreviousCreating CommandsNextMigrations Guide

Last updated 2 days ago

Was this helpful?

Creating Custom Commands
Template System
Testing Guide
Contributing Guide