Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Andrew Mercer
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Andrew Mercer

OOPhoto: Facade Layer Integrated Between Controller And Service Layers

By
Published in Comments (10)

The latest OOPhoto application can be experienced here.

The OOPhoto code for this post can be seen here.

This morning, I took my simple-value-based Facade layer ideas from yesterday and integrated them between the Controller and Service layers of OOPhoto, my latest attempt at learning object oriented programming in ColdFusion. To reiterate the point of this move, I am trying to hide the processing of data from the Controller. In my previous version of the application, the Controller needed to create the business objects, move data into them, validate them, and save them. This slew of efforts requires the Controller layer not just to know about the form data but to also know everything about how it is processed.

To make the application more scalable and maintainable, we don't want the Controller to think about processing data; the Controller should just be handing the data off to the Model for processing. This way, the Controller will be shielded from any changes that need to be made during processing.

That being said, I started with a base facade class, BaseFacade.cfc. As with all related groups in my Model, I like having a place where I can create a common set of functionality:

<cfcomponent
	output="false"
	hint="I provide the based functionality for all Facade objects.">


	<cffunction
		name="InjectDependency"
		access="package"
		returntype="any"
		output="false"
		hint="I inject dependency objecst into the VARIABLES scope of the extending Facade object.">

		<!--- Define arguments. --->
		<cfargument
			name="Property"
			type="string"
			required="true"
			hint="The key at which the dependency will be stored."
			/>

		<cfargument
			name="Dependency"
			type="any"
			required="true"
			hint="The dependency object that is being injected into the extending facade object."
			/>

		<!--- Inject the dependency object. --->
		<cfset VARIABLES[ ARGUMENTS.Property ] = ARGUMENTS.Dependency />

		<!--- Return this object for method chaining. --->
		<cfreturn THIS />
	</cffunction>

</cfcomponent>

As with my other base objects, the BaseFacade.cfc contains a method for dependency injection. The various Facade objects are going to need to call a number of Service objects and this method allows our ObjectFactory.cfc a way to easily inject the appropriate service objects.

Once that was in place, I started with the most complicated Facade object, the PhotoGalleryFacade.cfc:

