OOPhoto - Encapsulating Form Processing In The Service / Facade Layer
The latest OOPhoto application can be experienced here.
The latest OOPhoto code can be seen here.
Currently, all of my FORM processing happens in the Controller layer. That is, my Controller is responsible for taking the user-submitted FORM data, stuffing it into an object (that it requested), and persisting that object. I have been criticized for this methodology for several reasons. For starters, it is very data centric; while I am having objects perform work, I am still just shifting around data items - my "Asking" of other objects to perform their duties is kept to a minimum (not a good thing). Additionally, it does, in some way, couple the Controller to the implementation of the data distribution. By that, I mean that when the Controller asks for an object then populates it, the implementation of the data storage (within the given object) must be known by the Controller.
For this reason, Dan Wilson spoke of a Form Processing object. The purpose of this object was to take the FORM data, passed off by the Controller, and handle the interaction with the Service layer. This would address both of the concerns above, but it felt like a lot of overhead. After all, do I want to be creating an object for every single form I have (in addition to the Controller)?
But then, last week, Andrew Duckett mentioned something: why not put the "processing" method directly into the Service layer. Something along these lines:
|
|
|
||
|
|
|||
|
|
|
Here, we are basically giving the Service layer a generic API against which the Controller will be programmed. This API method will then worry about creating the appropriate objects, moving data, validating, and ultimately persisting the data.
While this might seem like a purely horizontal step (moving the processing out of the Controller and into the Service layer), it creates solid implementation hiding. And, what pleases me the most is that by using the API, the FORM data does not have to follow any application-wide naming conventions. Since the Controller has to explicitly pass the FORM data to the API an item at a time using named (or ordered) arguments, it can still carry out any sort of field translation that is necessary, thereby creating low implementation coupling.
While I like this idea, I am not sure that it should go directly in the Service layer. To me, it feels like we are creating a local version of a "RemoteFacade." Is there such a thing as a LocalFacade? Maybe what I need is to create a Facade object for each Service object that has argument-based API methods similar to the one outlined above.
For example, in my Comment vertical, I could have the controller call:
CommentFacade.SaveComment( photo_id, comment )
... which would, internally, call:
CommentService.Load()
Comment.Set()
...
Comment.Validate() --> Return if necessary.
Comment.Save()
I kind of like this approach. In a weird way, it feels both more object oriented AND more procedural. The API method call feels very procedural in that I am basically calling a method and passing unorganized arguments (like an old-school procedural SQL statement); but, on the other hand, I am asking objects to perform work for me, processing data while hiding implementation details.
I also like that I don't have to have a unique object for every Form; I simply have to have a general API into which all of my FORM processing can fit.
Andrew has given me much to think about.
Reader Comments
I've been wrestling with this idea a lot over the last day. Something about it is not agreeing with me fully. I think the problem is that of uniformity. If I use a "Facade" layer to Save an object, shouldn't I also use the Facade layer to do everything else? For example, deleting an object. Right now, my code allows for:
Object.Delete()
But, should this be rerouted through:
Facade.DeleteByID( ID )
... or something along those lines?
When we start to go down that road, I think we slip into the problem that Elliott Sprehn was referring to when he said that many people believe falsely that:
More Abstraction == Better Code
When we go through a Facade layer locally, are we adding abstraction just for the sake of adding abstraction?
What if we take the above method call for delete and rewrite it like this:
Facade.Delete( Object )
... does that change the way we feel about it?
Hmm, but that is exactly what our Service layer does. If we did that, we'd have our Facade layer and our Service layer doing the exact same thing. That can't possibly be the right way to handle it.
Hey Ben,
I've been struggling with the same problem you're describing. I'm thinking about trying to rearrange the relationship between the object and the service. Rather than have Object.save() invoke a service method, what if Object.save() is ONLY responsible for persisting the object.
So if we start in the Controller:
Service.saveObject(
id=form.object_id,
title=form.object_title,
...
)
The service will look something like:
ThisObject = Factory.get("Object").new()
ThisObject.load( arguments )
... before persisting (validate, etc) ...
ThisObject.save()
... after persisting ...
return
Now ThisObject.save() will only be responsible for persisting the data so it might look like:
if( this.getID() eq 0 ) {
getDAO().create( this );
} else {
getDAO().update( this );
}
I'm sorry if this just adds to the confusion. I'm wondering if it maintains a strong API while keeping the code clean and consistent.
Thoughts?
@Andrew,
I think one of the big problems when thinking / talking about this problem is that we are trying to simultaneously address two problems at the same time. This complicates the issue.
To simplify things, let's address each issue on its own. The way I see it, the two problem spaces are:
* API / Why
* Implementation / How
Does that make sense?
I think the Implementation question (How) is secondary. If we can't figure out Why we are doing something, it really doesn't matter how we pull it off. As such, let's totally ignore the "How" problem for the moment.
Instead, let's concentrate on the API and the why of what we are doing this. Why have the Facade / Service layer use an argument-based approach to saving data?
Thoughts: We are doing this because it slightly decouples the Controller form the processing of data. With the Service method, the Controller simply has to pass data off to the Service layer; it doesn't have to worry about what objects to create, how to move that data into those objects, what the validation methods are, and how to persist the object.
The Controller uses a "gesture" rather than an algorithm; it says, "Save this data" rather than trying to figure out how to do that.
This "feels" good for saving, but how far do we extrapolate this feeling? After all, is:
Object.Delete()
... more the "gesture" or the implementation? Would it more "gestural" to be:
Service.Delete( Object )
To me, it feels like the asking the service to do the Delete() is the same as asking it to do the Save(). In both cases, we are asking the Service to "process" some data for a given command.
However, when we start to call methods on objects, do we lose some of that "gesture" and move slowly into an implementation-based understanding of the system?
One of the things that screams "red flag" in an application is a lack of uniformity. That is an instinct of mine, and I trust it. As such, while I loved this idea initially, I am feeling more weary of it lately.
The reason - I like being able to call methods on objects:
Object.Save()
Object.Validate()
Object.Delete()
Object.GetXYZ()
...etc...
In my previous post, I came up with a rule of thumb for deciding which methods go where. I am afraid that using a service-based "Save" method will not cause inconsistency with my current way of doing things.
Not to say that is bad - it might just mean my rule of thumb is totally bunk.
All of this is really to say, let's not even worry about the implementation - heck, black magic might make it all run. Really, the toughest question is the WHY of building things a certain way. That is the problem I am stuck on.
.... total stream of consciousness, sorry.
Ben,
Love the series! Keep up the great work. I've thought about this issue quite a bit too, and one thing you could do is just have a method in your service object that calls the factory to get an instance of the desired object and merge it with SIMPLE user data. Having this single method would prevent you from having to provide different flavors (object argument or simple var arguments) for each of your methods (save, validate, ...). It becomes the single entry point, for constructing and populating an object based on a form submission.
Where it gets sticky is with nested objects. But since this is in your service layer, you could handle building nested objects like you deal with validating nested objects. And depending on how you name your form fields, this could easily be done. See Brian's article: http://www.briankotek.com/blog/index.cfm/2007/9/4/Implicit-Creation-of-Arrays-and-Structures-from-Form-Fields
It's possible that saving an entire object graph could become as easy as the code below if strict form field naming conventions were used:
In your Controller CFC
---------------------------
service.save(
service.populateInstance(
argumentCollection=form
)
)
In your Photo Service CFC
---------------------------
function populateInstance(
ID: int, optional
FileName: string, optional,
FileExt: string, optional,
Comments: array, optional, (array of structs)
.....
) {
if( StructKeyExists(arguments, "ID") ) {
obj = Load(arguments.ID)
structDelete(arguments, "ID")
} else {
obj = New()
}
foreach (key in arguments) {
if(key == 'comments') {
value = new array()
for(i in arguments.comments) {
value.append(commentService.populateInstance(arguments.comments[i])
}
} else {
value = arguments[key]
}
evaluate('obj.set#key#(value)')
}
return obj
}
I agree that you can over abstract and bloat an application.
For façades I'm not sure the issue is abstraction, but more an issue of stability.
Perhaps I shouldn't, but I use façades to provide a stable interface when I'm not sure how I'm going to slice and dice my services. I can code to the façade's interface, but still change my mind on how the services are structured at a later time.
I also use the façade to combine the interfaces from multiple services in to one object. e.g. a online store façade containing functions for login/out, getting item descriptions, totaling orders etc. that call the security, inventory, and ordering services to do the work.
Right now (from what I've had a chance to read) your façade and your service look like a one to one match, so the façade seems redundant. But because you will probably refactor this several times as you learn more about OOP, you may want to create one anyway.
Your thread on this has been an interesting read, you've got a knack for describing your thought process.
Reading over the comments that popped up while I wrote my last one, I think I understand what your after a bit better.
I think I made a comment before that I personally disliked having Object.Save() / .Validate() etc. because you may want to save or validate data from an object in more than one way, so my preference has always been Service.Save(Object) etc.
Now taking that a step further, I like facades because I want to change what service an object is sent to.
A project I previous worked on deals with data blobs being dropped off, and the system has to figure out what they are, then route them to the appropriate service to deal with them.
my Façade.Save(Object), calls a service to identify the datablob, then asks a factory service for the data service that can handle the data blob based on a classification scheme:
classID = identifyService.getClassID(Object)
dataService = dataServiceFactory.getDataService(classID)
result = dataService.Save(Object)
Whoops,
I meant to post
service.save(
service.populateInstance(
argumentCollection=formutils.buildFormCollections(form)
)
)
(of course assuming you have a variable formutils that's an instance of Brian's formutils CFC)
A question for you all:
Does it make more sense to have:
Facade.Delete( Object )
... or,
Facade.Delete( ID )
To pass in an object as the argument means that the calling code needs to have a populated object which means that it probably needed to know how to get or load that populated object (maybe Service.Load( ID )).
That's a lot of understanding that the Controller has to do. However, if we simply pass in the ID of the object to be deleted, then the Facade will take care of all the object population and deletion.
To me, the ID-based method signature is doing a better job of hiding the implementation.
In fact, the way I see it, a Facade can really only hide implementation if it deals with simple input values, no objects (I think part of what Kurt was saying).
But let's take that idea and move up the chain - where do we deal with object exceptions? Meaning, if we ask the facade to Delete( ID ) and the ID is not valid, does the Facade throw an exception? Well, currently, the Service layer would thrown an exception during the Load( ID ) method if the ID was not valid. This exception would / could just bubble up to the Facade.... so that part stays in-tact, which is nice.
And validation, what about that? I assume that Save( args ) methods would just do the validation in the Facade and then pass back a validation object of some sort.
Ok, I am getting a new picture in my head. I think a Facade layer should only accept simple arguments - no objects. If it ever accepts objects, then it's already failed a bit at hiding implementations.
But what about for non-manipulation type methods. What about for something like the Service.GetRecentlyUploadedPhotos() that returns an array of photo objects. Should that also be in the facade? Or can the Controller go directly to the Service layer for that?
Yes, yes, that is the next mental hurdle - where do non-processing methods go? Things like GetRecentlyUploadedPhotos() and GetPhotosWithNoGallery().... I don't think there is a need to put those in the Facade as they are not really processing? Thoughts?
Ben,
In my previous post I was primarily trying to suggest adding a single method in your service for converting simple values into objects so that you didn't have to provide BOTH a facade.save(id,name,someproperty,anotherprperty) and service.save(object) methods.
By having a single method in the service that accepts the "property" style arguments and builds an object out of them, you have just one conversion point to go from data structure land to object land and can eliminate the facade layer. This layer that does the converion of simple values to object graphs is nice though because as you mention it DOES hide the implementation of how to build the objects, you just pass in some basic data structures, and voila, your objects are built.
i.e. In your HTML controller the call to:
Facade.save(ID, name, someproperty, anotherproperty)
becomes
Service.save( service.buildInstance(ID, name, someproperty, anotherproperty) )
Facade.validate(ID, name, someproperty, anotherproperty)
becomes
Service.validate( service.buildInstance(ID, name, someproperty, anotherproperty) )
your current validation implementation would remain untouched. You don't have to worry about hopping between the facade and service layers. There's just ONE boundary where things convert from data structures to objects, then it's 100% objects from that point on.
I created a quick and dirty proof of concept to show what I was trying to get at. It's definitely half-baked, but I think it's pretty cool. Brian Kotek's FormUtils library is doing a lot of the heavy work and is what makes most of the technique demonstrated possible. You can download the code from the URL below, just extract the zip and run index.cfm and submit the form.
http://home.comcast.net/~kurt.bonnet/formToObjectGraph.zip
In regards to Facade.delete(ID), I definitely think that just passing an ID is an efficient route to go. Passing just a single integer argument is much simpler than passing an object. I'm pretty sure I get what you'd like to accomplish with introducing a Facade layer; my only concern is that a facade is something you'd end up having to provide for many of your business objects/services. It could become tedious and a lot of "busy work". Take for instance the scenario of having to maintain many flavors of each method in the facade layer (that accept simple parameters) whenever you add a new property to one of your business objects).
I'm wondering if some handler/filter could be added to the onMissingMethod for the service (much like the "withTransaction" handler) that would automatically convert "property" style argumetns into an object automagically and then call the "object" parameter version of the method with the constructed object. This could give you the best of both worlds - object parameters and simple parameters, and there would be no need for a formal/physical facade layer so you have no facade code to maintain.
If a handler like this were in place, you could have simple ajax calls or web service calls to your application that call stuff like service.save(id, name,...) service.delete(id), service.validate(id, name, ...) and they never have to know how to create an object, they just pass simple parameters.
As for the Service.GetRecentlyUploadedPhotos() method. I personally think that leaving it in the service is just fine. In many applications the service delegates calls to a gateway, which are almost always non-processing methods. The fact is your API is going to have to return data (not just process things) to it's consumers at some point. How would Flex get an array of photos to render in a data grid? It would need to call something the GetRecentlyUploadedPhotos method on some remote service to get an array of photo objects. How else would it get the data to populate it's data grid with? Maybe if you're using LCDS with push capabilities there would be a way for the server to push data to the client instead of the client requesting data. I'm definitely no flexpert, I'm just speculating and am trying to think of a case where the need to query data could be eliminated somehow.
I hope nothing above sounds pushy, I really just want to bring up some design considerations/options. I think it's great that you're developing and evolving OOPhoto out in the open like this.
@Kurt,
This is extremely interesting. First off, holy cow on the proof of concept! Way to really put in some effort. Thanks a lot man.
I like the idea of having an OnMissingMethod() driven interface for this style of programming, but I don't think that's possible. I still want the method to have explicitly named arguments. I guess I could do that even without the CFArgument tags... but even so, I am not sure that OnMissingMethod() would add much functionality since all the wiring still needs to exist.
One thing that I am 100% certain of is that I want the FORM name to Property name mapping to happen as part of the method call:
BuildInstance( Name = FORM.Name, PhotoIDs = FORM.photo_id_list )
I saw in the proof of concept that you had an internal type map in the service layer. My concern with that is that Service and the View now have to agree on naming convention and that is something that my gut tells me is to be avoided.
Now, I the way I am picturing this is that it can be called with a variable number of arguments. So, for example, building a Gallery object could be done with:
BuildInstance( ID = 4, Name = FORM.name, [....] )
... or, simply:
BuildInstance( ID = 4 )
I figure that this latter version could be used for things like the Delete() methods:
Delete( BuildInstance( ID = 4 ) )
Again, I am looking for consistency: if the Save() method takes an object, I'd like Validate() and Delete() to accept an object also (perhaps all built by BuildInstance()).
I see that you are using this methodology even from within the Service layer as well.
So this begs the question - do we even need the Facade? Sure maybe for the Remote calls (a RemoteFacade); but, I don't think we need one in the nature of the one I had (one per service class).
......
How does this conflict with the idea of a service method:
Service.Load( ID ) :: Object
This is really the same thing as:
Service.BuildInstance( ID = 4 )
Well, I guess in actuality, the BuildInstance() method would turn around and call the Load() method on the service. But, are they redundant. I want to say No, since the BuildInstance() is about processing a variable amount of data. The Load() method is also about processing data (takes data and magically returns a populated object), but can only do so in one specific way.
....
Oh wait! Just thought of something. If we look at the page flow of a form entry page, here is what we get:
- Create or Load object
- If first run:
------ Initialize form data (if first run) from object.
- If submission:
------ Move form data into object.
------ Validate object
------ Save object.
Here's the problem: The first thing we do on that page is load a new object or create one (based on URL-ID most likely). If we already have an object for that page request, we have no need to create one just for the purposes of saving the form data.
Unless, of course, you argue that the *redundancy* of creating the object twice is outweighed by the fact that creating it the second time using BuildInstance() hides the implementation of form data processing.
....
And what about Delete. Which is more appropriate:
BuildInstance( ID = 4 ).Delete()
... or:
Load( 4 ).Delete()
If we are on a page where all we need to do is load an object to delete it, which method has the right "intent" of the action at hand.
My brain is starting to hurt. Kurt, I like your idea, but I am having trouble trying to see how it fits within a consistent framework. I am not giving up on it - I just need more time to think.
Ben,
I've been thinking about this some more and after seeing that the controller could essentially make the call:
Service.BuildInstance( ID = 4 ).Delete()
I am thinking that maybe the BuildInstance method should be a private method on the service, or somewhere else in the model. What got me thinking this is that with the approach above, the HTML controller can actually integrate more tightly with the model and access more features of the model than a Flex controller. You simply couldn't make the same function call above in Flex (not verbatim). This is because DATA is transferred between Flex and ColdFusion NOT behavior. So why should the HTML controller get closer access / tighter integration to the model than Flex? This would actually create slightly different ways of performing actions on the model (by letting the controller actually get a reference to an actual model instance and calling instance methods that MANIPULATE things instead of asking the service layer to perform a high-level request).
I'm thinking that any manipulations should go through the service layer so there's just ONE way for all clients of the model to change things, but that all clients should have the ability to read objects. So basically start using access protection on your methods so that all desired GETS are "public", but any manipulations are "package" level. That way both HTML and Flex will use the model / services more consistently.
This way, the only way for either the HTML and Flex controllers do delete something would be to call Service.Delete(ID=4) and then somewhere AFTER that call to the controller, or maybe in the service.delete method itself you could do BuildInstance( ID = arguments.ID ).Delete()
That way, if you were to do a delete on a gallery or other composed object, you can instantiate the BO easily and then call the delete on all the other objects the gallery is composed of. This way your delete logic can all be fully OO still and not direct DB manipulations.
I'm not exactly sure where my BuildInstance proof of concept fits in with all of this, but I think it may have a place somewhere still, just not yet. I could see it helping with converting non-object data structures to object instances for high-level processing operations. Maybe it could help in implementing the "processing operations" you mention.
For instance, lets say we have a billing system that allows the user to enter MULTIPLE payments for an account. The form is laid out as a table with 10 rows of fields, and the columns/fields: description, amount, datePaid (Lets just pretend we don't have AJAX or Flex to let us add or delete rows dynamically) There could be a high-level service method: applyPayments(accountID:Numeric, payments:Array of structs) that loops through all the payments passed in and prunes any "empty" payment elements from the array, validates all the payments that had information entered (amount GT 0, valid date), and applies the payments to an account (prevents duplicate payments - same amount & date). The functionality demonstrated in the BuildInstance proof of concept could be used to convert from data structures to objects so that your HTML doesn't have to worry about CREATING objects to pass in to the service layer, it just passes in simple values, and then the simple data structure to object conversion happens just once so that all your processing logic can be done in OO (i.e. payment.validate, payment.save, payment.isDuplicate).
This ends up making your service layer less OO, at least the parts of the service that are publicly available to controllers. I can see where the facade layer would definitely help with providing an OO service API and a non-OO service API. Ideally you'd have your CONTROLLERS access the service facade, but the various parts of your model could directly call each other's OO service APIs (although the non-facade service could still have methods like delete(ID=4), they don't HAVE to be OO. The trick would be to do this in a way that would allow you to not have to maintain 2 APIs. Maybe with AOP this could be done (use getMetaData to analyze parameter types, if type=object use build instance, ...). Hmm.
I'm kind of chasing my tail and starting to ramble during this massive brain-dump so I'm going to cut off now until I can think about things a little more, but I wanted to share my thoughts thus far with you.
@Kurt,
I think perhaps we are starting to move in the same direction here. I definitely like the idea of a BuildInstance() method. Like you, I am not ready to get rid of it, but at the same time, I am not sure where it should go.
I think, perhaps, the reason it feels so appealing is due to what it represents, which is namely, methods that are powered by simple data rather than object data.
Really, I think what we are seeing here is some sort of desire to split service layer interaction into two different methodologies:
* Object-based methods (passing objects around)
* Data-based methods (passing data round)
While at first I thought maybe this was just an urge to go back to the procedural world where I feel more comfortable, I do think that it has some benefits; as I discussed in my next post, by passing around data for "gesture-based" methods (ie. SaveThis, DeletThat, DoSomething), we hide the implementation of our object creation. Also, as you mention, this allows truly external systems the ability to easily interact with the API without having the ability to pass behaviors back and forth.
Of course, when we get into third-party interaction, we also have to think about whether or not just having a Remote Facade which is data-based would be more useful than having our internal application worry about any data-based methods.
Of course, this still doesn't answer the question of "What does my HTML controller do with form data?"
....
What if we go way out on a limb and just say, OK, we are going to have ALL controllers use the Remote Facade (HTML, FLEX, etc.). What implications would that have?
To be honest, I don't know enough about remote calls or FLEX to know what that would mean. I am not sure what kind of data can be transferred back and forth. I doubt that true objects can go over that bridge (but I think I remember seeing something about AS classes that mirror CFCs to duck-tape that situation).
What would it mean locally, to the HTML Controller?
Again, it comes down in part to what type of data these methods are returning.....
Ugg, my brain is fried today. I'm just gonna stop talking now.