Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Randy Brown and Sebastian Zartner
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Randy Brown Sebastian Zartner

A Better Understanding Of MVC (Model-View-Controller) Thanks To Steven Neiland

By on
Tags:

In my web applications development, I use and love ColdFusion components! However, my use of them is definitely sub-optimal. My approach makes use of a two-tier model consisting of what might loosely be thought of as a Controller and a Gateway layer. This is fine for small applications where not much business logic is repeated. But, in larger applications (which I am starting to build), this approach leads to duplication of core code. Thankfully, Steven Neiland was kind enough to sit down with me at cf.Objective() 2012 and help me understand how I might improve my code with a better MVC (Model-View-Controller) architecture.

Over the course of two days, Steven and I sat down and sketched out some rough domain concepts and System Sequence Diagrams (SSD).


 
 
 

 
MVC diagrams and System Sequence Diagrams (SSD) with Steven Neiland and Ben Nadel.  
 
 
 

As it turns out, my "controller" layer was really a Frankenstein monster, acting as both the Controller and the Service layer of my domain model; I had it both routing requests and executing critical business logic. My "service" layer, on the hand, was actually a Gateway, doing little more than wrapping database access.

To frame the context of this MVC (Model-View-Controller) exploration, I came up with a scenario that I have at work: I have an application that requires both cookie-based authentication (standard web use) and Header-based authentication (API use). This is an important divide because the cookie-based authentication makes use of ColdFusion session management where as the header-based authentication is session-free (RESTful).

To allow for both of these authentication approaches in the same application, the Service layer must remain session-agnostic. This constraint means that it must be the Controller's job to translate session data into request data that can be explicitly passed into Service layer for execution of business logic.

I know there's never "one" way to do something; but coming up with general guidelines, like the one above, helps me to wrap my head around how all of the parts fit together. After my discussions with Steven, here's my basic understanding of the three-tier model:

Controller - The "C" in "MVC"

The Controller's job is to translate incoming requests into outgoing responses. In order to do this, the controller must take request data and pass it into the Service layer. The service layer then returns data that the Controller injects into a View for rendering. This view might be HTML for a standard web request; or, it might be something like JSON (JavaScript Object Notation) for a RESTful API request.

Red Flags: My Controller architecture might be going bad if:

  • The Controller makes too many requests to the Service layer.
  • The Controller makes a number of requests to the Service layer that don't return data.
  • The Controller makes requests to the Service layer without passing in arguments.

If the above "red flag" conditions start to happen, then I probably have a Controller layer that is doing too much. I should probably factor the logic out into a more cohesive Service layer that can be invoked by the Controller. Furthermore, if the Controller is making requests to the Service layer without passing in arguments, then I may have a Service layer that is breaking encapsulation (ex. referring to the Session, CGI, FORM, or URL scopes).

View - The "V" in "MVC"

The View's job is to translate data into a visual rendering for response to the Client (ie. web browser or other consumer). The data will be supplied primarily by the Controller; however, the View may also have a helper that can retrieve data that is associated with the rendering and not necessarily with the current request (ex. aside data, footer data).

Red Flags: My View architecture might be going bad if:

  • The View contains business logic.
  • The View contains session logic.

If the above "red flag" conditions start to happen, then I probably have a View layer that is doing too much. Business logic should be factored out into a Service object. And, session references should probably be factored out into a Helper object or a Controller. This will make the Views easier to build and to test since they rely solely on data and not on an entire application being in place.

NOTE: View rendering is something that I am still fuzzy on. My current approach uses a CFM-only approach to rendering; as such, integrating Components into a view rendering lifecycle is still something that I need to think about.

Model - The "M" in "MVC"

The Model's job is to represent the problem domain, maintain state, and provide methods for accessing and mutating the state of the application. The Model layer is typically broken down into several different layers:

  • Service layer - this layer provides cohesive, high-level logic for related parts of an application. This layer is invoked directly by the Controller and View helpers.
  • Data Access layer - (ex. Data Gateway, Data Access Object) this layer provides access to the persistence layer. This layer is only ever invoked by Service objects. Objects in the data access layer do not know about each other.
  • Value Objects layer - this layer provides simple, data-oriented representations of "leaf" nodes in your model hierarchy.

Red Flags: My Model architecture might be going bad if:

  • The Model contains session logic.
  • The Value Objects retain (ie. store) references to Service objects or Gateway objects.

If the above "red flag" conditions start to happen, then I probably have a Model layer that is breaking encapsulation. By letting the service layer refer to sessions, I am also creating a Model that is hard to test without having an entire application in place. Furthermore, having session references in the model makes the model incredibly hard to reuse across different controllers (as per my web/API split mentioned earlier).

I know that a lot of this information is probably old-news to most of you. As Steven even said, "I know exactly how you're feeling - this is where I was 2 years ago." My understanding of programming has always been at the lower level - the detail level; my understand of architecture has always lagged behind (both in ColdFusion and in JavaScript). Over the last few months, however, I've been really trying hard to learn about modular JavaScript application architecture and ColdFusion application architecture. I feel like I'm finally making some big leaps forward.

A huge thanks to Steven Neiland who took the time to sit down with me and help me understand this stuff. I know there's never enough time at Conferences like cf.Objective() to talk to everyone; as such, I'm very thankful that he took a good amount of time to talk to me.

Reader Comments

25 Comments

This is a great breakdown Ben. I'm right there with you, in fact you're probably further along than I am.

What I'd like to see is a directory structure of files and folders on how you'd organize code a simple app which uses MVC.

index.cfm
-- models
----user.cfc
-- views
---- display_users.cfm
-- controllers
---- something.cfc

Obviously mine is wrong, but I'd love to see how you might build out the structure. I don't even care about code, more about organization.

61 Comments

You're very welcome Ben. Its always great to share knowledge with people and look at problems from a different perspective.

Hit me up when you get further along in the design of that system we were discussing if you want a second pair of eyes on the structure.

One point I would like to clarify for you. On accessing the session scope. I always do this from the service layer, but I make the rule that only the service that initializes that session object can reference it directly. Any other service object must go through an accessor function on the parent service.

Note: This is only how I do this. Others have a different approach.

Example
securityService.cfc creates session.user and has a public method getUserID()

productService.cfc used by staff to add/remove products from a web store gets a reference to this service on init stored in variables.securityService

Then when a products controller addProduct calls "productService.addProducts({some arguments})" the addProducts() method calls the securityService to get the userId

<cfcomponent displayname="productService">

<cffunction name="init">
<!---args --->
<cfset variables.securityService = arguments.securityService>
<cfreturn this>
</cffunction>

<cffunction name="addProduct">
<!---arguments--->

<cfset var addedByUserId = variables.securityService.getUserId()>

<!---logic to add the product--->
</cffunction>

When you get to testing a service like this you can then mock the security service either by creating a stub securityService component or using something like mockbox.

Now this is only one way of handling it, but the nice thing about this approach is that you only need to make one call from the controller to the service layer and then all other logic happens in the service layer.

In an situation where you want to access a products listing using either a logged in userId or a passed in urlauthtoken you would pass in the authtoken from the controller to the service as an optional argument. If the token was passed in the getProducts method could then switch between authentication methods.