<cfcomponent
	extends="BaseFacade"
	output="false"
	hint="I provide value-based methods for Photo Gallery functionality.">


	<!---
		The following items will be injected:

		VARIABLES.PhotoGalleryService
		VARIABLES.PhotoService
	--->


	<cffunction
		name="Init"
		access="public"
		returntype="any"
		output="false"
		hint="I return an initialized object.">

		<!--- Return This reference. --->
		<cfreturn THIS />
	</cffunction>


	<cffunction
		name="DeletePhotoGallery"
		access="public"
		returntype="any"
		output="false"
		hint="I delete the photo gallery with the given ID. If the ID is invalid, I throw an exception.">

		<!--- Define arguments. --->
		<cfargument
			name="ID"
			type="numeric"
			required="true"
			hint="I am the ID of the photo gallery to delete."
			/>

		<!--- Define the local scope. --->
		<cfset var LOCAL = {} />

		<!--- Get the photo gallery. --->
		<cfset LOCAL.PhotoGallery = VARIABLES.PhotoGalleryService
			.Load( ARGUMENTS.ID )
			/>

		<!--- Delete the photo galleru and return. --->
		<cfreturn LOCAL.PhotoGallery.DeleteWithTransaction() />
	</cffunction>


	<cffunction
		name="GetPhotoGalleriesByKeyword"
		access="public"
		returntype="array"
		output="false"
		hint="I return an array of photo galleries that match the given keywords.">

		<!--- Define arguments. --->
		<cfargument
			name="Keywords"
			type="string"
			required="true"
			hint="I am the keywords on which we are searching."
			/>

		<!--- Return keyword search. --->
		<cfreturn VARIABLES.PhotoGalleryService
			.GetGalleriesByKeyword( ARGUMENTS.Keywords )
			/>
	</cffunction>


	<cffunction
		name="GetPhotoGallery"
		access="public"
		returntype="any"
		output="false"
		hint="I load the photo gallery with the given ID. If the ID is invalid, I throw an exception, unless flagged to return an empty gallery object.">

		<!--- Define arguments. --->
		<cfargument
			name="ID"
			type="numeric"
			required="true"
			hint="I am the ID of the photo gallery to load."
			/>

		<cfargument
			name="ReturnNewIfInvalid"
			type="boolean"
			required="false"
			default="false"
			hint="I am a boolean that flags the desire to return a New() photo gallery rather than raising an excpetion if the given ID is invalid."
			/>

		<!--- Define the local scope. --->
		<cfset var LOCAL = {} />

		<!--- Try to load the photo gallery. --->
		<cftry>

			<!--- Get the photo gallery. --->
			<cfset LOCAL.PhotoGallery = VARIABLES.PhotoGalleryService
				.Load( ARGUMENTS.ID )
				/>

			<!--- Catch error if gallery is invalid. --->
			<cfcatch>

				<!---
					The photo gallery was not valid - check to
					see if the user wants to raise an exception
					or return a new gallery.
				--->
				<cfif ARGUMENTS.ReturnNewIfInvalid>

					<!--- Load new gallery. --->
					<cfset LOCAL.PhotoGallery =
						VARIABLES.PhotoGalleryService.New() />

				<cfelse>

					<!--- No override - rethrow exception. --->
					<cfrethrow />

				</cfif>

			</cfcatch>
		</cftry>

		<!--- Return the photo gallery. --->
		<cfreturn LOCAL.PhotoGallery />
	</cffunction>


	<cffunction
		name="GetPhotoGalleryByJumpCode"
		access="public"
		returntype="any"
		output="false"
		hint="I return the photo gallery with the given jump code. If the jump code is invalid, I throw an exception.">

		<!--- Define arguments. --->
		<cfargument
			name="JumpCode"
			type="string"
			required="true"
			hint="I am the jump code on which we are searching."
			/>

		<!--- Return gallery. --->
		<cfreturn VARIABLES.PhotoGalleryService
			.GetGalleryByJumpCode( ARGUMENTS.JumpCode )
			/>
	</cffunction>


	<cffunction
		name="SavePhotoGallery"
		access="public"
		returntype="struct"
		output="false"
		hint="I save a photo galery based on the given properties.">

		<!--- Define arguments. --->
		<cfargument
			name="ID"
			type="numeric"
			required="false"
			default="0"
			hint="I am the ID of the photo."
			/>

		<cfargument
			name="Name"
			type="string"
			required="false"
			default=""
			hint="I am the name of the gallery."
			/>

		<cfargument
			name="Description"
			type="string"
			required="false"
			default=""
			hint="I am the description of the gallery."
			/>

		<cfargument
			name="PhotoIDList"
			type="string"
			required="false"
			default=""
			hint="I am the list of photo IDs associated with this gallery."
			/>

		<!--- Define the local scope. --->
		<cfset var LOCAL = {} />

		<!--- Load the photo gallery (or create a new one). --->
		<cfset LOCAL.PhotoGallery = THIS.GetPhotoGallery(
			ARGUMENTS.ID,
			true
			) />

		<!--- Set the gallery properties. --->
		<cfset LOCAL.PhotoGallery
			.SetName( ARGUMENTS.Name )
			.SetDescription( ARGUMENTS.Description )
			.SetPhotos(
				VARIABLES.PhotoService.GetPhotosByIDList(
					ARGUMENTS.PhotoIDList
					)
				)
			/>

		<!---
			Create the return struct. Because this method needs
			to validate and commit the data, we need a way to
			return errors AND successful data responses.
			Therefore, we need to create a packaging struct.
		--->
		<cfset LOCAL.Return = {
			Success = true,
			Errors = LOCAL.PhotoGallery.Validate(),
			PhotoGallery = LOCAL.PhotoGallery
			} />

		<!--- Check to see if we have any validation errors. --->
		<cfif StructCount( LOCAL.Return.Errors )>

			<!--- Flag no success. --->
			<cfset LOCAL.Return.Success = false />

		<cfelse>

			<!--- There were no errors, so save photo gallery. --->
			<cfset LOCAL.PhotoGallery.SaveWithTransaction() />

		</cfif>

		<!--- Return package. --->
		<cfreturn LOCAL.Return />
	</cffunction>

</cfcomponent>

As you can see, half of these methods simply turn around and forward the request onto the Service object. The other half do a good amount of processing on the simple data that has been passed through. Let's look first at the GetPhotoGallery() method. If you recall, the Load() method on our Service object throws an error if the passed-in ID is not valid. To accommodate this, the Controller had to put a CFTry / CFCatch block around the Load() method and then call the New() method if need be. Now, we have moved all of that processing out of the Controller and into the GetPhotoGallery() method. The GetPhotoGallery() method can still raise an exception, however, we now have an optional argument to return a New() photo gallery if the given ID is invalid.

