Skip to main content
Ben Nadel at the React NYC Meetup (Oct. 2018) with: Nir Kaufman
Ben Nadel at the React NYC Meetup (Oct. 2018) with: Nir Kaufman ( @nirkaufman )

OOPhoto - No More Validation In The Controller

By on
Tags:

The latest OOPhoto application can be experienced here.

The OOPhoto code for this post can be seen here.

Up until now, I have kept all of my OOPhoto validation in the Controller. In this last phase of the project, I have taken that validation and moved it into the Service controllers. It may look like it is in the primary domain object (the beans), but in reality, the validation on the primary domain objects just turns around and calls the appropriate Service object. Really, being able to call Validate() on the bean itself is simply a short-hand notation for calling Validate() on the service and passing in the bean.

I have thought about this for a couple of days and it makes the most sense to me. The basis of my thinking is that validation has to be, at least in part, in the service layer. This is because the service layer is the only entity that can tie different but related beans together. For example, let's say you have a system where people are selecting usernames for their accounts. The "Account" bean only knows that it has to have a Username property of a given format; it does not know about other beans' username values (it only knows about its own universe). The AccountService, on the other hand, is master of that domain and does know about the required uniqueness of the username property. Therefore, it must do some of the validation for the Account bean data.

Since some of the validation has to be in the Service object, I figured I might as well move all of it there. When it comes to debugging validation, I'd rather do it all in one place than two different places. Let's take a quick look at part of the Comment bean (the most simple of the OOPhoto objects). As you can see, the Validate() method of the Comment bean simply turns around and invokes the Validate() method on its service object:

<cffunction
	name="Validate"
	access="public"
	returntype="struct"
	output="false"
	hint="I validate the properties of this comment and place error messages into the given struct.">

	<!--- Define arguments. --->
	<cfargument
		name="Errors"
		type="struct"
		required="false"
		default="#StructNew()#"
		hint="I am the struct into which erorrs will be placed."
		/>

	<!--- Turn the validation over to the service. --->
	<cfreturn VARIABLES.CommentService.Validate(
		THIS,
		ARGUMENTS.Errors
		) />
</cffunction>

Now, this brings up the concern of the "Anemic Domain Model". An anemic domain model is one in which the primary domain objects (our Beans) are treated as little more than data containers. Remember, in true Object Oriented Programming (OOP), objects are supposed to be "Smart" and idealized versions of themselves. Even so, I don't believe that what we have here can be considered anemic. Yes, the validation is being performed in the Service layer; however, from an API standpoint, it appears that the validation is occuring within the bean itself - from an API standpoint, it appears that the bean is "Smart."

Following along with the example above, let's take a look at the Validate() method of the Comment's service object:

<cffunction
	name="Validate"
	access="public"
	returntype="struct"
	output="false"
	hint="I validate the properties of the given comment and place error messages in given struct.">

	<!--- Define arguments. --->
	<cfargument
		name="Comment"
		type="any"
		required="true"
		hint="I am the comment that is being validated."
		/>

	<cfargument
		name="Errors"
		type="struct"
		required="false"
		default="#StructNew()#"
		hint="I am the struct into which erorrs will be placed."
		/>


	<!--- Check comment. --->
	<cfif NOT IsSimpleValue( ARGUMENTS.Comment.GetComment() )>

		<cfset ARGUMENTS.Errors.Comment = "InvalidValue" />

	<cfelseif NOT Len( ARGUMENTS.Comment.GetComment() )>

		<cfset ARGUMENTS.Errors.Comment = "Required" />

	<cfelseif (Len( ARGUMENTS.Comment.GetComment() ) GT 500)>

		<cfset ARGUMENTS.Errors.Comment = "InvalidLength" />

	</cfif>


	<!--- Check date created. --->
	<cfif NOT IsDate( ARGUMENTS.Comment.GetDateCreated() )>

		<cfset ARGUMENTS.Errors.DateCreated = "InvalidValue" />

	</cfif>


	<!--- Check photo. --->
	<cfif IsSimpleValue( ARGUMENTS.Comment.GetPhoto() )>

		<cfset ARGUMENTS.Errors.Photo = "Required" />

	</cfif>

	<!--- Return errors struct. --->
	<cfreturn ARGUMENTS.Errors />
