Into The Box Notes: ColdBox HMVC 5 - Luis Majano

April 29, 2018

software is always bound to change
we must be ready to adapt

not getting out of “legacy hell” is what gives CFML a bad name
running on a deprecated environment
nobody wants to work that way
too many old CF engines still in use

60% of apps not using DI
55% not using testing
35% not using MVC

FuseBox is not MVC

this is the reality for a lot of CFML developers
no MVC, lots of CFinclude spaghetti code
no source control
no CI
no deployment strategy beyond manual FTP, etc.

moving these apps around is NOT easy
how do we modernize that?

what can we apply?

1. Cohesion vs Coupling
we want to achieve high cohesion, low coupling

try to create components that can do ONE thing and one thing ONLY and do it RIGHT
and reduce the number of dependencies
it’s impossible to get rid of all dependencies, but you can try to reduce the number of them


2. MVC
separates layers of concerns
enforce OO
but layers are tightly coupled and can become monoliths

3. N-Tier
add more layers for organization
laters are tightly coupled, still monolithic
even tho you’re creating services and objects, it’s still hard to move anything
but easier than the other approaches

4. HMVC
been around for a long time
been in ColdBox for 8 years

we have a “triad”
mini MVC app
request comes in, can be delegated to a specific triad
this is the evolution of MVC
we can start creating these independent mvc triads
there will be dependencies, but we try to adhere to low coupling.

we want to reduce the coupling
modularization
better organization, reusability, testability, etc.

5. Microservices
develop an app as a suite of smaller services

monolithic architecture
when performance is bad, you decide to add more RAM and more CPU speed

OR

build little triads
can be deploying them in separate container instances if need be

chop off the monolith little by little
analyze it, see which parts can be taken out and reused

legacy code updates are less intimidating
fault tolerant (especially in Docker containers)
version-able and maintainable
shorter release cycles
can evolve the monolith
have to identify things first
- open the “entire garage” and see which things are used, not used, critical to the business core
- start breaking them out into pieces
- then can start scaling little by little

CommandBox-Docker —
can run ANY cfml or Java engine
even Blue Dragon
can take something legacy and still Dockerize it
Can run any WAR file

Portable server settings
Portable CFML engine settings

Image healthchecks —
when an app fails, it will restart itself.

comes embedded in CommandBox-Docker

Secure Headless Modes
makes no sense to have an Admin UI in production
secure by design without a UI

Formula for Success:
HMVC + Microservices

“As a system evolves, its complexity will increase unless work is done to maintain or reduce it.’
Lehman’s 2nd Law

HMVC Triad in ColdBox will be ColdBox Modules
mini mvc apps
addressable
composed
dependencies
runtime installed
reloaded at runtime
unloaded at runtime

“You can have results or excuses, not both.”

MVC Conventions
Automatic Model Mappings (DI)
Automatic CFML Mappings
Automatic URL Routing
Automatic Interceptors
Overridable settings
etc

ColdBox MVC Application —
box.json - used by CommandBox
modules — tracked by CommandBox — should NOT go in source control
modules_app — where you put the modules you create
— mainly focused here

Module name
must be unique on disk

A Module ONLY needs
box.json
ModuleConfig.cfc
some piece of code
everything else is optional

in ColdBox 5
/config/Router.cfc
this router is ONLY for this router
it will inject its own URLs in your app
you don’t need to copy this anywhere, not to the parent app, etc.
it will “just work” when your app picks up the module

ModuleConfig.cfc
Simple CFC
can talk to the environment (if it’s in a Docker container, etc)
various metadata properties
- look at the API docs for all the info
- ex:
this.title
this.author
etc
to inform your user what you’re doing/building with this module

once a module is activated
if ModuleConfig.onLoad() exists, it will be executed
good for preparing the module for operation

if it’s unloaded
.onUnload() will be called
can remove records from the database, delete temp files, etc

Leveraging WireBox:
automatically maps al your models to {modelNmae}@{ModuleName}

property name=builder” inject=“builder@qb”;

can also say

property name=builder” inject=“@qb”;
and it will concatenate “builder@qb” for you.

modular execution
can create modules that are units, widgets, etc,
might want to execute them not from url or form but internally:
#runEvent(‘moduleName:users.dashboard’ )#

many ways you can talk to modules — not just from the URL but internally also.

[ code demo ]

coldbox create app skeleton=rest

(also have a rest mvc skeleton available)

install route-visualizer — for ColdBox 5

package set testbox.runner=http://site/tests/runner.cfm
install testbox

testbox run

coldbox create module name=api
creates /modules_app/api

coldbox create module name=v1 directory=nodules_app/api/modules_app

install mockdatacfc

/models
Response.cfc
allows you to wok with Restful responses
guide for writing REST services
Echo
extends Basehandler
leverages that prc scope
can put data in it, and it will be rendered

new model
in api/model
ContactsServic.cfc
component singleton{}
property name=“mockdata” inject=“mockdata@mockdatacfc”;

in function onDICoplete() after DI finishines loading

variables.data = mockdata.mock(
num = 10,
id = “autoincrement”,
name = “name”
age = “age”
email = “email”
);
function list()
{
return variables.data;
}
function get( required id )
{
return variables.data.filter( function( item )
{
return item.id == id;
}).reduce( function results,item )
return item; }, {} ); // if nothing found, return an empty struct
}

/modules_app/v1/handlers
Home.cfc
use CF5 marshaling to make it simple
property  name=“ContactSErvic” inject=“ContactService@api”
b/c the contact Service is in the API models

function index()
return contctsService.list();

/modules_app/v1/
ModuleConfig
cthis.inheritEntryPoint = true
this.entryPoint = “v1”

routes

Home.cfc
function get()
return contactService.get( rc.id ? 0 );

functionindex
return contactService.list()

then fwreinit=1 to refresh and see the changes

/route-visualizer
make sure the routes are working
can see API/v1 in there, etc.

site/api/v1
and we should see the contacts in JSON

/site/api/v1/1
and there is 1 record

create a docker container for you
portainer
“services”
create new container service to deploy in my local swarm
localhost:9000

/cbswagger
instant documentation for your api

coldbox create resource resource=contacts singlarName=contact model=Contact persist=true properties=name,email,age

then
in your Rotuer.cfc
/config/router.cfc

register the route
resources(resources=“contacts”)