By moving logic into the Facade layer, the Controller no longer has to know how to handle invalid IDs. All it has to know is that it can request a new photo gallery if the given ID is not valid. While this might seem like a small shift, this is actually a huge difference - we have moved the focus of the Controller from "implementation" to "gesture" while simultaneously protecting the Controller to the processing of these requests.

The persisting method, SavePhotoGallery(), was more complicated than I had originally thought. When I brainstormed it, all I thought about was the actual saving of the data. However, when I went to code it, I realized that I also had to validate the data and possibly return some sort of error collection. This posed a problem - I didn't want to return two different types of data depending on the success of the internal validation. The best solution that I could come up with was to return a structure that always contained several pieces of information:

  • A success flag
  • A collection of errors
  • The new business object (photo gallery)

This way, the format of the data being returned is always the same and can accommodate both successful and unsuccessful form data processing.

Despite some of the kludginess of the return struct, the SavePhotoGallery() Facade method gives us several advantages. By using named arguments, the Facade object provides an intermediary interface between the naming conventions of the View and the naming conventions of the Model. The property names can be completely arbitrary; the Controller simply has to bind in the appropriate data to the appropriate CFArgument.

The SavePhotoGallery() method also encapsulates the way in which simple data is transformed into business objects. By keeping this logic in one place, not only does it lower the coupling between the Controller and the Model, it requires much less code duplication when and if we expand the Front Controller to allow multiple ways to update the same data.

One benefit that I had not even anticipated when I started the Facade layer was that the Controller no longer needs to know about database Transactions. Originally, the Controller had to explicitly call "WithTransaction()" methods on the Model. Now, with the Facade layer acting as liaison to the Service layer, the Controller simply calls "Save" on the Facade and the Facade takes care of any transaction logic that needs to be applied.

The concept of a liaison is very deliberate. The Facade is only meant to be called by the Controller. In turn, the Facade layer is intended to and required to invoke operations on the Service layer only (one Facade object will never call another Facade object). By creating this as a rule, it allows us to easily handle things like transactions where we can be confident that any Facade method is being called from the Controller (and not by another post-Controller processing method).

Once the Facade layer was in place, the Controller became a little bit more simple. The biggest difference would be seen in the processing of something like our photo gallery data:

<!--- Save the gallery. --->
<cfset LOCAL.Results = ARGUMENTS.Data.Cache.Factory
	.Get( "PhotoGalleryFacade" )
	.SavePhotoGallery(
		ID = ARGUMENTS.Data.Attributes.id,
		Name = ARGUMENTS.Data.Form.name,
		Description = ARGUMENTS.Data.Form.description,
		PhotoIDList = ARGUMENTS.Data.Form.photo_id_list
		)
	/>


<!--- Check to see if we have any errors. --->
<cfif NOT LOCAL.Results.Success>

	<!---
		There were object validation errors. Now, we need
		to return the errors to the View in a way that will
		make sense.
	--->
	<cfif StructKeyExists( LOCAL.Results.Errors, "Name" )>

		<cfset LOCAL.Errors.name = LOCAL.Results.Errors.Name />

	</cfif>

	<cfif StructKeyExists( LOCAL.Results.Errors, "Description" )>

		<cfset LOCAL.Errors.description = LOCAL.Results.Errors.Description />

	</cfif>

	<cfif StructKeyExists( LOCAL.Results.Errors, "Photos" )>

		<cfset LOCAL.Errors.photo_id_list = LOCAL.Results.Errors.Photos />

	</cfif>

<cfelse>

	<!--- Redirect to gallery detail. --->
	<cflocation
		url="......id=#LOCAL.Results.PhotoGallery.GetID()#"
		addtoken="false"
		/>

</cfif>

Here, you can see that the Controller is just passing the simple FORM data into the Facade method, SavePhotoGallery(). It's not creating any business objects or moving data around; it's simply asking the Facade object to process the information. This persistence method then returns the success flag and error message collection, which the Controller handles in the same fashion it did before.

Object Oriented Reality Check

When we get into object oriented programming, there is this feeling that the "abstraction" of ideas is always a good thing. But, when you look at the various Controllers in this update, you can see that they have changed only very slightly. Was our Facade a layer of abstraction for abstraction's sake? Or does it have an actual role to play in the lifespan of our application?