</cffunction>

As you can see, it is within the Service object that we are now checking the properties of the Bean. In my application, there are really no validation rules that require different beans to relate, but if there were, that logic would go here as well. The only thing that comes close is the "Jump Code" property of the PhotoGallery.cfc; however, since this value is auto-generated by the database and cannot be updated, it does not require any validation.

When it comes to passing around these error messages, I have decided to go with an ultra simple format - storing the errors in a struct. The keys of this error struct are the property names and the values are the error types. So, for example, if I tried to save a PhotoGallery.cfc without any data, my error struct would look like this:

Bean Validation Struct Passed Back By OOPhoto Service Layer

I chose to go with a Struct rather than a more complex object because it seems to have all the functionality that I need. Because a Struct is indexed by name, it is very easy to check the existence of errors with StructKeyExists(). It is also very easy to check to existence of any errors at all with StructCount(). I could imagine creating a CFC to handle this functionality, but I think it would end up doing the same thing, only wrapped in method calls (ex. HasError(), HasErrors(), Size(), etc.).

NOTE: As you can see, I am doing all of my basic validation directly in the Validate() method. Originally, I was going to start off with some sort of Validation service that could handle generic type validation. But, I was quickly getting stuck on that step. So, I decided to skip it altogether and just do in-line validation. I figured this might be something I could easily refactor when a I get a better sense of the validation patters that arise.

Now, because these error Struct only contain the validation issues, we need some way to translate those into user-friendly messages to be displayed on the front-end of the application. Since the Model is not supposed to know about the View, we can't do this translation in the Service layer. Since the View is not supposed to know about the Model (as little as possible), we can't do this in the View. That leaves us with the Controller. It is up to the Controller to translate the "property-based" errors into "field-based" errors that the View will understand.

To explore this, let's take a look at the Comment Controller's "Add Comment" action file:

<!--- Param FORM fields. --->
<cfparam
	name="ARGUMENTS.Data.Form.photo_id"
	type="numeric"
	default="0"
	/>

<cfparam
	name="ARGUMENTS.Data.Form.comment"
	type="string"
	default=""
	/>


<!--- Create a new comment object. --->
<cfset LOCAL.Comment = ARGUMENTS.Data.Cache.Factory
	.Get( "CommentService" )
	.New()
	/>

<!---
	Load the form data into the comment object. Be careful
	about loading the photo - an invalid ID will cause an
	exception to be thrown.
--->
<cfset LOCAL.Comment.SetComment( ARGUMENTS.Data.Form.comment ) />

<!--- Try to load the photo. --->
<cftry>
	<cfset LOCAL.Comment.SetPhoto(
		ARGUMENTS.Data.Cache.Factory
			.Get( "PhotoService" )
			.Load( ARGUMENTS.Data.Form.photo_id )
		) />

	<cfcatch>
		<!--- Photo was not valid, leave empty. --->
	</cfcatch>
</cftry>


<!--- Validate the comment. --->
<cfset LOCAL.ValidationErrors = LOCAL.Comment.Validate() />


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

	<!---
		There were object validation errors. Since this is an
		API call, we need to translate the object validation
		errors into user-friendly errors.
	--->
	<cfif StructKeyExists( LOCAL.ValidationErrors, "Photo" )>

		<cfset ArrayAppend(
			ARGUMENTS.Data.APIResult.Errors,
			"The selected photo could not be found"
			) />

	</cfif>

	<cfif StructKeyExists( LOCAL.ValidationErrors, "Comment" )>

		<cfset ArrayAppend(
			ARGUMENTS.Data.APIResult.Errors,
			"Please enter your comment"
			) />

	</cfif>


	<!--- There were errors. Flag API request at unsuccessful. --->
	<cfset ARGUMENTS.Data.APIResult.Success = false />