EG
<!--- product service --->
<cffunction name="listProducts">
<cfargument name="authToken" type="string" required="false" default="">

<cfset var permittedAll = variables.securityService.isAdmin(authToken=authtoken)>

</cffunction>

<!--- securityservice--->
<cffunction name="isAdmin">
<cfargument name="authToken">

<cfset var userIsAdmin = false>

<cfif arguments.authtoken NEQ "">
<!--- logic to determine if this is an admin user--->
<cfelse>
<!--- check if the session.user is an admin --->
</cfif>

<cfreturn userIdAdmin
</cfif>

<!--- Breaking encapsulation --->
I try to never break encapsulation but in this instance it would make sense to do so for the security service by use onRequestStart to check for the existence of the authtoken and param it in the request scope. This way none of the controllers need to worry about passing in tokens to the service layer.

<cffunction name="onRequestStart">
<cfparam name="url.authToken" default="">
<cfset request.authToken = url.authToken>
</cffunction>

Then you would remove the authToken argument form the controller and service layer and have the securityService method look like this.

<!--- securityservice--->
<cffunction name="isAdmin">

<cfset var userIsAdmin = false>

<cfif request.authtoken NEQ "">
<!--- logic to determine if this is an admin user--->
<cfelse>
<!--- check if the session.user is an admin --->
</cfif>

<cfreturn userIdAdmin
</cfif>

-------------------------------------------------
As I said this is only how I do this but for me it makes more sense as it breaks down each function of the application into discreet areas of speciality.

7 Comments

Andy - that will often be dictated by your framework. No reason to roll your own MVC architecture when there are so many good ones already defined. In my Model-Glue applications for example, each subsection of the app has its own folder, and in each of those folders is a subfolder for the controller and the views.

10 Comments

I am bummed Steven its moving back to Ireland. It its always awesome to see and talk with him at conferences. I might have to catch sotr or vacation to Europe and see him.

61 Comments

One last thing. While the view/controller should not be aware of the data storage authentication method (imo), it should know the desired output format html/json. It is there that you would pass into the service layer the way you want the data returned. Then the controller would set the desired view/layout to either put html into the web layout or output just raw json.

For creating webservices I normally use a proxy cfc that exists outside the standard mvc framework which calls into the service layer directly bypassing the views and layout function altogether, however it is just as valid to use the mvc structure.

The most important aspect of mvc is that you should be able to (in theory) replace the controller and view structure of one framework with another without ever having to touch any code in the model layer.

5 Comments

Nice post, Ben! There are a lot of confusing MVC descriptions out there, but this does a really good job of summing up the different aspects of (in my opinion) a properly structured MVC application. I'm going to bookmark this so I can reference it whenever I need a simple way to explain MVC to someone. :)

5 Comments

I'm wondering anyone has any thoughts on Mach-II and what the controller actually consists of (just the XML file or the XML file plus listeners/filters ).

The reason I ask this is that i'm increasing seeing traditional programming constructs in the XML file. - eg, Performing Looping and variable assignment/reassignment, conditional/branch execution in the XML "language" - it just feels horribly unnatural.

Thoughts?
Dave

61 Comments

Typo

Now this is only one way of handling it

should read

Now this is not the only one way of handling it

@David
In theory the controller should be extremely light with no contructs such as looping.

It would be okay to have some conditional logic in the controller but only as far as setting up the view. EG In my website on some controllers I have a conditional switch to select a mobile view for users on smartphones.

15,640 Comments

@Andy,

Thanks! I'm exciting to actually be making some progress on this front. Not sure what the directory structures would be (my controller code is still in a folder called "content").

@Ryan,

Good point. I'm looking to try Sean's FW/1. Every one tells me its a great one to start learning with since it's super lightweight.

@Sean,

Thanks my man! Glad this helped! Steven really helped me wrap my head around it. Plus some of the presentations that I've watched recently on Google Tech Talks about testability (especially Misko Hevery) have helped frame everything nicely.

@David,

I haven't personally played with any XML-based configurations (or Model-Glue / Mach-ii). Seems like doing logic in XML would feel unnatural, though.

@Steven,

Since I come (currently) from a CFM-based controller, I have never considered things like the onRequestStart() (or anything in Application.cfc) to be "outside" of the controller. If nothing else, my CFM files are simply extensions of the "core" controller that is the "ColdFusion Application Framework."

I like what you're saying, though, about encapsulating access to the session. That way, at least you can mock out the retrieval of that data.

And, I just like having rules :D I know that rules are sometimes meant to be broken; but, if I can come up with at least a set of guidelines, I can give the hamster in my head a little bit of a break!

As for the View stuff, the way I envisioned it when I was writing was that you would have a different View for HTML vs. JSON. That way the service layer could always return the same data and its up to the controller to figure out which View to pipe that into.

Not to say that I see a Controller actually "switching" views on the fly - more likely, an HTML-based view and a JSON-based view would be invoked from completely different controllers:

HTML:
http://www.somecoolapp.com/index.cfm?event=user.view&id=3

JSON:
http://api.somecoolapp.com/users/3

Two different controllers invoking the *same* Service layer, but *different* views.

61 Comments

@ben

When you get down to controllers/views it really is 6 of 1 half dozen of the other. That's the beauty of having all the business logic tucked away in the service layer.

You can have:
* 2 controllers calling 1 service, 2 views

* 1 controller calling 1 service but switching between 2 views

* or go outside the mvc altogether with an api proxy to the service layer.

They are all valid and virtually the same amount of code.

---
The way I handle exposing api's is by creating a folder of controller proxies named api. Then I use a rewrite rule to map to components methods. This bypass's the vc part of the framework and allow me to visualize what parts of my application are exposed as an api.

For example my rewrite rule would map like this

http://api.somecoolapp.com/users/getuser/3

->

/api/usersProxy.cfc?method=getuser&userid=3

The reason this is an advantage is that normally a mvc framework does extra processing to setup the vc section rendering even if in the end it only outputs raw json, while an api does not need this overhead since it will only ever outputs raw data. The service layer operates completely the same way as it is ignorant of controllers or views.

This really is a question of coding style though. As I said 6 of 1, half dozen of the other.

10 Comments

This is a great summary and an excellent primer for MVC development. I will caution you on using cfc's for the view and instead point you to model-glue as a framework. Not only will using this framework make your projects more maintainable but its maturity will help you along the MVC path!

16 Comments

This is great discussion. Thank you Ben and Steven for elaborating.

I'm with Andy. I can fully get my head around the theory of MVC and Service layers, but a careful examination of an actual (framework-agnostic) implementation of MVC in ColdFusion would be a great help to get me over the hump to fully grok the mechanics of how the pieces all fit together.

4 Comments

@Ryan - Although I agree that there probably isn't a real reason to roll a full blown framework these days I still think it's absolutely important to be able to write a basic one yourself. Not only will it apply to small projects where maybe a full framework is unnecessary but also, even though you're using a framework that doesn't mean your code won't end up in the wrong layer.

I found it extremely beneficial to not only learn the correct place for things but be able to create it from the ground up. Plus, if you can do it yourself then you can explain it to coworkers.

