[coldbox-3.5.1] SaaS and the use of Modules

In trying to suss out how best to implement a SaaS application using CB, I started to address multiple domain use. Using the multiple domain ses interceptor from forgebox and some modifications, I can now easily “know” how the user is coming into the system with a little creative introspection and a table of valid domains / hosts. Once I have this of course, a database can easily provide configuration options unique to that “client”.

Now there is the problem of host customization. Typically in a SaaS application, much of what a user can do is clearly defined by what is available to the application and the particular host in question…in other words, here’s the SaaS application, take it or leave it. I might give you some creative color and layout control, but customizing the clients experience from a development point of view is only done through pre-determined programmable solutions. For example, client “x” wants a custom form they can provide to their users via a link on their home page. If the SaaS provides a form creation tool and a basic CMS for the front end…done.

However, let’s look at another situation, like storefronts accepting payments. The client can create store items and can apply them to a store front cms by which their users can select materials, apply them to a “cart” and check out. However, SaaS client “x” uses crypt pay and client “y” uses payflow.

So again, the SaaS application and provide the “base” interaction with any number of payment gateways but of course every client will have unique settings which a database can provide for…but now it gets more interesting.

Say one client uses a receipt system whereby a physical file must be dropped in an ftp directory that some other process is going to pick up. So far, no other client needs this. In an of itself, the code needed to do this is pretty simple but since only one client needs it, we want to be able to customize their solution but not impact core code and function in anyway.

Here it comes…

Modules.

It seems that being able to create a module, one per hosted client (unique domain url) would give me the ability to tie core code to a unique module so that initially the experience is the same for everyone…but then to customize the experience, module code can be messed with entirely and not impact core code or other client experiences with the software as a whole. This can be implemented in a simple way, say unique CSS skins for client “x” to the problem I’m outlining…core payment gateway code will have a setting that allows for a custom function call so that when a payment is processed, if the client has a defined custom post processing function, the gateway can check for it and call that code…in this case taking the payment results and information and writing it to a file in a specific location for their FTP use.

However, while this brings up all sorts of great ideas of custom interception points and interceptors and smart data registration of call back functions…my initial investigations yielded two problems I’m not sure I can get over.

-1-
Dynamic module creation and activation.

If I have some installation process that creates new clients (like my administration feature only) and I were to dynamically create a new module based on some settings, that module would not be included as usable until a reinit or the app restarted for some other reason. I don’t want to be reinit’ing the environment every time I need to create a new client. Furthermore, at the session level on down, I really don’t want to “activate” all the modules since only one would be relevant at the time…so when someone first hits the site, the only module (other than public or cross-host modules) loaded is the one relative to that domain. Remember, this cannot be an application wide accessible module, it has to be unique to the request / session.

Can this be done? Does it make sense? The goal here, in case I’ve successfully obfuscated it, is to provide a means for custom development and future unique client solutions without having to adjust “core” code shared by all clients.

-2-
Let’s presume #1 is resolved…the next concern I have is the url. In order to actively approach module code via the url, there is an entry point that clearly identifies in a unique way how to access module code.

uwoshkosh.mike.com/index.cfm/uwoskosh

that url provides me with the unique domain which my interceptor can grab, but then in order to uniquely access the clients module (see above item for details), there has to be a module subdirectory somewhere, in a sea of subdirectories, that uniquely identifies it in the url. We can eventually get rid of the index.cfm part…but you can see it almost appears redundant:

uwoshkosh.mike.com/uwoshkosh

Is there anyway to change the nature of how we access the entry point by setting the module reference in some code-based way so that the session can explain which module directory is being used based on the domain…

uwoshkosh.mike.com/home

would solve the problem…but without the url telling CB which module is being referenced…that won’t work in this format.

Thoughts?

Mike

I know it’s a long post…sorry about that, I can get a bit wordy…I’m just starting to think I have freaked out the masses.

Mike – didn’t quite have time to read your novel :wink:

However, have you looked into models/handlers/views/layouts externalLocations?

That’s how we deal with all of our “client customisations”.

