Brad asks the following:
Hi Ray,I've been switching to a more OO approach to CF. One thing I'm having a hard time figuring out is data validation in a CFC.
For example, let's say I have a DAO that handles CRUD functions. Most of the functions in the DAO get data from passing an object. What I need to know is where to do the validation.
Do I validate the data when it is passed to the bean or do I validate the data when it is passed to the DAO? Do I need to validate in both and how?
I've seen quite a few examples of working with beans, DAOs and data gateways, but they were all simple explanations that did not do any type of validation.
Luckily for me, OO-type questions really only have one simple answer that I'm sure we can all agree upon. Oh, and the moon landing was faked and Al Gore invented the internet. I'm sorry to tell you this, Brad, but there are many, many answers to this. I will attempt to answer it, but expect about 500 or so comments to follow saying that I'm either wrong, or close, or not in the ballpark. Of course I'm joking (kinda), but it does illustrate the point that it when it comes to object oriented programming, and ColdFusion in general, there are always more than one answer.
So before I answer, let me add that I do not consider myself an "OO" expert. I'm beginning to grasp some of the concepts. I'm beginning to get into frameworks (Model-Glue). When it comes to building applications with ColdFusion Components, I've tried many things, and I continue to play around with what I consider to be "Best Practice."
So, enough messing around. Lets just answer the darn question. The way I work with CFCs now is based on a method I saw in a Sean Corfield presentation a few months ago. I'm not sure who the original author was, so I'll just say Bob. I've used Bob's method for a while now, and I'm mostly happy with it, but I'm considering making changes in future applications.
In my application, I'll typically have three CFCs per type. By type I mean a type of content. So for example, at the Model-Glue version of CFLib, I have 3 CFCs for the "Resource" type, which represents one resource for the site. My 3 CFCs are a Bean, a DAO (Data Access Object), and a Gateway. My DAO handles the CRUD (Create, Read, Update, and Delete) of single instances. It typically takes and returns Bean instances. My Bean CFC will represent one instance of the object. It will have get and set methods for each of the properties of the type. The Gateway CFC handles everything else, like getting all of the instances, utility functions, and other methods. So where do I validate? In the Bean CFC. What I'll typically do, in an edit form for example, is something along the lines of the following pseudo-code:
resource = application.ResourceDAO.read(0)
resource.setName(form.name);
resource.setGoo(form.goo);
errors = resource.validate();
if(arrayLen(errors)) {
display the errors;
} else {
application.ResourceDAO.create(resource);
}
What I've done above is simple. First I create an empty instance of the Bean. (The whole read(0) thing is something I don't like.) I then set the values. Lastly I call the validate method. This method does whatever makes since. It may just ensure that name and goo have values. It may ensure name has more than 20 characters. The important thing is that the validation logic is not kept in the form, but inside the CFC. The validation routine will check everything it needs to, and return an array of error messages. If the array is empty, then everything was ok with the bean instance.
Make sense? There is no way in heck I can cover everything about OO or Beans/DAOs/Gateway objects in one blog posting. For some very good posts on the subject, and much more in depth (as well as more intelligent), I recommend a visit to Joe Rinehart's blog. Another resource is the CFCDev mailing list.
Archived Comments
This is one of the most commonly asked questions from CF developers looking to shift to a more "OO" style of coding.
And, like everything else in OO, the answer is generally...(as Ray noted) "It depends".
For myself, I have found that I usually have 4 components in the scenario that Ray mentioned above.
I have a DAO for performing crud operations.
I have a "Bean" that is essentially an instance of the data retrieved from the DAO.
I have a Gateway, for dealing with more than one record.
But unlike Ray's example, I also use what I refer to as a "Business Object" (BO).
In most instances, in the "Bean" that is created, I store information about the database field types (int, string etc), the length of the field, whether or not the field can contain a null value as well as the original value of the field.
(Storing the original value allows me to update only those fields that have changed in the Bean - a useful tool in multi user environments with reasonable load.)
When using the getter and setter methods of this bean, I can choose to validate the field or not, and I have a validate() method that can be used to validate the data in it's entirety.
The important thing to note here is that the validation methods validate whether or not the information contained within conforms to the <strong>Database Requirements</strong>.
I then have a BO, which uses composition to "contain" several beans. (IE. An "InvoiceBO" contains instances of both an InvoiceHeader Bean as well as an array of InvoiceItem Beans.)
Calling the getter and setter methods on the BO, will invoke the getter and setter methods on the internal beans. This essentially allows me to contain business logic (including validation routines) in a separate layer.
So my answer to this question would be "What do you want to validate?, that the data conforms to the database rules, or that it conforms to business requirements?"
Like Ray, I'm *starting* to grasp the concepts (so no flames !!), but I have found this approach to be invaluable for our applications.
I've also found the resources that Ray mentioned to be invaluable on my path to exploring OO.
Happy Hunting !!
Ray,
I just want to thank you for taking the time to compose such a detailed response. I know there are many paths I can take with an OO approach. I've already visited the resources you've listed and I think they are invaluable. As I stated in my question to you; I've seen many different examples of methodologies for OO CF, but most gloss over data validation (probably to save space).
I've even studied code generated by automated CFC generators (I know never design your objects around a database structure, unless you absolutely have to) to see what type of validation was generated. What I discovered was that this code was way over my head. So, what I was looking for was pseudo-code just to give me an idea. I've got to say that you hit the nail right on the head. I got an idea of what you do without getting in too deep. You know what they say: "Teach a man to fish..."
I also appreciate the post by Callum and I'd be interested in hearing about other OO validation methods that anyone is willing to post.
Thanks again,
Brad
>> The important thing is that the validation logic is not kept in the form, but inside the CFC
Ray, just to be perfectly clear here, you're talking about *server-side* validation/last line of defence, yes?
nothing to do with usability, user experiance and client-side validation, yes?
Hmm,
A Business Object is just that, an object which has a composition of entities (ie beans in callums case).
BO's are a nice safe name to say "service" or "collection manager".. Ie, If you want to find a semantically correct name you could even opt for DataProvider (if you are using a bunch of other methods like addBean, removeBean etc)
As for beans adhering to Database logic? in truth, i'd refrain from doing this period.
Its an easy mistake to be made or taken for granted, as majority of the time our coldfusion applications are consistently coupled with the one Database Language (MySQL or SQL).
That's fine, provided you can gurantee that existance for life.
If you can't, its best to leave DB specifics until you get to a point where you are data farming for information or storing information away.
Also, do you map your Beans to suite your Database? or do you do it to suite the context of its use?
An example:
InvoiceBO - an Invoice has costcodes associated to it, aswell as "Approvers".. both are an array of other sub-beans?
The InvoiceBO would have two properties, "Approvers" and "Costcodes" even though in the database they are seperate entities unto themselves, in that when you ask information about an invoice, its easy to assume that you'll want to know how many costcodes are in their or who approved it last etc.
Question about Composition: Does a BlogBean have comments or is that seperate if so why :)
Scott,
Does a BlogBean (maybe EntryBean?) need to have comments? :)
I'd probably not have the "EntryBean" know that comments even exist. My Comment *may* have-a EntryBean if I need it - say for e-mail subscriptions, I could say "New Comment Added to #CommentBean.GetEntry().GetTitle()#".
As far as an Entry's page, I'd probably use an Entry instance to show it, and use the results of CommentGateway's ReadByEntryId() method (query) to show the comments.
But that's just my $.02!
barney: Yes, I'm talking just server side validation.
brad: Glad it helped out.
callum, scott, joe - good comments. I was hoping this post would spark such conversations.
Great stuff Ray--the more everyone gets talking about this stuff, the better off we're all going to be as developers.
Joe, interesting comments about the EntryBean vs. CommentBean. I suppose in my mind (and this yet another example of the fact that there's "no one right way" to solve any of this stuff) the way I'd think about it is that since comments don't really exist independently of the entry (in other words, a comment *has* to be related to an entry), they should be part of the entry. So I suppose I'd turn your scenario on its head and use composition, doing something like having an array of comment beans in the entry bean.
Then again, tomorrow I might wake up and decide I don' t like that approach. ;-) That's one of the reasons I love OO--really gets your brain going and thinking very hard about how to design and architect applications. No matter how you solve these conundrums it beats the old way of doing things.
Ah infamous OO-Joe hehehehe
Hoping you'd take a bite or two ;) hehehehe
Actually, in truth their is no right or wrong answer and i asked the question more out of "show me how you'd do it and why"..meaning folks, stop and thought for a change instead of following blindly (ie not saying Joe follows, far from it..just he happened to bite first...joe your supposed to let others comment and then attack ehehe)
Actually what differs a comment from a post? semantics? could a comment be a post? when you think about it, a entry has comments, first being the original posters..
think forums..
Messages belong to a thread, a thread belongs to a forum. Original thread starting message is treated as original post?
Ie now we are getting into what's a container and whats an entity? heheh
In memory / objects, it's convenient to navigate from an entry to its comments (entry has-a list-of comments) but in the database, it's easier to have the entry ID in the comment record and do SELECT ... FROM comments where entryID = ... ORDER BY commentDate ASC to get the query object containing comments for a given entry.
In fact, since you rarely need to interact with a single comment (just when you are creating a new one), you may as well just stick with the query object for comments and your entry object might then contain the query object - in place of list-of comment objects. Generally not a good idea to convert aggregate data into objects when all you're going to do is loop over it and read some fields out of it!
Very good point Sean--I run into that a lot as I help people get into OOP. It's VERY common for people to want to *not* use a query object, which of course if as you point out all you need to do is output simple query-type data, creating an array of objects to do that is adding inefficiency for no reason. All depends on how you're going to use it.
> The way I work with CFCs now is based on a method I saw in a Sean Corfield presentation a few months ago.
Is the presentation available for download?
Thanks.
> errors = resource.validate();
What about having two separate methods validate() (returns true or false) and getErrors() (returns an error object, struct, array of structs or something like that)?
Wouldn't that be a better choice?
Thanks.
wolk2k5: Maybe. I don't know - it just feels right this way. :)
Also - Seans preso was on his blog. I believe if you search for BACFUG presentations, you will find it.
Mostly I give copies of the presentations to the CFUGs and conferences that I speak at and leave it up to them to post the PPT / PDF / FlashPaper files. I change the talks almost every time I give the talk so I don't post it on my site.
You can download the code examples from my site, however:
http://corfield.org/article...
There are seven variants of the same application:
1. traditional (procedural) Fusebox
2. MVC (procedural) Fusebox
3. OO Fusebox using three simple model CFCs
4. Mach II using three simple model CFCs
5. Mach II using refactored CFCs to separate out the business logic, data (bean) and data layers (DAO, gateway)
6. Fusebox + Tartan using refactored CFC model
7. Model-Glue using three simple model CFCs
If you search my blog for ggcc8, you'll find a link to an 8th variant that uses Model-Glue + Tartan using refactored CFC model.
Hope that helps?