15,640 Comments

@Steven,

Another API vs. web-access question that I keep batting around is the validation of data. With an API, tend to simply CFThrow custom error types the moment I hit an invalid data type. This approach provides only *one* error message per API request since the API doesn't bother checking subsequent data points (think of it like short-circuiting an AND statement.

My web-based approach, on the other hand, sometimes needs to return a collection of error messages (to display to the user). So, I have two controller contexts that need to invoke the same business logic (at a high level); but, have slightly different work flows for validation.

Off the top of my head, I'm thinking I need two methods that reflect this intent:

service.validateData(..) :: Boolean [raises exceptions]
service.inspectData(..) :: Array

... or something like that. One that raises exceptions, and one that validates all data points.

Or, maybe a better approach would probably be to have a save() method which will raise custom exceptions and then have a validate() method which will only return a collection of errors.

I'll do more thinking on this.

As far as the overhead for processing without a View, I am not sure that I understand; but perhaps that's because I don't really have a Framework in place.

15,640 Comments

@Steven E,

Thanks! I'm dying to really understand all this. I think I'll need to write some code before it all starts to sink in.

61 Comments

@Ben

If I'm reading you correctly you do a cfthrow when a user inputs something invalid to your api?

I tend not to use cfthrow for api's as I consider cfthrow to be an internal system error handling mechanism as opposed to an input validation method. Instead when I build an api I try to treat all input parameters as a structure the same way as if I was submitting a form.

Assuming for a moment that you are not directly calling a proxy component method and instead you are going through a controller method for your api and a different method for your web-based operation, I would handle this in two ways.

Firstly lets preface this with the concept that the controller acts as a simple pass through to the serviceLayer when handling undefined data structures. Also lets assume that we only need to worry about url variables for a moment so we dont need to worry about form etc

----IN CONTROLLER-----
<cffunction name="someControllerAPIMethod">
<cfset returnedJSON = someServiceMethod(argumentcollection=url)>
<!---snip--->
</cffunction>

----IN SERVICE----
<cffunction name="someServiceMethod">
<cfargument name="aRequiredVar" type="numeric" required="true">

<!---snip--->
</cffunction>

1: Leverage the onError method in
Given the 2 above methods in the controller and service use Application.cfc to handle invalid inputs to the component. Im cheating here just to illustrate. If a required argument is not provided then output the exception message.

<cffunction name="onError">
<cfargument name="exception">
<cfargument name="event">
<cfoutput>#Arguments.exception.message#</cfoutput>
</cffunction>

#2 Validate inputs in the service layer
If we do not specify any required arguments in the above service method or if the api user has gotten past the entering the correct required parameters required by the api you could then use the inspect/validate data methods you describe to run it through your validation rules.

There are two ways of running this.

--------------------------------------------
Example of using a single service layer call

When I handle form saves what I do is pass in the form to my service method and return a two key structure with the following setup from the service method.

<cffunction name="serviceMethod">
<!--- Validate the form structure --->
<cfset returnStruct.errorArray = {array or validation errors}>
<cfset returnStruct.returnValue = {Some value if no errors}>
<cfreturn returnStruct>
</cffunction>

Then in my view I either output the errorArray formatted nicely as you describe or output the message that the save worked.

<cffunction name="webMethod">
<cfset var resultData = serviceComponent.saveForm(form)>

<cfreturn resultData >
</cffunction>

Then in my view.cfm file I have a very simple output handler.

<cfif arrayLen(resultData.errorArray)>
<h1>Oops something went wrong</h1>
<ul>
<cfloop array="resultData.errorArray" index="error">
<li>#error#</li>
</cfloop>
</ul>
<cfelse>
<h1>It worked</h2>
You saved the form and got back #resultData.returnValue#
</cfif>

Note that I do have some logic in my view file. As a rule the only logic I put in a view is to display something if a specific variable contains a value to output.

The one downside to this is that in order for you to shortcircuit your validation you would need to specify a switch argument in your serviceMethod arguments to determine which validation to run (full/shortcircuit).

------------------------------
Example doing a 2 step call

As an alternative you could make a good case that you could do two calls to the service layer from the controller.

<!---Controller--->
<cffunction name="apiMethod">
<cfset var outputData = "">
<cfset var errorMessage = serviceComponent.validateData(datacollection=url)>

<cfif errorMessage NEQ ">
<cfset outputData = errorMessage>
<cfelse>
<cfset outputData = serviceComponent.doSave(datacollection=url)>
</cfif>

<cfreturn outputData>
</cffunction>

<cffunction name="webMethod">
<cfset var outputData = "">
<cfset var errorArray = serviceComponent.inspectData(datacollection=url)>

<cfif arrayLen(errorArray)>
<cfset outputData = {do something to format the errors nicely}>
<cfelse>
<cfset outputData = serviceComponent.doSave(datacollection=url)>
</cfif>

<cfreturn outputData>
</cffunction>

Then downside to doing it this way is that you are putting some business logic in the controller and that you are passing the same collection of data into the same service component twice.

The upside is that it makes your desired behavour easier to setup your short circuit errors.

15,640 Comments

@Steven,

When dealing with an API, I use a Try/Catch workflow that translates input errors to output responses. As a brief example, I'll have things like this:

<cftry>
	<cfif !valid( form.someValue )>
		<cfthrow type="BadRequest" message="Value required." />
	</cfif>
 
	<cfcatch type="BadRequest">
		<cfset response.statusCode = 400 />
		<cfset response.error = cfcatch.message />
	</cfcatch>
</cftry>

This way, the moment the workflow hits a bad input, it will stop processing the request and proceed immediately to delivering an response.

The theory behind this is that the use of an API is much less user-driven. Meaning that, if the API communication is bad, it's less likely that a friendly error message will even be useful. The assumption being that most of the API communication is dictated by a developer (of the client-side) than by the user.

As far as I'm concerned, any errors that come back from an API are most likely unexpected or based on logic that cannot be determined client-side (ex. uniqueness of a username).

Of course, I'm relatively new to building APIs :D

I understand what you're saying in our explanation. I think at this point, I have to sit down and actually try to write out some code - put some of this in practice! I'm super excited to try!

17 Comments

It was all enigma for me too, even when I started using Model Glue several years ago, it was not clear what Ms, Vs, Cs are. It was so until I started using FW1, then it snapped. Everything became clear.

Nowadays I look at an application like at a desktop program with a single entry point - that is Application.cfc and onRequest() methods if necessary, and a blank wwwroot/index.cfm. From there requests are routed to a corresponding controller which looks up the module to be used in the bean factory. In fact, this allows us to have only an Application.cfc and an index.cfm under the wwwroot. Everything else, controllers, views, of course model can be hidden from direct access in upper level folders.