Was This Step Worth Implementing?

I think this step was worth it. While, I'm not 100% satisfied with my particular implementation, I believe that a Facade layer will play a large part in the maintainability and scalability of the application. By creating a layer that is powered by simple data values rather than business objects, we shift the focus of the Controller from processing to gesturing; for example, rather than instantiating business objects, moving data into them, validating them, and then saving them, the Controller simply asks the Facade layer to perform the "gesture" of saving. As a side effect of this shift, the actual implementation of these "gestures" is hidden from the Controller. This creates a much lower level of coupling between the Controller and Model, which will, in turn, make the application more scalable and maintainable.

Is My Application Object Oriented?

With the addition of the Facade layer, I am creating a clearer separation between the three verticals of my application - Model, View, and Controller. While MVC is not required for object oriented programming, this increased delineation of layers is due to a higher separation of concerns amongst my components. This separation of concerns is, I hope, accompanied by lower coupling and higher cohesion of my objects.... but, does this make my application any more object oriented than it was before? I think the fact that the Model layer (the Facade in particular) is now processing the data means that we've taken a step in the right direction. We now have business objects that know how to perform actions (ie. they are not anemic) and a Model that knows how to process information. I think we're pretty close to having an object oriented system.

Want to use code from this post? Check out the license.

Reader Comments

140 Comments

Nice change, Ben, this definitely clarifies the communication between your Controller and Model, and it looks to me like it answers some of your persistent concerns over "what part of this handles the FORM??"

One other note on your "Is this app OO now?" challenge is that you could now consume that Facade from a completely different Controller, such as an external app consuming it as a web service. All that other View / Controller would need to do is get the Facade object and then pass in simple vars, just like your Controller does now, except using whatever local variables the external wants:

externalAppPhotoGalleryFacadeObj
.SavePhotoGallery(
ID = galleryID,
Name = galleryName,
Description = galleryDescription,
PhotoIDList = listOfPhotoIDs
)

or something like that :)

15,841 Comments

@Jason,

I really appreciate the positive feedback. I am feeling good about this move, and as you point out, it definitely helps to answer the "form processing" question. I think I am liking it. There are some details that could be made better (not sure how, but they feel wrong), but overall, I am happy.

I would love to start building some sort of remote facade that would call this whole system as a Facade. The biggest question I have about that is how to handle the photo uploads. How does a web service handle uploads? Does it take base64 encode file data? I just don't know anything about that aspect... but as you say, the rest would be easy.

55 Comments

After reading the code you post, can you please explain what's the different between your current OO model, and having all methods of your 'additonal Facade layer' back into your Service layer, mark them Public and mark the old ones Private?

I think having a single Service layer is already sufficient. Too many layers can be overkill.

15,841 Comments

@Henry,

Public and private wouldn't quite do it. I have Service objects that need to be able to refer to other service objects as well as business objects that need to refer to service objects. Right now, I keep all of my CFCs in the same directory, so theoretically, I could set the access on the existing service methods to package. Then, I could move the value-based methods from the Facade to the service with public access.

But, would I want to do that?

After giving this some thought, I would say No. Here's why:

1. Firstly, I don't want my CFCs to get too big. This is not a functional issue, but one of readability and maintainability. I find having two smaller, more purposeful components easier to search and maintain.

2. I would be afraid of having naming conflicts. As you can see from my Facade and my Service layer, many of the methods have the same name. After coding my Facade, I probably would go back and change some of these names in a way that even cause more conflicts. When I first thought out the Facade layer, I contemplated having a single Facade for the app, which is why the names of the methods seem more contextual (ie. SavePhotoGallery()).

If I could go back and update this, I would probably change the above method to be simply:

PhotoGalleryFacade.Save()

... which would call:

PhotoGalleryService.Save()

... (or Save() on the business object). .... but, the point is, these methods are going to have very conflicting names and cannot be in the same object.

If I had them in the same object, I would have to artificially change their names to fit in a one-object schematic; to me, this seems overkill when simply adding another object can take away all of this concern.

55 Comments

@Ben,

Sorry I didn't have a chance to read through your code. However, in the first paragraph of your comment you posted above. You seems to be concerned about dependencies and they are starting to bother you. ServiceA needs ServiceA1 & ServiceA2 etc. This is what ColdSpring excels at, so please check it out: http://www.coldspringframework.org/

