Exactly what Brian said!
Best Wishes,
Peter
On 1/14/08 4:19 PM, "Brian Kotek" <[EMAIL PROTECTED]> wrote:
> I think he is saying that his controllers only interact with services. The
> easy rule of thumb for me is: if this was called by Flex instead of my HTML
> controller, would it still work? The answer should be "yes". Which means all
> logic of any consequence (beyond doing something like a cflocation, which is
> specific to the HTML view anyway) should be handled in the model.
>
> On Jan 14, 2008 4:08 PM, Baz <[EMAIL PROTECTED]> wrote:
>> Pretty neat tricks you got up your sleeve. A lot of people go by the rule of
>> thumb that controllers should only interface with services and the services
>> in turn play with the objects. But your way does solve a lot of the issues
>> that arise from that.
>>
>> Pleasure picking your brain as usual...
>>
>> Baz
>>
>> On Jan 14, 2008 1:01 PM, Peter Bell <[EMAIL PROTECTED]> wrote:
>>> I use services:
>>> * As remote API can't send smart beans over the wire, so I have methods
>>> like UserService.save(). Of course, all they do is user = THIS.new(),
>>> user.load() and user.save(), so they delegate to the bean, but they provide
>>> the remote API.
>>> * To handle aggregates I find it more intuitive to UserList =
>>> UserService.getByFilter("Subscribed = 1") rather than asking a User for a
>>> list of users. Also, ColdFusion doesn't support class methods, so I'd have
>>> to create a Singleton User which just doesn't seem to be the best approach
>>> in CF.
>>> * As a factory user = UserService.new() returns a user with any
>>> dependencies injected. It's really just a one line call to LightWire which
>>> does the DI, but it gives me a place to add more intelligence to my factory
>>> if I want to override new() for a given business object.
>>>
>>> I don't currently have the use case for different validations, but I've had
>>> it in the past (although back then I just solved it procedurally). I'd be
>>> tempted to EITHER have different isValid() methods, or more likely to have a
>>> OrderState control which would allow me to set the state (and hence
>>> validations and the like) for different states, so I might do:
>>>
>>> Order.set("State", "Situation1");
>>> If (Order.isValid())
>>> . . .
>>>
>>> Although I'd have to play with it to see if it worked out.
>>>
>>> Best Wishes,
>>> Peter
>>>
>>>
>>>
>>> On 1/14/08 3:51 PM, "Baz" <[EMAIL PROTECTED]> wrote:
>>>
>>>> Peter I like what you are saying.
>>>>
>>>> When do you use a service by the way? You seem to be interacting directly
>>>> with beans a lot.
>>>>
>>>> Second, if you had varying levels of validation complexity for orders such
>>>> as:
>>>>
>>>> * Situation 1: Validate Order, User, Billing, Statistics (the previous
>>>> example)
>>>> * Situation 2: Validate Order, Shipping
>>>> * Situation 3: Validation Order
>>>> Would you then have different aggregate validations? I.e.
>>>> Order.isValidSituation1(), Order.isValidSituation2 (),
>>>> Order.isValidSituation3()
>>>>
>>>> Cheers,
>>>> Baz
>>>>
>>>>
>>>> On Jan 14, 2008 12:34 PM, Peter Bell < [EMAIL PROTECTED]
>>>> <mailto:[EMAIL PROTECTED]> > wrote:
>>>>> How about:
>>>>>
>>>>> If (Order.isValid())
>>>>> {
>>>>> Order.save();
>>>>> }
>>>>> Else
>>>>> {
>>>>> Screen = OrderForm;
>>>>> Data = Order;
>>>>> };
>>>>>
>>>>> First method could both return a boolean as to whether the order is valid
>>>>> (which would only be true if all of the composed objects were also valid)
>>>>> and would also set all of the necessary error messages within the Order
>>>>> (and the composed beans) so you could just pass the error (containing both
>>>>> the data and the associated error messages) back to (say) a form for
>>>>> displaying the issues.
>>>>>
>>>>> I think it'd be straightforward to have a generalizable solution to this
>>>>> which would use relationship metadata and introspection to get the order
>>>>> to automatically ask all composed beans and properties whether they were
>>>>> valid. I see something along the lines of:
>>>>>
>>>>> Valid = 1
>>>>> For each property:
>>>>> If NOT THIS.isValid(PropertyName)
>>>>> Valid = 0
>>>>>
>>>>> And the isValid() method would call the appropriate type of validation
>>>>> depending on the data type of the property. If the property was a composed
>>>>> bean, it'd just call that beans general isValid() method. Then you just
>>>>> need a errorMessage(PropertyName) method which would display the error
>>>>> message for each property (it'd be null unless the property was invalid).
>>>>> Details beyond that would depend on relationship between validations and
>>>>> properties and whether you just look for one error per property or catch
>>>>> them all ( i.e. could a single property have multiple error messages at
>>>>> any one time).
>>>>>
>>>>> I don't see any need for either service or controller to do anything here
>>>>> that seems a little procedural to me.
>>>>>
>>>>> Best Wishes,
>>>>> Peter
>>>>>
>>>>>
>>>>> On 1/14/08 3:21 PM, "Baz" <[EMAIL PROTECTED]> wrote:
>>>>>
>>>>>> Hey Peter, so you are doing the equivalent of a separate validate() and
>>>>>> save() right on the bean itself, which, in this example, doesn't make too
>>>>>> big a difference. But some problems can arise if we look at a more
>>>>>> complex yet realistic use-case.
>>>>>>
>>>>>> Let's say a user submits an order and we need to:
>>>>>> * partially update the user (perhaps they changed a communication
>>>>>> preference during the order process)
>>>>>> * create an order
>>>>>> * create a billing address
>>>>>> *
>>>>>> * update an independent statistics table that executive management uses
>>>>>> There are several objects involved here - possible composed, possibly
>>>>>> not, it doesn't really matter - and each would probably have a layer of
>>>>>> validation. One way of performing the save is to have one main function
>>>>>> saveOrder() (located in some service) that goes about creating,
>>>>>> validating and saving each object then aggregating the error messages
>>>>>> into one final ErrorCollection. This provides a nice, clean, simple
>>>>>> interface for a complex process. But then what do you return from that
>>>>>> function - just the ErrorCollection? What about the objects at hand, such
>>>>>> as the newly created billing address, how do we get references to those?
>>>>>> Perhaps we can reload everything on a subsequent broadcast, but using
>>>>>> which OrderID?
>>>>>>
>>>>>> On the flip side we can break apart the jumbo method and start calling
>>>>>> smaller methods independently each of them returning appropriate data
>>>>>> that we need back to the controller. But of course, the more you do that,
>>>>>> the smarter your controller is getting and the less re-usable your code.
>>>>>>
>>>>>> So the tighter and cleaner you make your service functions, the more
>>>>>> awkward or problematic the returntypes. And the more flexible or atomic
>>>>>> you make the service the more intelligence your controller has to hold.
>>>>>> In the aformentioned case it seems that there is no level of real
>>>>>> cleanliness.
>>>>>>
>>>>>> Thoughts?
>>>>>> Baz
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Jan 13, 2008 6:20 PM, Peter Bell <[EMAIL PROTECTED]> wrote:
>>>>>>> Hi Baz,
>>>>>>>
>>>>>>> I can't think of a case where I'd be saving 10 independent objects. In
>>>>>>> my experience, I'm either saving one object, I'm saving a collection of
>>>>>>> objects of the same type (e.g. To update the price on 8 different
>>>>>>> products using a single form or grid) or I'm saving an object which may
>>>>>>> have composed or associated objects that also require saving.
>>>>>>>
>>>>>>> My real code assumes a collection of n-objects of the same type, and a
>>>>>>> single object is just a special case where n=1, so imagine the code I
>>>>>>> sent you within a cfloop and you'll get the idea. For composed objects I
>>>>>>> let my ORM do the lifting. If I really had a use case where I had to
>>>>>>> save multiple completely independent objects (that don't have any
>>>>>>> association or composition relationships) , I'd probably create an
>>>>>>> appropriate service class for handling that and would write a method
>>>>>>> within it that would orchestrate the independent method calls to the
>>>>>>> various object services. To date that use case has never arisen in the
>>>>>>> projects I've built.
>>>>>>>
>>>>>>> As for returning errors, I've been playing with a number of different
>>>>>>> approaches since obviously you want both the bean data and the error
>>>>>>> messages. I'm moving towards something like:
>>>>>>>
>>>>>>> If User.isValid()
>>>>>>> User.save()
>>>>>>> /If
>>>>>>>
>>>>>>> And with a User.getInvalidFieldList() and/or a
>>>>>>> User.isValid(PropertyName) and User.errorMessage(PropertyName). Also, as
>>>>>>> this is an IBO, it can contain a collection of users, so I can loop
>>>>>>> through them and store validation status and error messages for each,
>>>>>>> making this equally easy to work with for one object or for a collection
>>>>>>> of objects as the single is just the special case of n-objects where
>>>>>>> n=1. With this approach, I just pass the bean around and it contains
>>>>>>> both the data the user tried to enter and the associated errors.
>>>>>>>
>>>>>>> Right now I do something a little different each time I touch validation
>>>>>>> so I can get a feel for the different approaches and decide what to
>>>>>>> standardize on.
>>>>>>>
>>>>>>> Best Wishes,
>>>>>>> Peter
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 1/13/08 8:40 PM, "Baz" <[EMAIL PROTECTED]> wrote:
>>>>>>>
If the form submission was a lot more complex and required the saving of,
say, 10 different objects. Would you still interact with each object
individually in the controller, or move all of that to a service function
that orchestrates the interactions? If you would choose the latter, and
given that in the another post you said that you would handle the creation
of objects in the service rather than the controller, you are then faced
with the issue of how to return the bean(s) and/or errorcollection from the
save() method...
You know?
Baz
On Jan 13, 2008 5:15 PM, Baz <[EMAIL PROTECTED]> wrote:
Hey Peter,
Looks very tidy! So I guess the validation occurs in the save() method. What
would that method return, True/False? And then later on you would evaluate
the result to see whether to retrieve the error collection?
Cheers,
Baz
On Jan 13, 2008 5:08 PM, Peter Bell <[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]> > wrote:
Hi Baz,
Why not just:
var Result = ";
var User = UserService.new(User);
User.LoadStruct(form);
Result = User.save();
AddResult(Result);
I have some generic code so I don't have to type this for every controller
that handles a form, but this is generally what I do.
Best Wishes,
Peter
On 1/13/08 8:00 PM, "Baz" <[EMAIL PROTECTED]> wrote:
>>> Another design choice I see go both ways is whether to perform validation in
>>> the save() method or to call each method separately. Example controller
>>> functions could be (pseudo code):
>>>
>>> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>>> - - - - - - -
>>>
>>> SEPARATE SAVE & VALIDATE
>>> var UserService=getBean('UserService');
>>> var FormVariables=getAllValues();
>>>
>>> var ValidationResult='';
>>> var SaveResult='';
>>>
>>> set ValidationResult=UserService.validate(FormVariables); // returns an
>>> error collection object
>>>
>>> if ValidationResult.hasErrors()
>>> setValue('ReturnObject', ValidationResult);
>>> addResult('Fail');
>>> else
>>> set SaveResult=UserService.save (FormVariables); // returns a populated
>>> user bean object
>>> setValue('ReturnObject', SaveResult);
>>> addResult('Success');
>>> end
>>>
>>> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>>> - - - - - - -
>>>
>>> COMBINED SAVE & VALIDATE V1.0
>>> var UserService=getBean('UserService');
>>> var FormVariables=getAllValues();
>>>
>>> var UserBean=UserService.newUser();
>>> var SaveResult='';
>>>
>>> set UserBean.setMemento(FormValues);
>>> set SaveResult=UserService.save(UserBean); // returns true if saved
>>> successfully, or false if validation errors exist (the error collection
>>> would be saved in the bean itself for retrieval later)
>>>
>>> setValue('ReturnObject', UserBean);
>>>
>>> if SaveResult is True
>>> addResult('Success');
>>> else
>>> addResult('Fail');
>>> end
>>>
>>> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>>> - - - - - - -
>>>
>>> COMBINED SAVE & VALIDATE V2.0
>>> var UserService=getBean('UserService');
>>> var FormVariables=getAllValues();
>>>
>>> var SaveResult=UserService.save(FormVariables); // returns a complex result
>>> object with properties: STATUS ('Success', ValidationError') and PAYLOAD
>>> (that contains either a UserBean or ErrorCollection depending on the STATUS)
>>>
>>> setValue('ReturnObject ', SaveResult.getPayload() );
>>>
>>> if SaveResult.getStatus() is 'Success'
>>> addResult('Success');
>>>
>>> else SaveResult.getStatus() is 'Validation'
>>> addResult('Fail');
>>> end
>>>
>>> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>>> - - - - - - -
>>>
>>> CONS OF KEEPING THEM SEPARATE:
>>> * Controller has to be smarter, which is bad, because there will be more
>>> code duplication if you change the presentation layer to flex, for example.
>>> * Function coupling: Coders have to "remember" to always validate() before
>>> saving() - an unnecessary additional complexity (unless, of course, there
>>> are times in your app where you save() by defining all values yourself
>>> (thereby knowing the values are correct) rather than from user input)
>>> CONS OF COMBINING THEM COMBINED
>>> * Requires complex and unnatural result handling (either by affecting the
>>> bean by reference, or using a special RESULT object)
>>>
>>> Cheers,
>>> Baz
>>>
>>>
>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>>
>>
>>
>>
>
>
> >
>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"CFCDev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/cfcdev?hl=en
-~----------~----~----~----~------~----~------~--~---