Models. I have all my modules (models) plugged into the application by a bean factory (I use Sean's DI1, very simple). By saying module I mean either a single CFC, or a folder with a moduleName.cfc and other (transient) entity CFCs.

Model CFC methods work only with arguments passed to them or the properties of that CFC. They never have access to any kind of scope. Never.

This lets me design modules absolutely out of the context of the application, which is very good for development process.

10 Comments

I've used Mach-ii for several years now across multiple applications and am very happy with it. It's more work up front, but maintenance is much easier.

As far as validation goes, I tend to use cfc "beans" to pass, say, form data between the layers. (Mach-ii makes this almost trivial with the event-bean, as the beans get populated automatically by matching up form field names or url parameters with bean properties.) I then add a boolean "validate" method to the bean which can be used to range-check, etc, and adds error messages as necessary to a struct within the bean. If validate is false, get the error struct from the bean and pass it back through the controller to the view.

61 Comments

@Ben
I get what you mean by throwing the error. Actually now that I think about it, it makes a lot of sense. I just never thought about it that way.

My thinking comes from using a home brewed error handler which logs any errors to a database. Since in my mind I never wanted to log bad input to an api I always use a validation script vs doing a throw. However I think you're method has a lot going for it. I'll have to try them both myself and compare :-)

383 Comments

I should probably study this more. My usage of MVC isn't always up to par. I used to work for a company that used it, but I have since then fallen away from using it, and I sometimes get confused about where things should go in a strictly MVC environment.

11 Comments

Ben - great article.

One thing to note, a pet peeve of mine:
bad code=
<cfif errorMessage NEQ ">

good code=
<cfif len(trim(errorMessage))>

383 Comments

@Sami, interesting pet peeve...I was just looking at some of my code like that at work the other day. Just got finished really reading through this and some of the comments. In my limited knowledge of MVC, I don't quite remember the Service layer, etc. The Controller is the part I seem to have the most problem in grasping the concept of. But most of it is a good reminder of the work I used to do when I worked for a company using MVC. Thanks for the great reminder. This is a really good blog posting.

61 Comments

@Anna

The easiest way to imagine the controller is as a delegator.

Take for example a blog article page where there are two sections. #1 The blog article itself and #2 the comments section. These would each have their own service to handle getting their data.

blogArticle.cfc
blogComments.cfc

These would output to a view, which is basically where you put your html layout

blogview.cfm
<code>
<div id="article">
<h1>#article.title#</h1>
#article.content#
</div>
<div id="comments">
<cfloop query="#comments#">
#comments.text#
</cfloop>
</div>

Where the controller comes into play is that the controller is actually the glue between the services and the view.

The controller component blogController will have a method viewArticle which will piece together the different services and send the output of each to the view.

blogController.cfc
<code>
<!--- This is pseudo code --->
<cffunction name="viewArticle">
<cfargument name="blogId">

<cfset article = services.blogArticle.getArticle(arguments.blogId)>
<cfset comments = services.blogComments.getComments(arguments.blogId)>

<cfset setView(viewArticle.cfm)>
</cffunction>

In short in the page request cycle, the controller is called. It pulls the data needed from one or more services and passes to the desired view. It basically acts as the glue between the model(services) and the view(layout).

61 Comments

One last thing. The terms services and model often get used interchangeably in the CF world, however the way I imagine the model is the sum total of the services, gateways and data persistence components (which I just call beans for lack of a better term).

For this reason I normally end up structuring my apps something like this.

controller
views
model
- services
- gateways
- beans (eg shoppingkart, user etc)

Normally though you will just see a services folder in frameworks, for example fw/1. In fact normally the gateway methods will be rolled into the services simply because it is not needed to go any more granular. Services/Gateways are just a further breakdown of the model into distinct areas.

The important thing to remember is that the model is all business logic with no knowledge of output formatting.

Views are purely about output with no knowledge of logic or data storage.

Controllers are the glue that pull data from the model/services, select the relevant view and supply the pulled data to that view.

383 Comments

@Steven,

Thank you so much for your explanation...it is making more sense to me now, definitely. This is one thing I love about the ColdFusion community...how helpful it is and how easy it is to find good help out there when you need it!!!

Ok...question - can you do/use/develop MVC if you have NO control of your environment? What I mean is, if the application is completely set up for you, and you simply write code that is dropped into a part of the page that is already set up for you with headers and footers...and if the root Application.cfc file is not controlled by you, but is stored in the root for you, and you only have control over the folders in which you develop code, could you still do an MVC setup within THAT folder, or do you really need to have control over the entire site in order to do that.

The company I worked for which had the MVC setup had for the controller an XML file, and if I remember correctly, no page was ever accessed except through the main page. Instead variables were passed in....variables were passed to the controller, and then the controller redirected based on the variables. And, on that subject, is that how the controller works? You simply pass the controller a variable, and it uses redirects and redirects the page? Ok, my other question coming from this is....can your controller be a CFM file? And is the controller only one file, whereas you have several view files....one for each view that is needed, and a set number of Model files? Is that correct? From what I remember, that is how our setup was.

Also, from what I remember, functions were STRICTLY forbid and prohibited in our view...they said that was "pure MVC". EVEN functions that had nothing to do with that company specifically, or business logic...for example, just normal functions, including string functions, such as REReplace(), Len(), Round(), etc., were strictly prohibited from being in our view. Now, I understand why one would not want to define a function within the view...in fact, I don't think you can, can you, if it is not a cfc, anyway? And I also can understand why they would want to keep logical functionality out of the view. But it seemed like a hindrance when we couldn't even call basic string functions like REReplace() and Len() in the view...it seemed to make it very hard to program like that. We had code reviews, and I specifically remember getting dinged on something like that, where I had called a common string function within the view, and they sent it back and said I had to fix it. Is this how all MVC's work? How do you do that...how do you use something like REReplace without using it in a view? I'm sorry if I am showing my ignorance, but it is a genuine question, I'm not trying to be a smart alack.

61 Comments

@Anna

#1: I would imagine that the answer is no...IF you want your code to be part of the main application. Now you could develop a standalone app in your sub folder using mvc and its own application.cfc file...BUT it would be totally separate in terms of memory space etc and from what you describe I don't think it would be advisable as it would not be cohesive with the rest of the main site. If you are developing a standalone app then there is no reason it cannot exist in a sub folder of a main website.

#2: Yes a controller can be a .cfm file...but modern practices use cfc's.

Really when you are talking about controllers in cfml mvc there are two controllers. The global controller which is normally the application.cfc file( or framework) and a section specific controller. The global controller is the code than does all the switching and loading of the different sections etc. In fw/1 the section specific controllers are cfc's while other frameworks use xml config files which are read by the global controller. Its hard to explain clearly as the terms can be a bit fuzzy.

In any event the key is that there is one main controller file which is passed in variables based on which it loads executes and delegates operations defined in section specific controllers, services and views.

#3: This is where I take issue with some shops on doing pure mvc. MVC purists sometimes cant see the forest for the trees. In my opinion it is acceptable to allow "some" limited function calls in the view...but only so far as they are specific to html generation.

For example, I would put number rounding function calls in the service layer as that is an outcome that I would want to test when I do unit testing. On the other hand when I am generating a html table and I want to add an odd/even class to the tr, then I would check that condition in the view.

The easiest way to look at it is this.

If it manipulates the data it goes in the model, if it manipulates the html it goes in the view.

Following this rule, it is ok to use len() in the view where you are toggling on a notice based on something existing, but it is not ok to use rereplace().

61 Comments