<cfelse>

	<!--- Save comment. --->
	<cfset ARGUMENTS.Data.Cache.Factory
		.Get( "CommentService" )
		.SaveWithTransaction( LOCAL.Comment )
		/>

	<!--- Return the new ID as the data. --->
	<cfset ARGUMENTS.Data.APIResult.Data = LOCAL.Comment.GetID() />

</cfif>

In this process, which is part of an AJAX request, we first create a new Comment object. Then, we attempt to load the FORM-based data into the Comment object. I believe this is where Dan Wilson would say that I am not properly "processing" data, but rather treating my Comment as a data container. While this might be true, you can see that the FORM fields don't line up with the properties of the Comment (the Comment has no "photo_id" property); as such, the Controller needs to translate the user-submitted data into data that the Model can handle.

Once the Comment bean is populated, we then call Validate() on it. This returns a struct with property-based errors. Now, just as with the data going into the Model, the Controller has to translate the data coming out of the Model. The Controller checks for different property validation issues and then adds the appropriate validation error messages into the AJAX response.

In this case, the Controller IS defining the error messages for the View. I am not 100% comfortable with this, but since this is an AJAX call, this was the easiest thing to do. I might refactor this. Ideally, what I'd like is the Controller to return a new Struct with View-relevant keys for which the View itself can create relevant error messages. This is, in fact, how it works with the Photo Gallery.

In the following Snippet from the Photo Gallery controller, we can see the Validate() errors are translated by the Controller into field-based errors that the View will understand:

<!--- Validate the gallery. --->
<cfset LOCAL.ValidationErrors = LOCAL.Gallery.Validate() />


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

	<!---
		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.ValidationErrors, "Name" )>

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

	</cfif>

	<cfif StructKeyExists( LOCAL.ValidationErrors, "Description" )>

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

	</cfif>

	<cfif StructKeyExists( LOCAL.ValidationErrors, "Photos" )>

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

	</cfif>

<cfelse>

	<!--- Save gallery. --->
	<cfset ARGUMENTS.Data.Cache.Factory
		.Get( "PhotoGalleryService" )
		.SaveWithTransaction( LOCAL.Gallery )
		/>

	<!--- Redirect to gallery detail. --->
	<cflocation
		url="#ARGUMENTS.Data.Controller#?do=gallery.view&id=#LOCAL.Gallery.GetID()#"
		addtoken="false"
		/>

</cfif>

Notice that while Name and Description line up, the "Photos" error is translated into a "photo_id_list" error. These translated errors are then handled properly by the View:

<!--- Check to see if we have any error messages to display. --->
<cfif StructCount( LOCAL.Errors )>

	<cfoutput>

		<div class="error-messages">

			<h4>
				Please review the following:
			</h4>

			<ul>
				<cfif StructKeyExists( LOCAL.Errors, "name" )>
					<li>
						Please enter a name.
					</li>
				</cfif>

				<cfif StructKeyExists( LOCAL.Errors, "description" )>
					<li>
						Please enter a description.
					</li>
				</cfif>

				<cfif StructKeyExists( LOCAL.Errors, "photo_id_list" )>
					<li>
						Please upload at least one photo.
					</li>
				</cfif>
			<ul>

		</div>

	</cfoutput>

</cfif>

When we do error message translation in this way, the View is coupled to the Controller because there has to be an agreement between the two on what the passed back Errors will mean. But, what we don't have is any coupling between the View and the Model. We could go and completely change the way the Model passed back errors and, so long as the Controller still translated them properly, the View would continue to work.

