ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Shannon, Bryan" <BShan...@Tribune.com>
Subject RE: Too many methods on a service object
Date Mon, 14 Nov 2005 20:12:14 GMT
Just throwing in something I've seen before...  A former co-worker of mine
made a pretty elaborate solution to the problem of loading object graphs of
arbitrary depth... (A contact that has an address, that has a city, that has
a ...)

She created metadata that were essentially constants shared between the
client layer and the services layer.  When calling a service method such as
loadPerson(), you would send in a "LoadLevel" class, which is basically an
array of the "Fields" (metadata) that you want.

You might do something like this:

LoadLevel myLoadLevel = new LoadLevel();
myLoadLevel.add(PersonMeta.FIELD_NAME);
myLoadLevel.add(PersonMeta.FIELD_AGE);
myLoadLevel.add(PersonMeta.FIELD_ADDRESS);

Person p = serviceLayer.loadPerson(id, myLoadLevel);



This metadata could also be used when persisting the change in order to
determine which fields to examine for differences...  

If the new person doesn't have a "name" field filled in but the original
person did, then normally you would delete the name for that person.  Using
the loadlevel stuff, you would save with the same loadlevel you loaded with
to determine which fields to update in the db.  (So you don't accidentally
delete a persons name when save after loading it without the name field).

It works very well.  Especially in cases where you want to re-use the find()
logic (that might display a list of people as search results with only the
id and name)  for the load logic, which would contain a more complete load
level.  Load Levels themselves could be pre-defined and shared for commonly
used subsets of data (one for partial, one for full, etc.)  It is also handy
in cases where more than one "Person" (in this example case) can share an
address...  Where you wouldn't want to change someone else's address by
updating only one person at that address.

-Bryan


-----Original Message-----
From: Ron Grabowski [mailto:rongrabowski@yahoo.com]
Sent: Saturday, November 12, 2005 12:32 AM
To: user-java@ibatis.apache.org
Subject: Re: Too many methods on a service object


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 <bent@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
> 

Mime
View raw message