ColdFusion Components in the MX Era
Use tag-based CFCs to organize business logic while avoiding the scoping and concurrency mistakes common in older applications.
MX history: ColdFusion Components were introduced with ColdFusion MX 6. The examples here use tag-based components and MX-compatible local-variable practices.
A Simple Service Component
<!--- components/CustomerService.cfc --->
<cfcomponent displayname="CustomerService" output="false">
<cffunction name="init" access="public" returntype="CustomerService" output="false">
<cfargument name="dsn" type="string" required="true">
<cfset variables.dsn = arguments.dsn>
<cfreturn this>
</cffunction>
<cffunction name="findById" access="public" returntype="query" output="false">
<cfargument name="customerId" type="numeric" required="true">
<cfset var customerQuery = "">
<cfquery name="customerQuery" datasource="#variables.dsn#">
SELECT id, name, email, status
FROM customers
WHERE id = <cfqueryparam value="#arguments.customerId#" cfsqltype="cf_sql_integer">
</cfquery>
<cfreturn customerQuery>
</cffunction>
</cfcomponent>
Creating the Component
<cfset variables.customerService = CreateObject(
"component",
"components.CustomerService"
)>
<cfset variables.customerService.init("support_portal")>
<cfset variables.customer = variables.customerService.findById(url.id)>
this and variables
| Scope | Meaning in a CFC |
|---|---|
this | Public data and methods visible to callers. Avoid using it as a general dumping ground. |
variables | Private component-instance state such as dependencies and configuration. |
arguments | Inputs to the current method. |
var-scoped variables | Temporary state for the current method invocation in MX-era code. |
Method Access
public: callable by normal component users.private: callable only from within the component.package: callable from components in the same package.remote: callable through supported remote protocols; treat this as an exposed endpoint.
A Private Helper
<cffunction name="normalizeStatus" access="private" returntype="string" output="false">
<cfargument name="status" type="string" required="true">
<cfset var normalizedStatus = LCase(Trim(arguments.status))>
<cfif ListFindNoCase("active,suspended,closed", normalizedStatus) EQ 0>
<cfset normalizedStatus = "closed">
</cfif>
<cfreturn normalizedStatus>
</cffunction>
Remote Methods and Web Services
<cffunction name="getStatus" access="remote" returntype="string" output="false">
<cfargument name="customerId" type="numeric" required="true">
<!--- Authenticate and authorize before returning data. --->
<cfreturn "active">
</cffunction>
remote is a security boundary. Validate arguments, authenticate the caller, authorize the action, restrict returned data, handle faults safely, and understand which protocols expose the method.Inheritance
<cfcomponent extends="BaseService" output="false">
<!--- inherited methods and new methods --->
</cfcomponent>
Inheritance exists in MX-era CFCs, but composition is often easier to understand in a maintenance project. A deep component hierarchy can make it difficult to find where a method or variable originates.
Concurrency and Long-Lived CFCs
A CFC stored in the application or session scope may serve many calls over a long period. Do not place request-specific temporary data in variables. Use properly local variables and locks only where shared state truly requires them.
Design Pattern for Legacy Cleanup
- Identify repeated query and business logic in templates.
- Move one coherent operation into a CFC method.
- Pass dependencies such as the datasource through an
initmethod. - Declare every temporary method variable with
var. - Keep presentation HTML in templates and return data from the CFC.
- Add characterization tests or repeatable manual checks before changing behavior further.