While I believe that this is what the intent of Model-View-Controller really is, it does have some down sides. For one, it makes the Controller much fatter, which can make it very self conscious in a world where the "thin" Controller is regarded as more attractive. We also have to do what seems like redundant translation. In the above example, only one of the three validation errors did not have the same name. As such, it might feel like we are wasting our time with the translation. But remember, part of what we are trying to do here is encapsulate change. So, while it might seem redundant, it is keeping any potential change in implementation encapsulated behind the Model-Controller relationship such that the View does not need to know about it.

After having completed this, I find myself asking, "Why can't the Controller just define the error messages for the View?" The View is already coupled to the Controller when it comes to field-based error translation. So, why not just skip the intermediary step of error-type translation and just return straight-up, user-friendly error messages from the Controller. After all, this is what I did in the Comment-AJAX call and it worked perfectly. In the end, the error-type translation feels like we are adding overhead with very little, if any, payoff.

One more thing I wanted to touch on is that by moving the Validation to the Service layer, we allow the Service layer to call Validate() before persisting the data. When the validation was all in the Controller, the Service layer just assumed that the beans passed to the Save() method were valid. Now, we don't have to make that assumption. Here is a snippet of the Save() method in the PhotoGalleryService.cfc:

<cffunction
	name="Save"
	access="public"
	returntype="any"
	output="false"
	hint="I take a photo gallery object and persist it (using a database transaction).">

	<!--- Define arguments. --->
	<cfargument
		name="Gallery"
		type="any"
		required="true"
		hint="I am the photo gallery object to be persisted."
		/>

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


	<!---
		Before we save the object, let's run validation one
		more time to make sure that the incoming bean actually
		contains valid data.
	--->
	<cfif StructCount( THIS.Validate( ARGUMENTS.Gallery ) )>

		<!--- Throw exception. --->
		<cfthrow
			type="OOPhoto.InvalidData"
			message="The selected photo gallery contains invalid data."
			detail="The photo gallery with ID #ARGUMENTS.Gallery.GetID()# contains invalid data for persisting."
			/>

	</cfif>

	......CODE........

</cffunction>

As you can see, the Save() method immediately calls Validate() on the bean and throws an exception if the incoming bean is not valid. We never have to rely on the Controller to pass us valid data. We have empowered our Service layer :)

The most complicated part of this whole process was validating composed objects. The PhotoGallery.cfc has an array of Photo.cfc instances. As such, when we go to validate the Photo Gallery, I assume we had to validate the Photos as well. To do this, my PhotoGalleryService.cfc loops over each Photo as part the gallery validation and calls Validate() on each of the composed photos instances. These errors are then added to the Gallery error collection and passed back to the Controller:

<!--- Check photos. --->
<cfif NOT IsArray( ARGUMENTS.Gallery.GetPhotos() )>

	<cfset ARGUMENTS.Errors.Photos = "InvalidValue" />

<cfelseif NOT ArrayLen( ARGUMENTS.Gallery.GetPhotos() )>

	<cfset ARGUMENTS.Errors.Photos = "Required" />

<cfelse>

	<!---
		Since we have photos, we need to validate each one
		of the photos as well. Let's start off with by creating
		a key for it.
	--->
	<cfset ARGUMENTS.Errors.Photos = [] />

	<!--- Now, loop over each photo to validate. --->
	<cfloop
		index="LOCAL.Photo"
		array="#ARGUMENTS.Gallery.GetPhotos()#">

		<!--- Validate photo. --->
		<cfset LOCAL.Errors = LOCAL.Photo.Validate( StructNew() ) />

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

			<!--- Add to the array of errors. --->
			<cfset ArrayAppend(
				ARGUMENTS.Errors.Photos,
				LOCAL.Errors
				) />

		</cfif>

	</cfloop>

	<!---
		Now that we have looped through all the photos, check
		to see if we found any errors. If we did NOT, then,
		let's remove that key.
	--->
	<cfif NOT ArrayLen( ARGUMENTS.Errors.Photos )>

		<!--- Delete key. --->
		<cfset StructDelete( ARGUMENTS.Errors, "Photos" ) />

	</cfif>

