Good post Baz.
I've been writing code in my controller:
(psudocode)
if NOT arrayLen(user.validate())
service.saveuser(user) then do a redirect
else pass the result array back to the view
Your suggestion of putting the call to validate() in the service's save()
method makes a lot of sense. I've already decided to refactor the result
array-of-structs into an object. I can see it will make the view code simpler
dealing with errors. I think I'll run with your suggestion of save() calling
validate() too.
Just a thought on one thing you said...
'Another problem is what if your validation is slightly more complex than
pass/fail - for example let's say that in a certain case of saving(), you want
to default one of the invalid values to some other value, rather than error out
the save(). Your options would be to add more controller logic, or make to make
yet another coupled special validation() function that also changes the values
of your BO - awkward'
Is this not what the bean setter methods are for. ie calling setSomething() you
can make it richer by checking for invalid values and setting a default there.
Then this logic isn't needed at the validate() or save() stage?
Also, when needing different validate methods depending on what needs to be
validated I had some success with the State / Strategy pattern. The factory can
set the bean up with the correct concrete validation strategy object (ie my
RegistrationValidationStrategy calls validate() in the abstract
ValidationStrategy first then runs its own validate() method). Its more complex
and probably overkill for most situations but at least the factory handles it
so it keeps things cleaner.
Alan
________________________________________
From: [email protected] [EMAIL PROTECTED] On Behalf Of Baz [EMAIL
PROTECTED]
Sent: 17 July 2008 22:21
To: [email protected]
Subject: [CFCDEV] Re: Service Layer X OO Architecture
Mark and Peter, I use the exact same technique very often in my applications
because I find it very clean and simple, but it has some serious drawbacks.
First, there is a kind of awkward coupling between the validate() and save()
functions. You will never want to save invalid data, so these functions will
almost always be called in sequence. This sequence, or relationship has to be
known, documented and remembered, which will only cause problems down the road.
Consider a slightly more complex example where you had different degrees of
saves(), like savePersonalDataOnly(), and therefore different degrees of
validation(). You will have to either know which validate() functions to match
to which saves() or look into the internal workings of the save() to know which
columns to validate() in your generic validate() method - the first solution
increases coding complexity and is prone to errors, and the second violates
encapsulation. Your team will have to be heavily trained and up to speed on
which functions are related to which and therefore mistakes will happen. Now on
the flip-side, if you use the result object technique, you can provide simple
service functions such as saveUser() and savePersonalData() that perform the
appropriate validation for you and return a nice neat result object with
everything that you need.
Another potential problem is the case of interacting with multiple BO's in one
service function. What if saveUser() also needs to validate or save a second BO
that the UserBO is not composed of? There is no real solution to that except
for more controller logic - which is bad.
Another problem is what if your validation is slightly more complex than
pass/fail - for example let's say that in a certain case of saving(), you want
to default one of the invalid values to some other value, rather than error out
the save(). Your options would be to add more controller logic, or make to make
yet another coupled special validation() function that also changes the values
of your BO - awkward.
Finally let's consider flex. The validation logic that you have in the
controller will need to be duplicated. So rather than simply invoking
saveUser() and getting back a structure that has the validation errors and the
business object, you will have to send two, separate, nearly identical and
potentially heavy requests to your service to first validate() then save().
What happens if your consumer forgets to validate? Basically there is no real
choice but to combine the validate() and save() into one neat function and that
requires a result object. Amazon, google and yahoo work like that.
Just some thoughts,
Baz
On Thu, Jul 17, 2008 at 6:10 AM, Peter Bell <[EMAIL PROTECTED]<mailto:[EMAIL
PROTECTED]>> wrote:
Ahh, makes perfect sense. Sounds like we do things pretty similarly.
On Jul 17, 2008, at 8:58 AM, Mark Mandel wrote:
>
> No, I wouldn't throw an exception for a validation error. That wasn't
> the point I was making.
>
> The point I was making was that it reminded me of days in which error
> handling was done by returning error codes. It was cumbersome, and
> fragile to handle. The better way to handle major errors is through
> exceptions, which is what they were built for, rather than having to
> remember error code 19223.2, you can catch a meaningful exception.
>
> In this sort of instance, if something goes horribly wrong, yes, I
> would throw an exception - that is what it is there fore. I feel like
> returning a result object for something like that goes against common
> development practices, and returns us to error codes.
>
> For validation type erroring, I have no issue with some sort of
> validation error collection, be it an object or a struct being coupled
> to the BO's validation, and the view, I feel funny about that coupling
> being extended to the service as well.
>
> I would ask the BO if it is valid in the controller, because if it is
> valid, is part of the controller logic. If it is valid, it will
> probably go to one view, and if it isn't, it would go to another. I
> feel like this shouldn't be moved to the service to handle.
>
> Brian, you have talked about having 'dumb' service objects. Isn't
> moving this validation logic into your service a move away from that?
>
> Mark
>
> On Thu, Jul 17, 2008 at 5:40 PM, Alan Livie
> <[EMAIL PROTECTED]<mailto:[EMAIL PROTECTED]>> wrote:
>>
>> Mark, I have my validation in BO's too. Say in your User BO in
>> validate() method you find username is less than 6 chars (which it
>> shouldn't be as a business rule dictates).
>>
>> Are you saying you'll throw an exception rather than return the
>> info in some sort of error struct / object so your form can handle
>> it?
>>
>> Alan
>> ________________________________________
>> From: [email protected]<mailto:[email protected]> [EMAIL
>> PROTECTED]<mailto:[email protected]>] On Behalf
>> Of Mark Mandel [EMAIL PROTECTED]<mailto:[EMAIL PROTECTED]>]
>> Sent: 17 July 2008 05:22
>> To: [email protected]<mailto:[email protected]>
>> Subject: [CFCDEV] Re: Service Layer X OO Architecture
>>
>> Honestly, the result object approach feels a little funny to me...
>> but
>> I do see where the reasoning comes from.
>>
>> Maybe because it reminds me too much of returning error codes (1234),
>> rather than throwing exceptions.
>>
>> And maybe because it couples your entire model to this result object.
>>
>> I don't have validation coming from my service save() methods, I have
>> it coming from my BO's. I don't think that its the Service's
>> responsibility to handle validation. Maybe that's just me tho.
>>
>> Mark
>>
>> On Thu, Jul 17, 2008 at 1:32 PM, Alan Livie
>> <[EMAIL PROTECTED]<mailto:[EMAIL PROTECTED]>> wrote:
>>>
>>> Thanks for this Brian.
>>>
>>> I'm just returning an array of structs with properties that map to
>>> form fields, message, error type etc. Makes a lot of sense to wrap
>>> this in a nice Result object.
>>>
>>> I'm just about to refactor the bean validation code so this was a
>>> timely discussion :-)
>>>
>>>
>>> Alan
>>>
>>> ________________________________________
>>> From: [email protected]<mailto:[email protected]> [EMAIL
>>> PROTECTED]<mailto:[email protected]>] On Behalf
>>> Of Brian Kotek [EMAIL PROTECTED]<mailto:[EMAIL PROTECTED]>]
>>> Sent: 16 July 2008 21:32
>>> To: [email protected]<mailto:[email protected]>
>>> Subject: [CFCDEV] Re: Service Layer X OO Architecture
>>>
>>> Yep that's pretty much it....things like isSuccess(),
>>> setSuccess(), hasErrors(), getErrors(), etc.
>>>
>>> On Wed, Jul 16, 2008 at 4:17 PM, Baz <[EMAIL PROTECTED]<mailto:[EMAIL
>>> PROTECTED]><mailto:[EMAIL PROTECTED]<mailto:[EMAIL PROTECTED]>
>>> >> wrote:
>>> I think Brian's result object is a generic one that can be re-used
>>> with any change operation. Something along the lines of:
>>>
>>> * Status (i.e. Success/Fail)
>>> * StatusCode (i.e. User.Validation.Error)
>>> * Message (i.e. There were some validation errors in the user
>>> object)
>>> * Payload (i.e. contains a structure of validation errors, or a
>>> proper business object - depending on the whether the operation
>>> was success or fail)
>>>
>>> The view would check the status of the result object to see how to
>>> handle the payload.
>>>
>>> Correct me if I am wrong Brian...
>>>
>>> Baz
>>>
>>>
>>>
>>> On Wed, Jul 16, 2008 at 1:09 PM, Ronan Lucio <[EMAIL
>>> PROTECTED]<mailto:[EMAIL PROTECTED]><mailto:[EMAIL PROTECTED]<mailto:[EMAIL
>>> PROTECTED]>
>>> >> wrote:
>>>
>>> Brian,
>>>
>>> Brian Kotek escreveu:
>>>> No Result is only returned during change operations, i.e. saving a
>>>> User. For list operations I just return the query, because there
>>>> can
>>>> be no validation failures for simply *getting* something. Hopefully
>>>> that makes sense.
>>>
>>> Yeap. That's clear.
>>>
>>> Thank you once again,
>>> Ronan
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>>
>>>
>>
>>
>>
>> --
>> E: [EMAIL PROTECTED]<mailto:[EMAIL PROTECTED]>
>> W: www.compoundtheory.com<http://www.compoundtheory.com>
>>
>>
>>
>>>
>>
>
>
>
> --
> E: [EMAIL PROTECTED]<mailto:[EMAIL PROTECTED]>
> W: www.compoundtheory.com<http://www.compoundtheory.com>
>
> >
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---