@Anna

One thing I will say again though is this. These are just my opinions on this. There is no one right way of doing this. Don't kill yourself trying to do it 100% right straight out of the box.

What is important is that when you do start doing mvc in your organization is that everyone is on the same page as to what goes where.

15,640 Comments

@Kirill,

I really like this:

"This lets me design modules absolutely out of the context of the application, which is very good for development process."

This makes me think of what Misko Hevery was saying in a previous blog post - that the more testable something is, the better it is probably architected. And, the more easily tested it is, the more it likely decoupled from other objects.

@Steven,

Going back to your blogArticle/blogComments separation, this is where I am having a big issue with organizing my objects. When I think about who is in "control" over the aspects of the blog, I would think that both the article content and the comments are both the purview of a single part of the application (and hence a single Service object).

As such, my instinct would be to have something like:

blogService.getBlog( blogID )
blogService.getComments( blogID )

... where the blog service would provide access to the underlying and related data. Of course, this is kind of the approach I've taken in the past and the Service objects (which are really my Gateways) are **HUGE** with dozens of queries and thousands of lines of code.

Forget about "reading" data for a second, though; what about deleting data. If I wanted to delete a blog post, it seems that it would make sense that I should have a single, top-level method for deleting:

blogService.deleteBlog( blogID )

... and, to make this easily reusable, it probably makes sense that this request to delete *also* deletes the related comments as well. And this is where I get very stuck.

Would you see something like this:

blogService.deleteBlog( blogID )
----> (Gateway) blogGateway.deleteBlog( blogID )
----> (Gateway) commentGateway.deleteComments( blogID )

... where a single service is making use of multiple gateways?

Or, going back to your two-service concept, would you see something like this:

blogService.deleteBlog( blogID )
----> (Gateway) blogGateway.deleteBlog( blogID )
----> (Service) commentService.deleteForBlog( blogID )

... where the blog service object is making use of its *own* gateway plus the use of the comment service (which has its own gateway).

This is really where my entire understanding of this stuff breaks way down.

61 Comments

@Ben,

Its just as acceptable to have a single blogService with two gateway components, one for comments and one for the articles. It does not have to be a one to one relationship.

I simply have two services because (like yours I suspect) I have a lot of formatting code in my blogService and I ended up with a huge component where one set of methods were totally separate to the other so I was able to cleanly separate it out into two services.

On deleting...

This is one of those question where I answer, it depends.

When it comes to deleting data you have to ask yourself, is there a relational dependency. In this case there is, so personally I actually go directly to the database and setup a cascading trigger. I still have separate delete comment and delete blog methods in my gateways, but the article delete does not need to worry about the comments delete.

Of course if you want to handle that totally at the application level then it really is a case of do you want to transaction the delete?

I actually have'nt tried it so I dont know if you can transaction queries wrapped in separate functions (I dont think you can but I could be wrong). So if you need to transaction the delete I would put a dedicated delete all comments query in the delete article function on the blogGateway.

If though you are not worried about being able to roll back the comments delete when deleting the article I would take the approach of having a delete blog service calling two delete gateway methods.

<cffunction name="deleteBlogServiceMethod">
	<cfargument name="blogId">
 
	<cfset var returnMessage = "Something went wrong deleting the blog">
 
	<cfif commentsGateway.deleteAllComments(blogId=arguments.blogId)>
		<cfif blogGateway.deleteBlog(blogId=arguments.blogId)>
			<cfset returnMessage = "Blgo deleted">
		</cfif>
	<cfelse>
		<cfset returnMessage = "Something went wrong deleting the comments">
	</cfif>
 
	<cfreturn returnMessage >
</cffunction>
61 Comments

@Ben,
If you go with 2 services it is ok to couple them at the service layer. The blogService can maintain a reference to the commentsService and call the deleteComments service method.

I would however not advise you directly calling the comments gateway from the blogService if you go with the 2 service separation.

The reason I say this is because the way I build my gateways is to make them dumb. They simply take some arguments do a single function and return a result. They dont know about business logic or the service layer, so if one service were to call anothers gateway it could potentially bypass critical business logic coded in the bypassed service component.

15,640 Comments

@Steven,

I definitely like the idea of Gateways being as "dumb" as possible. Something about a gateway being able to use another gateway feels like its quickly going to be unmaintainable (much like the way I currently intertwine my "services" which are really Gateways). I think it's important for business logic to be initiated at a higher level and then delegated explicitly to one (or more) lower levels.

As I've been working on some sample code:

https://github.com/bennadel/MVC-Learning

... I think I've made a mistake by thinking about the Gateways first. Really, I think I need to start thinking about the Service layer first and then worry about the actual persistence afterwards.

Probably, I need some more pen-and-paper work before I jump into any more code.

6 Comments

@Ben,

The way I have architected our application, Gateways are "dumb" and contain no business logic at all. They simply perform CRUD operations and return either query objects or true/false depending on the operation (find*,save* or delete*). It is up to the service layer to perform any business logic and data transformation.

We're running our unit tests all the way down to the database, but in this scenario, we could actually create Mock gateways if we just wanted to isolate and test the business logic.

Your comment about enormous CFCs hits close to home. Combine that with 500 line stored procedures and you have our legacy application that we are moving to FW/1 and ColdSpring.

4 Comments

@Ben,

I used to do the same thing with combining my service/gateway objects just so I didn't have to create another file (silly reason, I know). Now that I separate out my gateway and utilize the same base functions in all of my gateways and services I use the Illudium code generator up on riaforge. I re-wrote everything into script based CFC's and now I wouldn't give up that tool for anything. It saves me an incredibly amount of time and guarantees consistency. I still hand write all of my complex relationships but at least the foundation is easy to knock out.

15,640 Comments

@Erik,

I like the Gateways being dumb; furthermore, I would like to avoid (at least initially) putting any cascading / triggering behavior on the database. For one, I don't actually have a database in my Learning MVC project :D So, clearly, that's not going to help. But also (and this is why I went with a no-database option), I don't want to slip into my "old ways" just at a different tier of the application.

The constraints are helping me thinking about division of responsibilities.

Do you have service objects that intercommunicate?

15,640 Comments

@C.J.,

I have heard many good things about Illidium. Hopefully, after I muscle through this example, I'll have a foundational understanding that will make things like that a bit easier to integrate.

4 Comments

Well, just in general to anyone who has thought about using Illidium but didn't want to learn something new know this...

In a CF9 environment, I went from install to full production use in under a day of illidium which includes learning xsl (that I had never used before). That being said, it's worth noting I had been writing all of my objects by hand previous to this and do not use any external frameworks that I haven't written.

Ok, I'm done. Sorry for the advertising, it's just really saved me a lot of time since I started using it.

6 Comments

@Ben,

Yeah, we have service objects that are highly-interdependent, e.g. our UserService:

	<!--- dependencies --->
	<cffunction name="setSiteService" returntype="void" output="false">
		<cfargument name="siteService" type="any" required="true" />
		<cfset variables.siteService = arguments.siteService />
	</cffunction>
	<cffunction name="getSiteService" output="false">
		<cfreturn variables.siteService />
	</cffunction>
 
	<cffunction name="setAccountService" returntype="void" output="false">
		<cfargument name="accountService" type="any" required="true" />
		<cfset variables.accountService = arguments.accountService />
	</cffunction>
	<cffunction name="getAccountService" output="false">
		<cfreturn variables.accountService />
	</cffunction>