</cfif>

To be honest, this was the shakiest part of the entire validation process. I am not sure if I like the way this is being implemented. Any feedback on performing nested validation would be greatly appreciated.

Object Oriented Reality Check

Moving functionality from one layer into another is never easy. Let's take a step back and think about it.

Was This Step Worth Implementing?

Without a doubt Yes. I think that some of the implementation can be a bit reworked, but there is no question at all that the validation for the Model should be done in the Model and NOT in the Controller. If this logic remained in the Controller, then every part of the application that needed to persist data would need to duplicate the validation logic. With the validation logic in the Model, it means that I could have an infinite number of persistence actions in the Controller and never need to duplicate the logic. Yes, I might need to duplicate some of the error handling, but even that is something I believe can be refactored for simplicity in the future.

Is My Application Object Oriented?

By moving the validation out of the Controller and into the Model, it is making our Model more "rich." Also, by allowing the Controller to call a Validate() method directly on the Beans, it appears, at least from an API standpoint, that our beans are "Smart". While this alone does not make our application object oriented, having a "Smart" and "Rich" domain model is definitely an indicator of object-oriented-like programming. I am definitely getting closer, much closer than I was a month ago. But, I am not sure if would really consider this an Object Oriented application just yet. I am concerned about Dan's thoughts regarding "Actions" vs. "Data". Right now, I am afraid that my application is still very much glorified "Data processing" logic and not a representation of action-based thinking.

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

Reader Comments

74 Comments

What would your approach be to multiple types of errors for one field? i.e. missing vs. too short? Additional struct keys?

15,640 Comments

@Justin,

If you take a look at the Comment validation snippet at the top, you will see there are different values that can be returned:

- Required
- InvalidValue
- InvalidLength

I didn't want to get too granular with the errors because, frankly, my user-friendly error messages are never that specific. When you think about it, most of the data on the screen is really about "required". The only time a real "length" issue would come into play is if the user has a lot of special characters in the field OR if they hacked the "maxlength" attribute.

These are not going to happen too often; and, if someone hacks the form, I don't care about being friendly, I simply don't want the data to persist and throw an error.

Really, the hard part would be to come up with all the proper translations to the user-friendly messages; that's where the effort is. And, if you take a look at the procedural version of the code, you will see that I don't do that there either. So, I am not losing information, I am simply not adding any.

74 Comments

@Ben,

I meant the various error message translation for the error type. I saw the different error validations. I was just curious what you :would: do, not that you should do it. :)

15,640 Comments

@Justin,

Yeah, good question. Honestly, my error handling has never been quite that robust in *any* application :) Of course I will handle the big ones like:

"Please enter a username"

vs.

"That username is not available."

... but as far as length-validation and whatever minutia I might come across, I rarely every create special error messages for it.

If anyone has a slick way of doing that, I would be all ears.

12 Comments

Not that I've ever actually done it this way :-) but my ideal approach (if I'm understanding the question correctly) would be to do the validation in the database, using a stored procedure to produce an error code (including a code for 'no error'). That code could be looked up in an 'error_message' table and the stored procedure could return it to the program. Again, I haven't actually ever done this (like you, I do only gross error-checking as I assume the users will generally put in reasonable values) - but that's how I've always thought "I really should do it this way..."

Matt

140 Comments

@Ben,

Doesn't the approach you outlined already account for multiple types of errors? If the value of a given key is InvalidValue you can construct a different message in the Controller than if the key is InvalidLength, right? You could simply choose to pass along:

If Error on Comment, then show "Please enter a valid comment."

But you could make the error handler smarter, too, if you wanted, since you already have the details from the Model:

If Error on Password, then
If Error is InvalidLength, then show "Please use an 8-character pwd"
Else If Error is InvalidValue, then show "Please use at least one number in your pwd"
Else If Error is Required, then show "Please enter your password"
/If

Or, frankly, you could pass the Error value along to the View, too, and let the View decide how (or if) to describe the details.