Unless you have a strong reason for not using any framework. If you already posted why in the previous post of the series, pls forgive me, I just started reading from this blog entry.

"Business objects that need to refer to service objects" smells weird to me. Why? I'm not sure what's the benefit. My current architecture (which is similar to others using ModelGlue), Service calls BO's methods but BO doesn't call Service's methods. Can you show us an example? or point us to the right CFC in your OOPhoto app?

I think from what I read, you prefer a super lean Controller that calls only 1 single method from the Facade, while I opt for a not-as-lean Controller that call method(s) of the Service layer directly. Naming conflict is not a concern for me as I only have a Service layer.

I am by no means an OO guru. Please forgive me if I make it sound like I know everything. I just find exploring the reasons behind someone building something differently very interesting. :)

15,841 Comments

@Henry,

I am sure that I will look into ColdSpring eventually. However, right now, I am just trying to learn the basics. I think if I start to add frameworks too early, then my understanding as to how/why the frameworks are used is going to be lost. Ideally, I need to "feel the pain" of not using them before I can see why they work.

As far as the Business Object (BO) - Service layer connection, its comes down to an API vs. implementation. From the external world, the business objects API makes one believe that the BO is not anemic - that it can, in fact, do stuff with its own data:

Photo.Validate()

However, from an implementation standpoint, what is going on internally to the BO is that it is calling the appropriate service object and passing itself in (pseudo code):

function Validate(){
. . . . PhotoGalleryService.Validate( THIS );
}

At the end of the day, I am more comfortable having the implementation lie in the service object because I think it needs to know things that the business object itself should not / cannot know. However, I think the BO should know how to invoke all methods that related to its own data (hence the rich API).

Please let me know if I can further explain something; my whole goal here, other than to be learn object oriented programming, is to, beyond any ambiguity, explain why I make the decisions that I do.

55 Comments

Maybe it is time to provide us with some UML diagrams... it is getting hard to understand your architecture with only text explanations. :)

So you want BO.doSomething() and that doSomething() needs to be in the Service layer? I think when you couple BO and Service together, the Layer becomes bi-directional, and things will get ugly.. Like... how many service(s) can BO references?

Btw, are you Services singletons now?

15,841 Comments

@Henry,

I can understand how dropping in at this point can be a bit confusing. This series, OOPhoto, has like over 20 posts and several iterations of code which are all archived and available on line (see links at the top of every post).

I am not sure how bi-directional communication between the business objects and the service layer can get ugly. The business objects aren't really doing a whole lot. They just contain data and do some lazy loading.

As for singletons, yes, all of the Service objects and Facade objects are singletons. The cross-references are built using ultra simple dependency injection in my ObjectFactory.cfc.

I can looking into making some UML diagrams. I don't know much about those.

3 Comments

Enjoying the articles.

I'm currently wrestling OO myself and have spent the last few months realising I have been building an 'anemic domain model' as I had far too much business logic in the services and my BO's were mainly data holders with some dumb getters and setters.

I think asking a service method to validate a BO falls into the ADM category. Although you are at least calling service.validate(this) from the BO, the behavior and data are in different places and surely this is one of the key points OO is meant to address; keep data and behavior together.

On the facade approach ... I usually have several services in my apps and less facades. If a functional area of my app will require several services to get the job done I then would consider a facade so the controller doesn't have to know about all the services. If my controller just needs one or two services I tend not to bother with the facade in front of it.

I think a good explanation of the facade pattern is the one from Head First Design Patterns - I also think sometimes Facade / Service / API mean the same thing but can also mean completely different things to different people too (a bit like the 'factory pattern')

Also, thanks for the injectDependency() method idea. I may 'borrow' that and save some typing with my setXXXXX() injection methods :-)

15,841 Comments

@Alan,

Glad you're enjoying the series and my brotherly struggling with OOP. I'm actually at an OOP class right now down in Florida with Hal Helms and what I'm seeing is that I have been doing a lot of this wrong (I think). Yesterday, I came to Anemic Domain Model Realization as well:

www.bennadel.com/index.cfm?dax=blog:1380.view

I am sure that when I get back to NYC I will have to sit down and totally re-think my entire OOPhoto application. It's been a fun journey. I don't regret any of it, but its funny to realize that several months of thought process were not exactly accurate :)

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