Managing these *without* some kind of Dependency Injection framework would not be fun. I use ColdSpring and highly recommend it.

@C.J.,

Illudium is a very cool tool, especially if you have a nice clean DB schema. Unfortunately ours is a mess, so I end up writing most gateway objects by hand, but I often use the generated code as a good reference point.

61 Comments

I agree with Matt about using a dependency injection framework (I love coldspring) when developing production apps because it really becomes painful to manage after you get to having more than a few service components.

To start though its ok to manually do your injection just so you dont have another layer to learn understand.

15,640 Comments

@C.J.,

Ha ha, no problem - I'm never against learning about / sharing cool things !

@Erik,

Ok cool, good to know that this is not a crazy concept. My biggest "emotional" fear ("emotional", meaning that it's not grounded in practice) is accidentally getting myself into some weird circular reference of method calls for coupled services. I can't think of a reason this would happen, though.

@Steven,

Yeah, for this R&D project, I want to do everything by hand. It will slow me down and give me plenty of time to think.

6 Comments

@Ben,

ColdSpring can manage circular references, no problem, see the bottom of this page:
http://www.coldspringframework.org/coldspring/examples/quickstart/index.cfm?page=constructor

It probably is a good idea to take on one thing at a time, but there are a lot of benefits to using a bean factory. I'm pretty sure most frameworks have native support for ColdSpring. The two I am most familiar with (Mach-II and FW/1) have nice shortcuts for auto-wiring services into controllers.

15,640 Comments

@Erik,

Sorry, I didn't mean a circular reference to the objects themselves - I meant a circular chain of method calls. If I have services that call methods in other services, I have an emotional fear that I'll end up doing something like this:

serviceA.doSomething()
---> serviceB.doOtherThing()
-------> serviceA.doSomething() (oooops!)

I can't think of a reason that would happen - but it just makes me nervous :D

61 Comments

@Ben,
That is actually a valid fear. I am currently in the process of refactoring a system that has just that kind of problem (only many times worse). And believe me it hurts.

However its not really a mvc issue, more its a general code design issue.

2 Comments

@Ben,

I had to laugh with your statement "My 'service' layer, on the hand, was actually a Gateway, doing little more than wrapping database access."

That's exactly what I ended up with on my first go-round with model-glue. Was kind of a big project and when I got done and looked around at various blogs on MVC I realized my app I was so proud of was really kind of two-tier with a third group of CFCs that just wrapped cfquery tags. Kind of deflated my enthusiasm. : )

Anyway, please keep posting your experiences! I stuck with modelglue and like it a lot. I saw the other frameworks mentioned above. If you end up reviewing one I'll be watching for your review!

6 Comments

@Ben,

Ouch, yeah, not much can be done about that. As Steven said, that is just an issue with code design.

One of the huge benefits of separation of concerns is testability. If you've designed your components properly, you should be able to write test cases that can give you some peace of mind.

I say this knowing full well that half of my commits this week end with "need to come back and write some tests for this" :(

15,640 Comments

@Steven,

Uggg, that's exactly what I was afraid of. I think I've been going about this completely backwards. I started out with the Gateway - way too deep. Then, I started to think about Services - still too deep. I think I need to go all the way up to the View+Controller layer(s).

I keep trying to think about how systems will be interacting when I don't even know what parts of the system will need to be created to facilitate the UI.

Gonna go back and make a simple UI prototype and clickable interaction model. The Views will render with static data. And, the Controller will simply forward the user from one action to the next (no validation or any data persistence). Hopefully this will help me see the "big picture" of the application.

@Irv,

Ha ha, glad to know that I'm not alone on that one :) This is both *extremely* frustrating and exhilarating! I will be triumphant!

@Erik,

After I get this demo / experiment done, test-driven development is definitely the next thing I'm going to look into. I think they will go hand-in-hand when it comes to adjusting the way I think about and approach application development.

61 Comments

@Ben,
Yeah I think that is a better approach for learning. The way I started to learn mvc was I started purely with views where everything needed to generate the view was in the template file (queries, formatting logic and html).

I then separated out everything like queries, formatting logic and layout selectors and pushed them into the controller leaving just the html and value outputs in the template.

Then I separated out the queries and formatting logic into the service layer component(s).

Only then did I separate out queries into gateways. In fact I still have many service components in my blog that don't have separated out gateways because it was simply not needed.

This is why I love FW/1 because you can take this incremental approach without having to create your controllers and service components to start.

3 Comments

@David Ames

In Mach II we don't allow branching logic in the xml config file. The Mach II config is more like a roadmap for your application that shows which controllers (listeners in Mach II), events, and views get called for each event handler (like an action) in your application. Any login concern app flow needs to be done in a listener or filter method which you write in CFML. You might be getting Mach II confused with Fusebox which does have an XML file that allow branching and other logic. Personally I am not a fan of that approach.

15,640 Comments

@Steven,

That's kind of the approach I used a few years ago for an experiment called OOP(hoto) "ooPhoto". Except, for I didn't really have any idea what I was doing :) What's sad is that when I start Googling now about stuff I don't understand, I keep coming across the OOPhoto pages ... years later and still confused :D

... I feel like I'm actually getting closer now, though!!

@All,

I've tried to do some more thinking / meditation on application architecture. I can't stop thinking about this stuff - it's so much fun!

www.bennadel.com/blog/2384-More-Thinking-About-Model-View-Controller-MVC-And-Application-Architecture.htm

383 Comments

Thanks again, @all, for all of your extremely useful and helpful information, and especially thanks to @Steven for answering my questions specifically. Just thought I would mention that every folder, more or less, in the system (from what I can tell) does have it's own application.cfc file. It's almost as if each section we write is encapsulated...and that is probably the wrong word to use here, but I think you get the drift as to what I am saying about it. Anyway, thanks also especially for answering my questions about the controller, because that is probably what I was most unclear about, but I have a much clearer picture now about that part of it. Thanks a lot! I enjoy learning about this stuff!

61 Comments

@Anna,
Im not sure if I read that right. Are you saying every single folder in your system has its own Application.cfc file?

As in every single folder is its own application?

If that is the case I'm going to borrow a phase from Dave and Scott. 'You're doing it wrong'.

Im sorry if that sounds a bit flip but if your setup really is like that then there is'nt a better way to put it.

Encapsulation is the design methodology of segmenting an application into discreet components that do not need to know the internal workings of each other in order to communicate.

Having per folder application.cfc files is not the way to achieve this.

You should have one and only one application.cfc file for an application which serves as a single point of configuration for that application as a whole.

I have to ask, are you using components in your development?

383 Comments

@Steven,

I may have slightly exagerated that, but just about every folder does have it's own Application.cfc file, and I have no control or authority over that. And when I say just about every folder, what I should say to clarify is...every folder under the root directory. Now, each of those folders has subfolders and such...well, I would guess most of them do. Mine does. And the sub folders don't have their own Application.cfc file. But the main folders under the root directory have their own Application.cfc file. And then, there is another standard file that is named which the Application.cfc file points to, and the reason for this setup is different databases amongst the different folders. I don't think we use mySQL, but Sql Server and Oracle are used, at least, and maybe other ones, too, that I don't know about.

383 Comments

Thanks. To clarify, I wasn't trying to be critical about the company I work for, I was just trying to work out in my mind the application of mvc to the architecture we have here (if possible). I'm sure they have their ways for doing things, and I really just want to work everything in to be the best and most efficient I possibly can without breaking anything. :-)