Seems like the pieces are there, and now it's up to the View implementation as to how / if to utilize what's coming back from the Controller, which is just what you want. And meanwhile you've already assured that at least the Model won't persist anything it shouldn't.

2 Comments

Nice work again Ben. Keep it up.

I'm personally hoping for a nice PDF of this entire series with comments included, to be my new most favored reference/guide. :-)

15,640 Comments

@Matt,

Interesting approach. As someone who was late to the game when it comes to database, I tend to shy away from putting actually business logic in my database. I think it creates a higher learning curve, too, for anyone else who might jump on a project.

That being said, I know that people who leverage the power a database absolutely swear by it, so there's something to it.

@Jason,

Agreed, the pieces are in place. It's just a matter of taking the time to put all the error message evaluation in the View. As Matt was saying above, people generally put in correct values; as such, it becomes a question of effort vs. payoff for the various minutia of errors.

Not to say I shouldn't do it... just to say, I am not sure if I will :)

@Andy,

I think that would be a pretty badass PDF :) Let's see what happens when this series has been completed. Thanks a lot for the support.

12 Comments

One drawback I can see to using stored proc validation is that you have to make absolutely sure that you'll be able to port the procs to a new DBMS if that's necessary. It does however give you a great deal of flexibility as far as adding new error types, changing messages, etc.

