You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user-java@ibatis.apache.org by Ron Grabowski <ro...@yahoo.com> on 2005/11/12 00:12:20 UTC
Too many methods on a service object
I've been discussing design patterns (mainly for web sites) with a
colleague over the last couple days and my brain is fried with trying
to come up with quick answers. Here's some of the things that we've
talked about. Hopefully people will share their two cents on these
issues...
A common pattern for iBATIS is to use it in a service based
architecture. Suppose I have a Person and an Address object. Normally
there would be one or more static methods on the service object:
Person person = PersonService.GetOne(personId); *
bool success = PersonService.Update(personId);
My colleague brought up the point that this can get out of hand (i.e.
you start getting _a lot_ of methods):
PersonService.GetMany();
PersonService.GetOne(personId);
PersonService.GetManyWithNonNullAddresses();
PersonService.GetManyWithNonCityAddresses();
PersonService.UpdateAddresses(personId, addresses);
PersonService.UpdateIfPersonIsMale(person);
PersonService.UpdateIfPersonIsMarried(person);
...
My argument was that those methods wouldn't be written until they
needed to be used. In other words I wouldn't just be making up random
methods and never use them...each method would serve a purpose in the
system.
Another good point that I wasn't able to answer on the spot was the
idea that it would be complicated to populate related objects. I can't
think of a good example of this but suppose I wanted to get the city
names of a person's addresses:
Person person = PersonService.GetOne(personId);
AddressCollection addressCollection =
AddressService.GetManyByPerson(person);
...
Again, that's not the best example. Imagine the case where I have to
call through to many service objects until I get my final result. My
answer to him was that I would replace all those calls with a single
method that returns exactly what I wanted. Instead of calling multiple
service objects, I would simple encapsulate the calls into another
service object:
IList cityNames = CityService.GetNamesByPersonId(personId);
My vague question is...is there a better way I can explain things?
"Adding methods when I need them" doesn't seem like the best answer.
Some other questions I was stumbling on: should the service layer be
defined through interfaces?
Should the service layer throw its own ServiceExceptions or just pass
through the data layer's exception? What happens if the service layer
just throw up the data exceptions to the presentation layer? The
presentation layer shouldn't know anything about the data layer...
Thanks,
Ron
* - Yes, I'm aware that I posted C# style examples to a Java list.
Re: Too many methods on a service object
Posted by Larry Meadors <lm...@apache.org>.
WARNING: Long, rambling email alert!
OK, I had to read this a few times and wait before responding, because
my first inclination when I saw that service API was: "If someone
checked code with those methods into *my* project, I would kill them
and feed their remains to the bears...and not because of the .net
naming."
:-D
Now, with Ron's clarification, I can contribute my $.02US.
I am not sure that I agree 100% with Ben or Paul on the interfaces in all cases.
I understand the principle, and agree with it in the data layer, but
in my service layer, I am enforcing my application's business rules.
Maybe the difference in opinion is just based on what we are putting
where. I do agree that having interfaces for areas where the
implementation could change, but hopefully, that is not at the core of
your business rules - some of them, yes; all of them, no. :-)
Using interfaces for the sake of completeness (i.e., on every layer)
is a mild form of insanity, IMO. :-)
Of course, I am also using Spring to inject those services, so maybe
that is at the heart of my uneasiness with that approach. My service
consumer is managed by spring, and doesn't know or care if it has an
interface or implementation. The service gets injected into the
application, just like the dao implementations get injected into the
service, which begs the question: Why do I use an interface there?
To answer my own question, I guess I feel like I need an interface
there because the DAO is an integration point. It is talking to
something *outside* of my Java (or C#, for Ron) code, and I like to
have a service gateway (http://tinyurl.com/7bz2j) for testing
purposes. An interface/impl pair makes that easier. That is not the
case in the service layer.
Larry
On 11/11/05, Ron Grabowski <ro...@yahoo.com> wrote:
> Thanks for the feedback. Most of the methods on the PersonService
> object were used just as examples to show how one could get carried
> away with added methods. I switched jobs recently and talking about
> design strategies with new people has caused my head to spin a bit.
>
> Getting back to the Person / Address relationship, suppose that the two
> entities are related in the database through a PersonAddress table
> (PersonAddressId, PersonId, AddressId). I usually represent this table
> as a PersonAddress entity with a matching service PersonAddressService
> class. I "cheat" a little bit with the PersonAddress object so it looks
> like this:
>
> PersonAddressId
> PersonId
> PersonFirstName
> PersonLastName
> AddressId
> AddressStreet
> AddressCity
> AddressState
> AddressZip
>
> I add additional properties from the Person or Address objects as
> needed. The awkward part about the PersonAddress object is that I have
> to remember when creating or updating PersonAddress objects that only
> the PersonAddressId, PersonId, AddressId fields are important to the
> database. Values in the other fields exist purely out of convenience
> and are ignored during adds or updates. I always flip-flop on whether
> its better to just do away with the PersonAddress object and just have
> the PersonService class return a list of complete Address objects (or
> vice versa). That would definitelty decrease the number of little
> objects my system has to keep track of. I never liked the fact the
> layers further down the line know that Persons and Addresses are
> related via a PersonAddress table. They shouldn't know about that. An
> argument for such an object would be the case where a Person object had
> a btye[] that contained a large picture of the person and each Address
> object had a byte[] that contained a large picture of the property. If
> there are 50 people and each person has 5 addresses with pictures, I
> shouldn't populate those fields if I'm never going to use them on the
> list page that just shows first names and street names. The
> PersonAddress object comes in handy for that...
>
> --- Ben Munat <be...@munat.com> wrote:
>
> > Ron Grabowski wrote:
> > > I've been discussing design patterns (mainly for web sites) with a
> > > colleague over the last couple days and my brain is fried with
> > trying
> > > to come up with quick answers. Here's some of the things that we've
> > > talked about. Hopefully people will share their two cents on these
> > > issues...
> > >
> > > A common pattern for iBATIS is to use it in a service based
> > > architecture. Suppose I have a Person and an Address object.
> > Normally
> > > there would be one or more static methods on the service object:
> > >
> > > Person person = PersonService.GetOne(personId); *
> > > bool success = PersonService.Update(personId);
> > >
> > > My colleague brought up the point that this can get out of hand
> > (i.e.
> > > you start getting _a lot_ of methods):
> > >
> > > PersonService.GetMany();
> > > PersonService.GetOne(personId);
> > > PersonService.GetManyWithNonNullAddresses();
> > > PersonService.GetManyWithNonCityAddresses();
> > > PersonService.UpdateAddresses(personId, addresses);
> > > PersonService.UpdateIfPersonIsMale(person);
> > > PersonService.UpdateIfPersonIsMarried(person);
> > > ...
> > > My argument was that those methods wouldn't be written until they
> > > needed to be used. In other words I wouldn't just be making up
> > random
> > > methods and never use them...each method would serve a purpose in
> > the
> > > system.
> >
> > Not only would I not add all these before they're called for, I
> > wouldn't even add any
> > methods like this. They smack of "hardcoding the search criteria",
> > something that's quite
> > likely to change. A better approach would be to write a
> > PersonService.search method that
> > takes a Person object and calls a PersonDAO.search method. This
> > copies the fields that are
> > in the person object into a dynamically built select query. The
> > results of this are all
> > the Persons in the db that match that criteria.
> >
> > Hmm, although I just noticed that you have multiple update methods
> > too. Seems like you're
> > trying to push too much into the service... like you want to have the
> > service methods
> > handle every possible logic condition that your action (or the client
> > of the service)
> > might need. Down that path lies madness.
> >
> > > Another good point that I wasn't able to answer on the spot was the
> > > idea that it would be complicated to populate related objects. I
> > can't
> > > think of a good example of this but suppose I wanted to get the
> > city
> > > names of a person's addresses:
> > >
> > > Person person = PersonService.GetOne(personId);
> > > AddressCollection addressCollection =
> > > AddressService.GetManyByPerson(person);
> > > ...
> > >
> > > Again, that's not the best example. Imagine the case where I have
> > to
> > > call through to many service objects until I get my final result.
> > My
> > > answer to him was that I would replace all those calls with a
> > single
> > > method that returns exactly what I wanted. Instead of calling
> > multiple
> > > service objects, I would simple encapsulate the calls into another
> > > service object:
> > >
> > > IList cityNames = CityService.GetNamesByPersonId(personId);
> > >
> > > My vague question is...is there a better way I can explain things?
> > > "Adding methods when I need them" doesn't seem like the best
> > answer.
> >
> > I think that's a perfectly good answer. As the agile folks say:
> > YAGNI... you ain't gonna
> > need it. Which basically means, if you think you can forsee all the
> > functionality you're
> > gonna need, forget it... that always changes. Just build what you
> > need today.
> >
> > I also think that for many systems, most functionality you'll need
> > for most services is
> > covered by basic CRUD stuff... or more like retrieve, save, delete,
> > and search, with save
> > handling both new records and updates.
> >
> > That city example does seem pretty far-fetched, but I guess if there
> > was a need for that
> > (finding all the cities that a person record has in their
> > addresses?), yes it would make
> > sense to make a service method, which in turn calls a dao method,
> > which calls a particular
> > sqlmap that performs the query you need.
> >
> > > Some other questions I was stumbling on: should the service layer
> > be
> > > defined through interfaces?
> >
> > Absolutely! All boundaries -- layer and system -- should ideally be
> > represented by
> > interfaces. This allows you to change implementations or substitute
> > implementations based
> > on a strategy. And services should generally have two implementations
> > right off the bat:
> > test and impl.
> >
> > > Should the service layer throw its own ServiceExceptions or just
> > pass
> > > through the data layer's exception? What happens if the service
> > layer
> > > just throw up the data exceptions to the presentation layer? The
> > > presentation layer shouldn't know anything about the data layer...
> >
> > Hmm, exceptions are kind of a contentious issue... the argument being
> > that checked
> > exceptions were a mistake because you're still hopefully only gonna
> > actually deal with an
> > exception at one place, so why be forced to catch and rethrow at
> > every layer. Spring, for
> > example offers a huge Exception hierarchy that is (I think) entirely
> > unchecked.
> >
> > Anyway, the general pattern I've seen is to create an abstract
> > Exception base class for
> > your app, with two subclasses: Logic and System (so like this:
> > MyAppException,
> > MyAppLogicException and MyAppSystemException). Any business rule
> > validations throw the
> > logic exception, while any unforeseen system errors (db failure, file
> > not found, etc.) get
> > wrapped in the system exception.
> >
> >
> > > Thanks,
> > > Ron
> > >
> > > * - Yes, I'm aware that I posted C# style examples to a Java list.
> >
> > Eeeewwww... I hate that ICrap.... ;-)
> >
> > b
> >
>
>
Re: Too many methods on a service object
Posted by Ron Grabowski <ro...@yahoo.com>.
Thanks for the feedback. Most of the methods on the PersonService
object were used just as examples to show how one could get carried
away with added methods. I switched jobs recently and talking about
design strategies with new people has caused my head to spin a bit.
Getting back to the Person / Address relationship, suppose that the two
entities are related in the database through a PersonAddress table
(PersonAddressId, PersonId, AddressId). I usually represent this table
as a PersonAddress entity with a matching service PersonAddressService
class. I "cheat" a little bit with the PersonAddress object so it looks
like this:
PersonAddressId
PersonId
PersonFirstName
PersonLastName
AddressId
AddressStreet
AddressCity
AddressState
AddressZip
I add additional properties from the Person or Address objects as
needed. The awkward part about the PersonAddress object is that I have
to remember when creating or updating PersonAddress objects that only
the PersonAddressId, PersonId, AddressId fields are important to the
database. Values in the other fields exist purely out of convenience
and are ignored during adds or updates. I always flip-flop on whether
its better to just do away with the PersonAddress object and just have
the PersonService class return a list of complete Address objects (or
vice versa). That would definitelty decrease the number of little
objects my system has to keep track of. I never liked the fact the
layers further down the line know that Persons and Addresses are
related via a PersonAddress table. They shouldn't know about that. An
argument for such an object would be the case where a Person object had
a btye[] that contained a large picture of the person and each Address
object had a byte[] that contained a large picture of the property. If
there are 50 people and each person has 5 addresses with pictures, I
shouldn't populate those fields if I'm never going to use them on the
list page that just shows first names and street names. The
PersonAddress object comes in handy for that...
--- Ben Munat <be...@munat.com> wrote:
> Ron Grabowski wrote:
> > I've been discussing design patterns (mainly for web sites) with a
> > colleague over the last couple days and my brain is fried with
> trying
> > to come up with quick answers. Here's some of the things that we've
> > talked about. Hopefully people will share their two cents on these
> > issues...
> >
> > A common pattern for iBATIS is to use it in a service based
> > architecture. Suppose I have a Person and an Address object.
> Normally
> > there would be one or more static methods on the service object:
> >
> > Person person = PersonService.GetOne(personId); *
> > bool success = PersonService.Update(personId);
> >
> > My colleague brought up the point that this can get out of hand
> (i.e.
> > you start getting _a lot_ of methods):
> >
> > PersonService.GetMany();
> > PersonService.GetOne(personId);
> > PersonService.GetManyWithNonNullAddresses();
> > PersonService.GetManyWithNonCityAddresses();
> > PersonService.UpdateAddresses(personId, addresses);
> > PersonService.UpdateIfPersonIsMale(person);
> > PersonService.UpdateIfPersonIsMarried(person);
> > ...
> > My argument was that those methods wouldn't be written until they
> > needed to be used. In other words I wouldn't just be making up
> random
> > methods and never use them...each method would serve a purpose in
> the
> > system.
>
> Not only would I not add all these before they're called for, I
> wouldn't even add any
> methods like this. They smack of "hardcoding the search criteria",
> something that's quite
> likely to change. A better approach would be to write a
> PersonService.search method that
> takes a Person object and calls a PersonDAO.search method. This
> copies the fields that are
> in the person object into a dynamically built select query. The
> results of this are all
> the Persons in the db that match that criteria.
>
> Hmm, although I just noticed that you have multiple update methods
> too. Seems like you're
> trying to push too much into the service... like you want to have the
> service methods
> handle every possible logic condition that your action (or the client
> of the service)
> might need. Down that path lies madness.
>
> > Another good point that I wasn't able to answer on the spot was the
> > idea that it would be complicated to populate related objects. I
> can't
> > think of a good example of this but suppose I wanted to get the
> city
> > names of a person's addresses:
> >
> > Person person = PersonService.GetOne(personId);
> > AddressCollection addressCollection =
> > AddressService.GetManyByPerson(person);
> > ...
> >
> > Again, that's not the best example. Imagine the case where I have
> to
> > call through to many service objects until I get my final result.
> My
> > answer to him was that I would replace all those calls with a
> single
> > method that returns exactly what I wanted. Instead of calling
> multiple
> > service objects, I would simple encapsulate the calls into another
> > service object:
> >
> > IList cityNames = CityService.GetNamesByPersonId(personId);
> >
> > My vague question is...is there a better way I can explain things?
> > "Adding methods when I need them" doesn't seem like the best
> answer.
>
> I think that's a perfectly good answer. As the agile folks say:
> YAGNI... you ain't gonna
> need it. Which basically means, if you think you can forsee all the
> functionality you're
> gonna need, forget it... that always changes. Just build what you
> need today.
>
> I also think that for many systems, most functionality you'll need
> for most services is
> covered by basic CRUD stuff... or more like retrieve, save, delete,
> and search, with save
> handling both new records and updates.
>
> That city example does seem pretty far-fetched, but I guess if there
> was a need for that
> (finding all the cities that a person record has in their
> addresses?), yes it would make
> sense to make a service method, which in turn calls a dao method,
> which calls a particular
> sqlmap that performs the query you need.
>
> > Some other questions I was stumbling on: should the service layer
> be
> > defined through interfaces?
>
> Absolutely! All boundaries -- layer and system -- should ideally be
> represented by
> interfaces. This allows you to change implementations or substitute
> implementations based
> on a strategy. And services should generally have two implementations
> right off the bat:
> test and impl.
>
> > Should the service layer throw its own ServiceExceptions or just
> pass
> > through the data layer's exception? What happens if the service
> layer
> > just throw up the data exceptions to the presentation layer? The
> > presentation layer shouldn't know anything about the data layer...
>
> Hmm, exceptions are kind of a contentious issue... the argument being
> that checked
> exceptions were a mistake because you're still hopefully only gonna
> actually deal with an
> exception at one place, so why be forced to catch and rethrow at
> every layer. Spring, for
> example offers a huge Exception hierarchy that is (I think) entirely
> unchecked.
>
> Anyway, the general pattern I've seen is to create an abstract
> Exception base class for
> your app, with two subclasses: Logic and System (so like this:
> MyAppException,
> MyAppLogicException and MyAppSystemException). Any business rule
> validations throw the
> logic exception, while any unforeseen system errors (db failure, file
> not found, etc.) get
> wrapped in the system exception.
>
>
> > Thanks,
> > Ron
> >
> > * - Yes, I'm aware that I posted C# style examples to a Java list.
>
> Eeeewwww... I hate that ICrap.... ;-)
>
> b
>
Re: Too many methods on a service object
Posted by Ben Munat <be...@munat.com>.
Ron Grabowski wrote:
> I've been discussing design patterns (mainly for web sites) with a
> colleague over the last couple days and my brain is fried with trying
> to come up with quick answers. Here's some of the things that we've
> talked about. Hopefully people will share their two cents on these
> issues...
>
> A common pattern for iBATIS is to use it in a service based
> architecture. Suppose I have a Person and an Address object. Normally
> there would be one or more static methods on the service object:
>
> Person person = PersonService.GetOne(personId); *
> bool success = PersonService.Update(personId);
>
> My colleague brought up the point that this can get out of hand (i.e.
> you start getting _a lot_ of methods):
>
> PersonService.GetMany();
> PersonService.GetOne(personId);
> PersonService.GetManyWithNonNullAddresses();
> PersonService.GetManyWithNonCityAddresses();
> PersonService.UpdateAddresses(personId, addresses);
> PersonService.UpdateIfPersonIsMale(person);
> PersonService.UpdateIfPersonIsMarried(person);
> ...
> My argument was that those methods wouldn't be written until they
> needed to be used. In other words I wouldn't just be making up random
> methods and never use them...each method would serve a purpose in the
> system.
Not only would I not add all these before they're called for, I wouldn't even add any
methods like this. They smack of "hardcoding the search criteria", something that's quite
likely to change. A better approach would be to write a PersonService.search method that
takes a Person object and calls a PersonDAO.search method. This copies the fields that are
in the person object into a dynamically built select query. The results of this are all
the Persons in the db that match that criteria.
Hmm, although I just noticed that you have multiple update methods too. Seems like you're
trying to push too much into the service... like you want to have the service methods
handle every possible logic condition that your action (or the client of the service)
might need. Down that path lies madness.
> Another good point that I wasn't able to answer on the spot was the
> idea that it would be complicated to populate related objects. I can't
> think of a good example of this but suppose I wanted to get the city
> names of a person's addresses:
>
> Person person = PersonService.GetOne(personId);
> AddressCollection addressCollection =
> AddressService.GetManyByPerson(person);
> ...
>
> Again, that's not the best example. Imagine the case where I have to
> call through to many service objects until I get my final result. My
> answer to him was that I would replace all those calls with a single
> method that returns exactly what I wanted. Instead of calling multiple
> service objects, I would simple encapsulate the calls into another
> service object:
>
> IList cityNames = CityService.GetNamesByPersonId(personId);
>
> My vague question is...is there a better way I can explain things?
> "Adding methods when I need them" doesn't seem like the best answer.
I think that's a perfectly good answer. As the agile folks say: YAGNI... you ain't gonna
need it. Which basically means, if you think you can forsee all the functionality you're
gonna need, forget it... that always changes. Just build what you need today.
I also think that for many systems, most functionality you'll need for most services is
covered by basic CRUD stuff... or more like retrieve, save, delete, and search, with save
handling both new records and updates.
That city example does seem pretty far-fetched, but I guess if there was a need for that
(finding all the cities that a person record has in their addresses?), yes it would make
sense to make a service method, which in turn calls a dao method, which calls a particular
sqlmap that performs the query you need.
> Some other questions I was stumbling on: should the service layer be
> defined through interfaces?
Absolutely! All boundaries -- layer and system -- should ideally be represented by
interfaces. This allows you to change implementations or substitute implementations based
on a strategy. And services should generally have two implementations right off the bat:
test and impl.
> Should the service layer throw its own ServiceExceptions or just pass
> through the data layer's exception? What happens if the service layer
> just throw up the data exceptions to the presentation layer? The
> presentation layer shouldn't know anything about the data layer...
Hmm, exceptions are kind of a contentious issue... the argument being that checked
exceptions were a mistake because you're still hopefully only gonna actually deal with an
exception at one place, so why be forced to catch and rethrow at every layer. Spring, for
example offers a huge Exception hierarchy that is (I think) entirely unchecked.
Anyway, the general pattern I've seen is to create an abstract Exception base class for
your app, with two subclasses: Logic and System (so like this: MyAppException,
MyAppLogicException and MyAppSystemException). Any business rule validations throw the
logic exception, while any unforeseen system errors (db failure, file not found, etc.) get
wrapped in the system exception.
> Thanks,
> Ron
>
> * - Yes, I'm aware that I posted C# style examples to a Java list.
Eeeewwww... I hate that ICrap.... ;-)
b
Re: Too many methods on a service object
Posted by Paul Benedict <pa...@yahoo.com>.
Ron,
Some comments on your worsd:
>>Suppose I have a Person and an Address object. Normally there would be one or more static
methods on the service object
In Java, the popular method is to create a real object and make instance calls. ALot of people
have taken hold to the Spring Framework, which will instantiate singletons of whatever class and
then you use those during your app. So here, use singleton instance objects.
> My colleague brought up the point that this can get out of hand (i.e.
> you start getting _a lot_ of methods):
I guess it is what level of tolerance you have. If you are realistic about a situation, you will
end up having many methods that all represent real business cases, but I have to say some of those
methods below look seriously wrong. There is no reason to have a GetManyWithNonNullAddresses and
then a GetManyWithNonCityAddresses method; you should make the choice to return ALWAYS full
objects at least one object-depth deep. So, in your case, always return addresses if they are one
depth in the object graph. When objects get more complicated and you are returning too much data
you will not use, this is where you refactor into nested objects. Here, you can then craft methods
to return how many nested objects you want or use iBATIS' autoproxying to run SELECT statements on
the nested objects you later hit. I don't have your problem here, and yours can be effectively
solve.
> My argument was that those methods wouldn't be written until they
> needed to be used. In other words I wouldn't just be making up random
> methods and never use them
Do not expose any methods your clients will not need. Make the rest private until there is a
business case.
> Again, that's not the best example. Imagine the case where I have to
> call through to many service objects until I get my final result.
There's nothing wrong with service objects relying on other services. However, your problem,
again, goes back to your object graph. If you model your objects properly, you will understand
that when you SELECT for an object, you take as many levels deep as you think you will need....
But at least one level deep for simple properties.
> Some other questions I was stumbling on: should the service layer be
> defined through interfaces?
Absolutely. All service objects should be an interface that is implemented. Java requires this to
do auto-proxying.
> Should the service layer throw its own ServiceExceptions or just pass
> through the data layer's exception? What happens if the service layer
> just throw up the data exceptions to the presentation layer? The
No. The service layer should not create a wrapper exception just for the sake of it. The Spring
Framework philosophy -- which is pretty popular -- is to let all runtime exceptions bubble to the
top and only throw checked exceptions if the client can actually recover from it. I allow all my
exceptions to go through the roof and then I have a webapp configuration which handles the
exceptions for me. BTW, I did do EXACTLY what you're asking and it turned out to be a bad idea;
yes, you will have one nice exception to handle but it's a completely useless feature. Only throw
custom exceptions if they have real business meanings (UnauthorizedException,
StaleObjectException, etc.) and real business decisions can be made from them. Otherwise, let them
go to the sky.
-- Paul
__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005
http://mail.yahoo.com