36 Comments

This is a bit of an old thread, but I was thinking about this the other day for a new project. If I want to be using REST for my new project and say use FW/1 how do I best set this up? Best practice? This is a new area for me obviously. :-)

My thought is to I make it outside of my project and and my model calls go to my REST service. The idea being is that I eat my own dog food. My api and REST service that I would provide to the outside world is outstanding and full featured as I need it for my own site. I think Amazon does much this way. Of course there are authentication considerations, etc for REST exposure that I want in my app, but perhaps not to the public use of the service.

15,640 Comments

@Josh,

I'm currently working with Jamie Krug on an FW/1 application and he's been taking lead on the high-level architecture. In this case, we are using an API "sub-system" in the FW/1 app. This gives us action values like:

api:users.add

... where the precolon stuff is the sub-system. Then, he has each subsystem use its own authorization approach (which may share a common-subsystem).

I don't fully understand it yet; when he is done, I'm really gonna pour over it to see if I can figure out how it all fits together. FW/1 seems really cool, but there's a lot of implicit action taking place and I'm so used to everything be super verbose and explicit :D

@Anna,

Do to work, I've lost a lot of momentum on this side-project; but, I think what I'm going to do is just keep an array of objects cached inside my Gateway. So, if you reset the application, you lose all the data - but it will at least give me something to work with.

We'll see, hopefully I'll have more time to actually get to this soon!

11 Comments

@Ben, @Steven...

Here is a question that I can't seem to find the best answer for.

Where is the best place to put a component full of common functions that can be used for various things, regardless of the controller/service?

17 Comments

@Bret, sounds like an Util.cfc! I am placing such modules inside a bean factory (well, I just let bean factory know about it). And since framework is aware of the bean factory, this component becomes available from any part of the application - views, controllers, or business modules (models). If you do not use a bean factory, then just put them in application scope on application start (provided the components you are talking about are singletons). And here I am talking about FW1 (framework) and its counterpart DI1 (bean factory / dependency injection).

383 Comments

@Ben, oh, I know what you mean about work keeping you busy! I can certainly relate to that!

What my situation was is this: At work, they got to where they expected me to do off site work, but wouldn't give me access to the database. So for most of my applications, I needed to be able to get data back from the "database" to keep my pages from erroring out. So what I did was I developed a dynamic model QueryObject that would give me something to work with for those times that I didn't have access to the database data, and I could just slide this thing in, and keep the code commented out when I had database access, but when I didn't, comment out the database code and use this. I am going to post the code here so that you, or anyone else who might find this useful could use it if you ever got to a place where you wanted data but didn't have access to a database or didn't want to take the time to connect to a database for the project.

And please, feel free to improve upon my code all you want...I love for my code to be improved upon! I used regular cfml in the cfc for the model database, but if you can improve it using cfscript, be my guest! I also used dynamic variables, but if you know a better way to do it, please do. I'm sure my code isn't the best by any means, but it is something I just threw together to make it easier for me, personally, to have a database-like setup without having to actually connect to the database itself.

The cfc that gives you a database:

<cfcomponent hint="A query object for a Model Query" output="false">
<!--- provide a few cfparam defaults to make sure those values at least exist --->
<cfparam name = "variables.queryName" default = "reportData">
<cffunction name = "makeModelQuery">
<cfargument name = "queryName" type = "string" required = "false" default = "queryName">
<cfargument name = "numberOfColumns" type = "numeric" required = "false" default = 1>
<cfargument name = "columnNames" type = "string" required = "false" default = "SampleColumn">
<cfargument name = "columnTypes" type = "string" required = "false" default = "cf_sql_varchar">
<cfargument name = "columnValues1" type = "string" required = "false" default = "black">
<cfargument name = "columnValues2" type = "string" required = "false" default = "lace">
<cfargument name = "columnValues3" type = "string" required = "false" default = "chocolate">
<cfset #variables.queryName# = queryNew("") />

<cfloop from="1" to="#arguments.numberOfColumns#" index="i">
<cfset "variables.col#i#" = ListGetAt(arguments.columnNames, i)>
<cfset "variables.colTypes#i#" = ListGetAt(arguments.columnTypes, i)>
<cfset queryAddColumn(
	#variables.queryName#,
	"#variables["col" & i]#",
	"#variables["colTypes" & i]#",
	listToArray( #arguments["columnValues" & i]#)
	) />
</cfloop>
<cfreturn #variables.queryName#>
</cffunction>
</cfcomponent>

And then, a file to access it and use the query ojbect...this is my test file for that:

<!--- param those variables so we know they exist!:  --->

<cfparam name = "variables.queryName" default = "thisQueryData" />
<cfparam name = "variables.numberOfColumns" default = 4 />
<cfparam name = "variables.columnNames" default = "Flavor,Quantity,color,Material" />
<cfparam name = "variables.columnTypes" default = "cf_sql_varchar,cf_sql_integer,cf_sql_varchar,cf_sql_varchar" />
<cfparam name = "variables.columnValues1" default = "chocolate,vanilla,strawberry,banana,grape,tutti fruitti" />
<cfparam name = "variables.columnValues2" default = "3,7,2,10,14,8" />
<cfparam name = "variables.columnValues3" default = "black,brown,creme,pink,purple,rainbow" />
<cfparam name = "variables.columnValues4" default = "lace,silk,velvet,velour,cotton,velveteen" />
<cfparam name = "variables.pathToFileName" default = "cf.your.path.here.modelDatabaseQuery">
<!--- <cfinclude template = "modelDatabaseQuery.cfm"> --->

<cfinvoke component="#pathToFileName#"
	method="makeModelQuery"
	returnvariable="returnQuery">
	<cfinvokeargument name="queryName" value="#variables.queryName#">
	<cfinvokeargument name="numberOfColumns" value="#variables.numberOfColumns#">
	<cfinvokeargument name="columnNames" value="#variables.columnNames#">
	<cfinvokeargument name="columnTypes" value="#variables.columnTypes#">
	<cfinvokeargument name="columnValues1" value="#variables.columnValues1#">
	<cfinvokeargument name="columnValues2" value="#variables.columnValues2#">
	<cfinvokeargument name="columnValues3" value="#variables.columnValues4#">
	<cfinvokeargument name="columnValues4" value="#variables.columnValues4#">
</cfinvoke>

I made it dynamic so that you could use it to fit just about any data situation you would need. I got a lot of help from your blog, so THANKS!!!!

18 Comments

@Ben - a thought for you. You are up on REST APIs so think of your Service Layer like a REST API. Does your API know where/how/why developers are making requests? Nope. There should be a similarly agnostic split between your Controllers (which are shuttling data to/from the user and the view) and your Service Layer (which is doing the actual business work of your app). If you do this well, the REST API and Controllers can use the same service layer methods. This is what I (try to) do using PowerNap as the API framework.

I am experimenting with building a service layer that behaves like a REST API (every method returns a struct with status, success, message and data attributes) where status is an HTTP code). My hope is to force the development of our service layer to be cleaner and simpler like we want our REST API to be.