Can you tell I was a DBA in a previous life? :-) That's been my biggest challenge in approaching OOP - it's really hard for me to disentangle myself from a relational view of objects. (And don't even start with ORM, that's a whole 'nother mess. One thing at a time.)

Matt

116 Comments

Ben, is there a reason why you want to perform the validation in the Service, rather than creating a dedicated validation object or set of validation objects, compose them into the business objects, and let the business objects handle their own validation?

15,640 Comments

@Brian,

From the outside world, I am doing validation inside of the business objects. It is only internally that the business object is turning around and calling the Service object for validation. So, really , now that I think about it, the service object is sort of like the "dedicated" validation object that you are referring to. Granted it is not fully dedicated as it does a number of other things, but I think the ideas are similar?

55 Comments

Hi Ben, I get that certain validations may depend on calls to the service class, but I'm with Brian. Going back to the Single Responsibility Principle, I'd look at having a validation object composed within the bean. It will indeed need to have access to the service class for any validations across the universe of functions, but as your service classes do more and you have more different validation methods I think you're going to end up with a lot of methods within your service classes.

That said, the whole purpose of the experiment is to try things and play with different approaches, so keep on keeping on and see whether you run into any issues as the app gets more complex.

20 Comments

Ben,

I reckon you were on to something when you were talking about layers of validation. Some discussions about which object is responsible for validation run into trouble because we try to find one correct place, and/or feel bad if we end up with validation in more than one place. "Validation" is a single concept, so we should have a single coherent object to handle it, right? Wrong.

I don't have the definitive list of layers, but here's a start.

1. Absolute basic invariants. Your data simply can't make any sense if these are violated. Maybe your inserts and updates will just fail. This stuff should be defined at the database level where possible, *and* in the domain model beans. This has nothing to do with any controller or service, and it is completely legitimate to report validation errors simply by throwing an exception.

2. Uniqueness/consistency constraints. These are probably also defined in the database, but can't be coded into the domain model beans because they typically involve lots of domain objects. Normally I would code these into collection classes that are included in the domain model, as uniqueness is always defined relative to a specific collection.

3. Context-specific constraints. Finally we get to the controller layer. Rather than validating data, we are validating a command. Is it OK to delete this user record? Can I make this user a manager of this organisational unit? You can see this as data validation, but the command paradigm can be more useful. Security rules and differing application contexts can often boil down to making specific commands available or unavailable.

4. Input constraints. Ideally our UI doesn't let us submit invalid data. The truth is, though, that our UI layer can't always know enough to guarantee submitted data can be committed. So UI level validation (and in that I include both browser and server-side form validation) is really just initial data scrubbing.

One of the big headaches is doing all of this DRYly, particularly rules that have to be the same in both #1 and #4. I'm not sure if in general there actually is a way to stay DRY that doesn't breach encapsulation.

The rules in #1 are totally dependent on the actual data representation - e.g. database field lengths and types. The UI layer, which administers #4, has no business knowing any of that stuff. In a simple CRUD application we're quite justified in simply dropping encapsulation and tightly binding the UI into the data model, but more generally I think the price of encapsulation is going to be some duplication.

15,640 Comments

@Peter,

While I agree with the principle of "Single Responsibility", I wonder if there is enough meat in the validation to require its own object. I am also curious about you referring to "more different validation methods". To me, it seems that the only validation method I will have is a Validate() method. What other types of validation might you have?

The only think I can think of is an action-based validation. For example, validation for "update" might be different than a validation for "insert". Of course, since we are talking objects and not database columns, I wonder if that would even make a difference?

Also, I assume you would need a validation object for each business object? So if I have a Photo object I would have a PhotoValidation object as well? Or, you guys (you and Brian) mean one validation singleton for whole app that has methods like ValidatePhoto(), ValidatePhotoGallery(), etc?

I assume the former, not the latter; with the latter, our Validation object would grow as our application grew and could become large and unwieldy.

@Jaime,

When you refer to a "collection class", is that akin to my "service" classes?

Also, I never even considered "context-specific" validation issues. I agree that those should happen in the Controller, but, at the same time, I assume that they would leverage functionality in the service layer. So, for example, to check if a user can access an item, you might have something like:

<cfif SecurityService.CheckAccessRights( USER, "admin" )>
....
</cfif>

So that it is performing the "check", but not the internal logic?

Good list. And, while I agree that #4 can exist, I don't think that it should really be taken into account for THIS project just yet as all applications can run without it. But, I agree that to do that layer DRYly, it would take some magic, or some principle violation.

20 Comments

@Ben,

>When you refer to a "collection class", is that akin to my "service" >classes?

Yeah, probably. While conceptually a collection can be a first-class domain object, in practical CF the service layer can be a good place for this logic to live. Most people would be managing several collections from within a given service class, but I know Peter Bell has the concept of domain classes each having a paired service class (so he'd have User and UserService, Order and OrderService), and in a lot of ways those things are like collection objects.

>while I agree that #4 can exist, I don't think that it should really be >taken into account for THIS project

Fair enough. Most real-world applications will do some flattening and combining of layers, and some layers are simply empty for certain applications.

116 Comments

It's a common approach to try to handle security in the controller, but I would argue that it is actually a very bad place for it. Consider a Flex application or a web service making calls to this model. The HTML UI controller is not involved. The security enforcement needs to be handled in the model in order for this to be scalable across multiple types of access.

Unfortunately, this usually can get complicated. The best solution I've found involves AOP, since security is a perfect example of a cross-cutting concern, which is exactly the kind of problem AOP solves. However, it definitely adds another level of complexity. Even so, it beats the heck out of scattering security logic all over your service layer.

It may not be something you want to grapple with just now, but be aware that this will probably come up at some point. Yes, the cascade of things to learn never stops, does it? ;-)

20 Comments

@Brian,

Good point. Command objects (if one is using that style) would be invoked by the controller, but the controller should actually get those objects from the service layer (or directly from the model if the service layer is vanishingly thin).

Jaime

132 Comments

@Ben

I believe the issue that Peter is hinting at is that if you have a service that works on multiple object types, then now you need multiple validation methods, one for each type.

Of course provided you created separate services to map against each domain type, in addition to a "complex type" service that's not really an issue.

I don't really see any big issues with providing validation like this in the service in a small app like this. You're not gaining all that much by creating more cfcomponents that do validation, except a false sense of better abstraction. Now if you wanted to do some kind of automated validation based on metadata from the database and other complex logic you might benefit from a dedicated Validation Service just because it keeps your code more brief in the objects. Of course you could just extend some kind of object, or even mixin the methods for validation too.

More classes != better abstraction or better code.

Allowing an object to have knowledge of it's own validation semantics isn't breaking rules either. You could put all this validation down in the object itself and inject the services/components required for it. Some would argue this breaks some kind of abstraction, but there's hardly a rule against it.

Rails for instance has a method validate, validate_on_create and validate_on_update which are automatically invoked on those events. Or you can call valid? to do it yourself. Then every object has an "errors" property that contains the collection of potential errors.

<http://api.rubyonrails.org/classes/ActiveRecord/Validations.html>

Django takes a radically different approach to even that. Objects validate that they're sane, but you don't generally go at them like that. Instead you use a Form object that take the object as a constructor argument and validates it's properties and provides validation errors.

form = CommentForm(request.POST)

if form.is_valid():
new_comment = form.save()
...
else:
...

<http://www.djangoproject.com/documentation/forms/>

Don't get stuck in the "one OO method to rule them all" mindset. The rings just weren't made that way. There's lots of right ways to do OO, figure out what works for you.

132 Comments

@Brian

I agree with Brian that putting security at the controller level presents an issue with remove access through something like Ajax or Flex. Though personally I think trying to do the same authentication model through AOP with a proxy for both is a big mistake too.

For instance twitter uses http authentication for web services, and totally ignores session management in the classical sense. So wrapping up the service layer in the same security model would have been a huge mistake. And also not scale nearly as well.

Google and all the other big services do the same thing now. They use standard login for http, and API keys for remote web service access.

Not every application sharing the same services necessarily uses the same security model either. Not even every context requires the same security. Sometimes we want to require permission X in context B, but permission Y in context A.

Sometimes we also want to totally side step the security model for some kind of inner service system or automation model. AOP and Proxies kind of solve this at a very rudimentary level with not a lot of control or context.

I think using an explicit filter approach works best because it stops you from ever having implicit security checks that your code isn't aware of so you don't get unexpected security exceptions.

The security is VERY explicit in your controller so you MUST handle it explicitly. If you forget, it's plainly visible because the check just ain't there.

I do this with named tiered permissions based on Permission objects that are mapped to them. I use URI notation for permission mapping names.

<cfif permissionService.isGrantedFor("blog.admin/post/delete")>
....
<cfelse>
...
</cfif>

We can do a lot of this in a request Filter, but a lot of it might also be very context sensitive. Like calling some kind of very specific method method. So it ends up in the controller and web services. Usually with different permission names...

isGrantedFor("blog.webservice/post/delete")

Maybe this permission requires an API key while the blog.admin one requires session based authentication.

Anyway, that's another way to do it. Best way is to try and learn and start simple to scale up. :)

15,640 Comments

@Brian,

I agree that security should be put in the Service layer, not in the Controller (for all the reason you mention). However, I wonder about implementing this. As my applications are all procedural, I have never had to deal with this.

One thing I can think of is that you ask the Model to do something and if the current user does not have access, it must throw an exception. Throwing an exception seems like the only way for the Controller to be able to function without knowing about security.

It could be something like:

<cfthrow type="Security.AccessDenied" .... />

Now, at some point, the Controller *does* need to know about the security concerns, because, for instance, when you get an accessed denied page, it needs to know to throw the right Status Code (401 - Access Denied) and potentially present a login page.

But, after writing that, it makes me think that really, you can just think of the exception as a new "Event" that the controller is responding to.

15,640 Comments

@Elliott,

I like the points you make about the security stuff, very insightful.

That Django approach is interesting. Not sure how I feel about it. Not sure how I feel about FORM objects just being passed around. It makes me nervous that too many parts of the system need to know what the FORM will actually contain.

As far as the active records approach, in a way, I think have parts of that in place. You can call Validate() on the object itself. The implementation of this can be changed behind the scenes at any time.

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