The only issue is that all “clients” need their own Coldbox.cfc – where you specify the custom handlers/view/layout overrides. So you do need to setup virtualhosts manually, and symLink to link to the “core” application for each virtual host. However, we have a bash-script that does this automatically, so it can still be automated.

Wow…I would love to get more detail on this…I will first read about the externalLocations. I had not even considered that…it sounds like a smarter / easier solution.

More to come on this…thanks.

Mike

ok…so I see what you are talking about, I think…using …

coldbox = {
coldboxExtensionsLocation = “”,
modulesExternalLocation = “”,
pluginsExternalLocation = “”,
viewsExternalLocation = “”,
layoutsExternalLocation = “”,
handlersExternalLocation = “”,
requestContextDecorator = “”
};

…and I assume that by including a “client specific” coldbox.cfc, you are overriding these values from the main coldbox configuration file. What I have yet to find in the documentation is how and more importantly when to load it.

Mike

Yeah…I’m getting part of this but not all of it…need some more input when / if you have a chance.

Ok,

Firstly, you have to do this in what I felt was an arse-about-tit way – you set the external locations as the default (main), then the conventions as the overrides.

So here’s a snippet of one of my “custom” Coldbox.cfc files:

pluginsExternalLocation = “plugins”,

viewsExternalLocation = “views”,

layoutsExternalLocation = “layouts”,

handlersExternalLocation = “handlers”,

Then….

conventions = {

handlersLocation = “handlers/custom/bob”,

pluginsLocation = “plugins/custom/bob”,

viewsLocation = “views/custom/bob”,

layoutsLocation = “layouts/custom/bob”,

modelsLocation = “model”,

modulesLocation = “modules”,

eventAction = “index”

};

So when rendering a view (lets say “user/profile”), it will look for “/views/custom/bob/user/profile”, and if that doesn’t exist, it will display the “default” external view, which is “views/user/profile”

That view/layout override also works for modules, so if you had a module called “amazonshop”, for example, you could override the default module view. So “/modules/views/amazon/shop/basket”, could be overridden with “/views/custom/bob/modules/amazon/shop/basket”.

So inside “/views/custom/bob” you can override all views from the main app, and from modules.

Pretty cool stuff :slight_smile:

Tom.

Man. Ok…this is going to keep me busy for a while.

One thing…I cannot find any documentation on loading multiple configuration (coldbox.cfc) files so I need to know:

  • Where do you put the custom configuration for each client
  • When do you load them to override the default coldbox cfc configuration

Thanks for the direction

Well, as I said, I do it using symlinks – but that’s only useful if you’re on linux.

So I have my “core” app in one folder, and then my sites in their own folders.

So I put my application in “/fs/sites/mycoreapp/”

I then symlink each custom site to the core, but exclude Coldbox.cfc

So my “custom” site folder layout is just:

Application.cfc

Config/Coldbox.cfc

Index.cfm

Everything else is then a symlink, i.e

.htaccess -> /fs/sites/mycoreapp/.htaccess

Application.cfc

config

favicon.ico -> /fs/sites/mycoreapp/favicon.ico

handlers -> /fs/sites/mycoreapp/handlers

includes -> /fs/sites/mycoreapp/includes

index.cfm

interceptors -> /fs/sites/mycoreapp/interceptors

layouts -> /fs/sites/mycoreapp/layouts

logs -> /fs/sites/mycoreapp/logs

model -> /fs/sites/mycoreapp/model

modules -> /fs/sites/mycoreapp/modules

plugins -> /fs/sites/mycoreapp/plugins

views -> /fs/sites/mycoreapp/views

robots.txt -> /fs/sites/mycoreapp/robots.txt

I have a bash script which automatically creates these symlinks. I then just upload the Coldbox.cfc, index.cfm and Application.cfc – and create my apache virtual host

Groan…I’m in a windows environment…should I stop this line of thinking or do you think there is a correlary in windows worth pursuing?

http://en.wikipedia.org/wiki/Symbolic_link#Windows_7_.26_Vista_symbolic_link