15,640 Comments

@Bret,

I agree with @Kirill. To start with, I'd put your random util methods in a single ColdFusion component. I'm not too familiar with frameworks, so I typically just create the CFCs manually and inject them into other components during initialization (ie. the init() method).

Example:

// Create the utility method and cache in Application.
application.utils = new model.Util();
 
// Create other components that depend on util methods:
application.someThing = new model.SomeThing( application.utils );
application.otherThing = new model.OtherThing( application.utils );

If you start to deal with a lot of components, certainly a Dependency Injection framework can simplify your life.

Over time, I try to find ways to extract similar methods into more cohesive components.

@Brian,

I really like the sounds of that. I like to think of RESTful API as being handled by a Controller (set of controllers) the same way that any other request is handled. Especially if the RESTful API needs to return some sort of standardized error response.

I'm in the middle of a deadline that involves a lot of HTTP + API interfaces, so hopefully I'll learn a thing or two about all this architecture stuff.

@Anna,

Seems like some cool stuff. Definitely working locally is something that I have to become familiar with over the past 2 years. But, I have to say that, once I got comfortable with it, it really does make life a lot easier. At work, we've moved to all local development AND we're working with Git / GitHub as a way to share code across a project and developers. Been loving it!

That said, one weird thing is having everyone have their own copy of the database. However, since we don't want to give sensitive information to the developers, we all have the same structure, but everyone is forced to populate with their own data (ie. not actual user data).

This is good, but causes some issues with testing strange edge cases.

1 Comments

Hello Ben,
Thank you for this great breakdown of MVC. I am new to PHP development and even if I am finding it difficult to understand some of your term; I think that was a good one there.

I currently have a task and it involves MVC, OOP with PHP and Smarty. I am really having some headache I must say. Please can you be of help to me Ben because am very close to my deadline for the task or anyone here. I will appreciate. My e-mail is jeff.ict@gmail.com

Kind Regards

2 Comments

Nice article, I found your MVC learning github project which has also been extremely useful. I can't seem to find much on the topic of MVC in ColdFusion other than recommendations on using frameworks. I want to learn about it from end-to-end before choosing a framework so thanks for your examples.

2 Comments

How i see it...

index > controller > model > view *
* arrow is direction of vision.

example:

1. The index retrieves the uri and picks a controller.

2. Controller controls the model.

-----
if ($this->model->validUser($_GET['user']))
{
$this->model->prepareNavMenu();
$this->model->prepareUserpage($_GET['user']);
}
else
{
$this->model->prepareNavMenu();
$this->model->prepare404();
}

$this->model->finish();
-----

3. I use 1 model per controller.

prepareNavMenu and prepare404 functions are based in the main model parent.
prepareUsagepage in the child model.

prepareNavMenu...
- $this->data_for_view['navmenu'] = array('link1', 'link2');

prepare404...
- this->use_view = 'error404';

prepareUserpage($user)...
- this->use_view = 'userpage';
- $this->data_for_view['username'] = /*select $user from database */;

finish...
- new $this->use_view($this->data_for_view);

btw. If you like, the controller can set a type of view in the model.. html, xml, etc.
The model bases his actions on that setting.

4. The view processes the data into the templates, sends out the headers and echo's.

61 Comments

@Jansen,
I don't agree with that terminology. The controller does not control the model. The controller controls the flow of the request cycle, it interacts with the model to request data or send commands to the model depending on the requested action, but the model logic itself is independent.

Likewise views are independent of the model. The controller selects the appropriate view and passes it any relevant data returned from the model which it requires to output.

The controller should never need to know what view is selected just as a view should never know what model was called to get the data. The only thing that should matter is that the data sent/retrieved from the model by the controller should meet the needs of the corresponding view selected by the controller to output said data.

index > controller (Requests data from) > model (Returns data to) > controller (selects and passes data to) > view

2 Comments

@Steven,

Clear comment. I think you are right. My thoughts about it change by the hour.

It makes sense. eg. That way i can use a user-, basecontent- and maincontent-model in my controller.

And the database connection i place in a shared-by-the-models database class.

correct?

61 Comments

@Jansen, Exactly.

In addition by keeping this separation you can make any changes you want to the model logic or storage without having to worry about changing the controllers or views.

1 Comments

A pet peeve: REST is not just for services. You write:

"This view might be HTML for a standard web request; or, it might be something like JSON (JavaScript Object Notation) for a RESTful API request."

There is no reason in the world that the Representation returned by a RESTful site can't be an HTML page. One of the worst things Microsoft has done in recent years is perpetuated the misunderstandings that REST is somehow like SOAP-lite, and that the HTTP operations are analogous to CRUD operations.

OpenRasta was the only .NET REST implementation I've seen which got it right, but sadly the main developer got sidetracked and it withered on the vine.

NancyFX looks promising but their lingo is polluted by MVC and I'm worried they don't really have a fundamental grasp of what REST should be about in terms of interactions between the client and the server.

Real REST is amazingly powerful. I keep hoping someday we can truly develop for the web the way it was designed to be used...

1 Comments

I have to develop a web application like social network but for special kind of users. But I'v develop a sample project for it.

Actual problem of creating project is how to do modulation of project.
I faced huge problem in M of MVC.

What you have written in Model layer is great and quite understandable.
But someone has suggested that where we write our main logic is also come in the category as Java Bean. So where we write our logic for application, we should have to make this category as JavaBean.

I can associate this JavaBean category with the Service Layer of Model layer of your blog.

Based on my acquired knowledge. I'v create this type of project structure of my sample project.
-----------------------------------------------------------
-JavaSource
-actionPack
LoginAction.java
RegistrationAction.java
....
-daoPack
AdminAccountDAO.java
UserAccountDAO.java
UserDataDAO.java
UserDataInfoDAO.java
...
-dtoPack(It is actuall JavaBean by java spec but due to some suggestion I made it like this)
LoginDTO.java
ProfileEditDTO.java
...(In this JavaBeans I'm feeding form data into this)
-hiberPack
AdminAccount.java
HibernateSingleton.java
HibernateTemplate.java
UserAccount.java
...
-javaBeansPack(It's my main logic package, Service Layer of Model Layer)
AccountJB.java
..
-voPack
UserGetDataInfoVO.java
..........
-----------------------------------------------------------------------------
I have already read out about Java Bean what is written in java spec.
But the suggestion came from very highly reliable person, so I can't deny it. so please suggest me about project structure of real time project.

My project is going to be very huge, so I don't want to go with this confusion
If all of them is wrong then leave all of them and provide me a fresh project modulation and diagram.
please send me some project structure diagram docs. If not possible here then send it into my mail id.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel