By Peter Bell

Objects or Services - how do you design your model?

In this post I look at service mania, overreaching objects, why the real world sucks and what the smart kids are doing!!!

There is a very straightforward syntactic choice that we have to make when architecting our domain models. Do we prefer a service based approach with UserService.save(ThisUser) or the slightly more concise ThisUser.save()?

Service Mania Some people really like their service classes. I believe that an overexposure to Java in their youth gave them mutant powers allowing them to type faster than the rest of us (and you certainly get plenty of practice typing if you program in Java!).

They are quite happy to use their service classes as a complete API for their model with the business objects being just a dumb bean with nothing more than perhaps a handful of custom getters or setters to distinguish it from an old fashioned struct (hash table if you’re temporarily joining us from another language).

Overreaching Objects At the other extreme, some developers will go to a User and ask it for (say) a list of all site users. To me calling User.getAll() seems a little strange because I think of a User as either a single user or (in my expedient approach) a collection of users (an encapsulation of a recordset). I don’t think of a user as a way of asking for a group of users but as a way of representing one or more users and interacting with them, so I’m not a fan of “everything in the object” either.

Forget the Real World – it’s Bogus One of the first things you learn when you start with object modeling is to look for real world objects (nouns) as candidate business objects. That is a good idea. However, some people take this further and start justifying design decisions based on whether the methods are consistent with the real world behavior of the objects.

Unfortunately after a couple of beer fuelled sessions you pretty well figure out that this is often a complete waste of time. I could support my design preference above by saying you can’t go to an invoice and ask it for a list of all of the invoices in the system. But there again, you can’t actually go to an invoice and ask it for anything – it’s a piece of paper – it doesn’t speak!

And then there are the whole class of issues that don’t have real world analogies such as saving to a database and transaction management.

I have found debates about what the real world object might do a great way to confuse people and to support pretty much any position you’re creative enough to take, but I have not found it to be much of a help in designing maintainable, flexible, usable applications with cohesive, shy, loosely coupled classes which is what I’m looking for. This leaves you with what others are doing and what feels good to you.

What are Others Doing? If you play with Ruby on Rails you’re going to get a feel for one approach where there is very little in the way of service classes and the business object acts as the primary interface to that object. If you spend some time in the Java world, you’re probably going to see a lot of service methods with much dumber beans.

What’s the Difference The interesting thing is that there is basically no difference between the two approaches. Functionally you can do the same things either way (thanks Dave Ross for explaining this to me so clearly). It is just a matter of whether the syntax of User.delete() or UserService.delete(User) seems more appealing.

Which Should I Choose The service crowd have been getting a lot of hassle over the last couple of years for anaemic domain models (thank you Mr. Fowler). But if the goal is encapsulation and cohesion the only real difference between the approaches is that with a service approach you think of the business object, service method and DAO as a small package that is cohesive and loosely coupled with the rest of the application and as long as you know what methods will be in each class, it is a perfectly valid way of organizing code. It doesn’t matter whether saveUser is in User or UserService. Just as long as it isn’t in your address object or your controller or your page views.

In fact, by giving you more files to spread your methods between, a service based approach provides certain benefits for larger applications where the number of methods grows and the business object class file can get unwieldy if it is given too much to do.

On the other hand, the object crowd also get hassled over the complexities of things like the active record pattern, but User.save() is no harder to implement than UserService.save() and User.getAddresses() is no harder to implement that AddressService.getUserAddresses() – they are just different ways of saying the same thing, so there is no reason not to use an active record pattern if you find the syntax (as I do) a little more natural – there really isn’t a downside (other than ending up with a lot of methods in the business object – but you have to put those methods somewhere).

I like the simplicity of calling User.save(). In the end I’ve decided to put my getting and deleting of collections of objects into the service layer, but once I have an Iterating Business Object (encapsulating a recordset with one or more records) I ask it for all of the operations I might want to perform on it. Just start walking deeper object trees and you’ll see why I like the syntax so much.

ThisCompany.getUser('17').getAllAddresses()

or

AddressService.getAllAddresses(CompanyService.getUser(ThisCompany,'17'))

I know which I find to be more readable and easier to use, and as writing programs is as much for readability to other people (think maintenance costs) as for comprehensibility for the compiler/interpreter I think that is an important point.

Dependency Injection The one gotcha is that ColdSpring is capable of injecting dependencies into transient business objects, but it wasn’t really designed to do so. If you start adding more intelligence to your business objects, they need to be part of the DI party so that User.getAddresses() can call AddressService.getUserAddresses(get(‘UserID’)) which requires it to have AddressService injected into it.

You can create custom bean factories in ColdSpring, but I decided to put together a simple DI engine for people who want a lightweight way to create transient objects as well as singletons. Right now LightWire is a proof of concept (although I’m using it internally and it works fine). I need to remove the dependence on base methods, add constructor based injection, implement auto-wiring and allow it to read at least the majority of a CS XML file, but once that is done there will be a very lightweight DI engine to make life easier for people who want to take a more business object (as opposed to service layer) approach to their CF OO development.

Comments
Peter,

This is a great discussion. I use a service architecture rather than an object architecture because I personally find it easier and I haven't found the need for a true object architecture (though I still find many OO rules - like encapsulation - to be essential.

I find with an object architecture it is essential to pass around objects and iterate through objects. With a service architecture, however, I find that I can reasonably pass around data and iterate through data (and get back a query recordset without guilt).

I'm certainly not arguing that an object architecture is a bad idea, just that a service architecture is viable in most situations (or, at least, that it is viable in mine).

I actually have my own DI engine that is really simple and I have been using for a while (before ColdSpring was around). It works with my style of using services instead of objects and has made my development life exceedingly easy. I am curious to see where you go with LightWire.
# Posted By Steve Bryant | 10/20/06 11:28 AM
I think you're comparing apples and oranges when you compare userService.save(user) vs. user.save(). It's two completely different approaches to persistence, not to mention overall application architecture, so using that as the sole example for your discussion greatly oversimplifies the power and of a service layer. When you say "there is basically no different between the two approaches" I think you're completing missing the point. It is *not* just about syntax.

By creating a service layer you are in effect creating an API through which the backend of the application is accessed. That goes far beyond persisting single entities. The service layer will typically provide not only access to your CRUD methods, but also to a host of other methods that allow for more flexible interaction with and encapsulation of the business logic of the application.

I also completely disagree that there is no downside to using active record; again this goes far beyond syntax. When you use active record your business objects "know" how they are persisted, and you're thereby wedding yourself to a particular persistence mechanism. If you change your persistence model, you have to change your business objects. With a service layer + DAO your business objects don't know anything about how they're persisted. Both approaches have advantages and disadvantages, but again, please don't oversimplify the discussion.
# Posted By Matt Woodward | 10/20/06 11:45 AM
@Matt,

I think I need to be more specific to show how similar these really are, so lets look at a practical architecture.

I have three files for (say) a product. ProductService,cfc, ProductDAO.cfc and ProductIBO.cfc (for my iterating business object). They are all in the same folder as each other and a different folder from all other cfcs so I can set package methods in the DAO keeping it shy. I put these in com.custom.model.product directory.

Please note that these methods are very tightly coupled and I have no use case where I would wantr to reuse a ProductService without reusing ProductDAO and ProductIBO - I just don't have that need.

If I had ProductService.save() there would be a save method in ProductService. If I had Product.save() there would be a save method in ProductIBO. Either way it would use composition to call the package "save" method of ProductDAO. I don't see how moving that one method between those two files does ANYTHING for coupling or encapsulation and I happen to feel it improves readability.

I should clarify that I do NOT put the persistence code within the object. Product.Save() is a one line method. ProductDAO.save(THIS) calling a package method within Product DAO. I guess that is not exactly how sme people might do active record, but I couldn't imagine actually putting SQL code in a business object.

Lets say I wanted to change my persistence mechanism, and lets say the interface changed. In the service approach I'd go into ProductService and change the save() method to do something else. In my approach I'd go into ProductIBO and change the save() method to do something else.

What is the downside, what am I missing?!
# Posted By Peter Bell | 10/20/06 1:54 PM
What you're missing is that you're still ignoring what else a service layer can do for you. As I said before, by focusing so heavily just on persistence of single records I think it results in a rather myopic view of the situation.

When you say you use composition in your beans are you actually saying you'd put the DAO *in* your bean? If so, that's quite a bizarre approach to me. Having a service layer access a DAO makes far more sense to me than having each of your beans contain a DAO. If you're sticking a whole DAO in each of your bean instances just to improve readability (and I would argue that at best it improves readability very marginally if at all), that's not the right way to go IMO.
# Posted By Matt Woodward | 10/20/06 2:05 PM
Hi Steve,

Makes perfect sense. The reason I do things differently is that I like to encapsulate my recordsets using an iterating business object so if you want to get("Ago") when the db only contains DoB or getRealPrice where that is a calculated attribute, they are as accessible as real fields. I like to completely encapsulate my database decisions to the extend that if you ask the DAO to get Age as part of an object it is smart enough to return you a recordset containing date of birth.

I find this approach to be essential as I'm trying to get to a place where nobody writes code to generate the apps so I need a pretty sophisticated engine to allow all of this to be specified declaritively.

Would love any hints or tips re: your DI engine, but I'll finish out LightWire (couple of pretty lame design decisions such as base methods and not fully working with standard init methods I need to fix out tomorrow) and popst it on RIAForge for people to play with.
# Posted By Peter Bell | 10/20/06 2:09 PM
Hi Matt,

I love a simple service layer. I HAVE a base service method with getbyFilter() and getbyUniquettribute() as well as deletebyFilter() and deletebyUniqueAttribute() methods. There are a number of things a service layer can do, but there are also plenty of things an IBO/business object can do.

What would be bizarre about putting a REFERENCE to a DAO into a bean? More importantly, what exact underlying software engineering principle does it violate more than allowing the service layer to speak to it? I'm not instantiating a DAO for my transient objects - just passing in a reference, and with a lightweight DI engine the cost of doing so is nominal.

What IS this magic difference between putting a one line save method into one file vs another file in the same directory? Who appointed the service layer as the API? There are real cases where allowing a subset of the business object API to reside in the object and another subset to reside in the service layer is more readable and intuitive. As long as there are clear and distinct rules so it is easy to understand what part of the API is in each I find it easier and more natural. I'm not going to invoke it being more OO, although most Ruby and Python developers have as hard a time using a service layer as some Java programmers have with robust beans.

I look at concepts like "tell don't ask", and wherever it seems reasonable I would much rather TELL a product to save itself than have to know the implementation of that by passing my "OO struct" to ProductService and asking it to do the work.

I'm not out on a limb here. There are a lot of programmers much smarter than me who've been using these patterns for a while outside of the CF world and they really do work well and they really do scale.

Is the object aproach BETTER than the ervice approach? I personally like a balance as I described in the article, but I am not seeing what possible harm one does over the other. They are both valid, robust, maintainable approaches to architecting a domain model.
# Posted By Peter Bell | 10/20/06 2:18 PM
Peter,

I may be missing something (wouldn't be the first time), but I don't see why that is much better than having an "Age" column in your recordset (even though your database doesn't have such a field).

All the IBO examples I see (which all end in "See how easy it is!") look like ASP to me. In fact, they look like the ASP code I use to compare to cfoutput as a demonstration of how easy ColdFusion is.

I haven't posted anything on my DI engine before because it is pretty basic. I have an XML document which defines my service components. The DI engine loads any that don't exist (in Application scope) or any that you want to reload. If you reload a component, it will also reload any component that is depending on it.

it also holds a copy of all of the components so that you could pass it into a component to expose all of the components of your application through it.

The other reason I haven't shared it, is that I am not sure it is "best practice" (though it has never caused me any problems).
# Posted By Steve Bryant | 10/20/06 2:22 PM
Is there confusion over here about the difference between passing by reference and passing by value?

Objects are passed by reference - I'm just telling the business object where to look - not creating a wole new DAO per operation . . . and I use DI to provide that refernce using object mixins.

Seems sraightforward to me . . .
# Posted By Peter Bell | 10/20/06 2:22 PM
Let me also clarify, I think reporting, general get and delete group operations and the like make more sense in the service layer. I think that it makes more sense to say UserService.reportMonthlySignUps() than User.reportMonthlySignups(). I think there are many things the service layer should do that relate to the entire object collection.

I just don't think they need to hande save(), getAssociated() and a number of other methods that (for me) fit more intuitively into my IBO. I'm refactoring all my base classes this weekend, so I'll post my refactored interface tomorrow - maybe then this'll make more sense to people.
# Posted By Peter Bell | 10/20/06 2:30 PM
Who appointed the service layer as the API? A whole lot of people; in some circles it's definitely a best practice. That doesn't mean it's the only way to do things, but your original post definitely had (as many of your posts do) a bit of a "people who do things this way are crazy" tone to it, so I merely wanted to voice the other side of things.

As for Java programmers having problems with robust beans, again you're oversimplifying. You can have robust, functional beans that do a whole lot more than get/set *without* throwing persistence into their realm of knowledge.
# Posted By Matt Woodward | 10/20/06 2:30 PM
Hi Steve,

You're trying to solve a general problem for any business object. One of the problems is that you need to treat calculated attributes as if they were persisted as your used doesn't care whether User.Age is in the db - they just want to be able to order, list, filter, page and find based on user age.

That is a trivial example, but I have some objects for some clients which have as many calculated attributes as persisted fields and I don't want to have to write ANY code to be able to order, filter, ist, page, etc. by calculated attributes OR by db fields. That is why I pass around an IBO as it encapsulates custom getting and setting rules for all of my attributes and hides whether a given attribute is persisted or not as in most cases (performance issues limit some options) it shouldn't matter at all to the front end.

You could put an Age column in your recordset and calculate that in the service layer, but find the IBO approach easier to work with as it gives me nice getAge() methods just where an OO programmer would expect to find them (in the business object) and this approach took me a while to get used to but as your applications get more and more complex it continues to be easy to maintain all of the appropriate business rules and calculations. I'll probably have to release some larger sample apps for the benefits to be apparent.

I get what you're saying about display code, but that is the cost of OO encapsulation - nothing is for free! However, I'm going to be replacing that with a simple designer templating language so they'll just put %FIrstName% and it'll replace with the appropriate getter.
# Posted By Peter Bell | 10/20/06 2:38 PM
Hi Matt,

Thanks for jumping in and balancing things out - always much appreciated! I'm sorry that my style sometimes rubs the wrong way. I still feel that the content was fairly balanced if you read through the whole posting again, but there is no question that there is a large community (mainly in the Java world I believe - although correct me if I'm wrong) that uses service layers (although I think a tide may be turning when I look at the kind of people who're looking to different approaches - often the original proponents of Java when it was "new kid on the block" back in what - 95?).

I also want to say that I stuff like the J2EE patterns guide is amazing. The Java world is still way ahead of .net in terms of architectural leadership (really - a PAGE controller in .net as opposed to a front controller - now that is bizarre!).

What I'm trying to redress is the fact that it seems to me there is a lot of discussion about the Java way to do OO in CF and I want CF developers to get that there is also the Smalltalk/Ruby/Python way. I'm not telling CF'ers to learn Ruby any more than you are telling them to learn Java, but I think there is a real debate to be had about the relative strengths of the different approaches for different use cases rather than just knee jerk reactions.

I hereby formally retract the assertion that Java programmers are indeed mutants (in case it wasn't obvious) and I further I formally retract the assertion that Java programmers can type faster than ordinary mortals. I do NOT retract the statement about the amount of typing required in Java and I would be quite happy to show real world code snippets in Ruby vs. Java if you want to debate the point!

But I've got to call you on one thing Matt. Now that I've explained it, what in the world is bizarre or even demonstrably less good according to fundamental software engineering principles about Product.Save() vs. ProductService.Save(Product) as described above?! If I AM missing something, this is the time . . . :->
# Posted By Peter Bell | 10/20/06 2:47 PM
@Matt - one really good point you made I want to echo and highlight:

Even Fowler said that adding persistence to business objects was orthogonal to his point about anemic domains. You are absolutely right that adding save() to your business objects does not make a rich domain object. And as pointed out by many people, many web domain models are anemic because the objects themselves are anemic. How much behavior is there in a simple CRUD admin system?!

I still feel that by using service layer be default as the API to the object can make behaviors that could reasonably be put into business objects migrate into service methods so you end up with something close to function libraries and structs (by another name). I do want to point out that that as I first mentioned, as long as you group your functions by object, an extremely service layer aproach just leave you with "two classes per class" with one focused on properties and the other on methods and while that is not what some OO developers would consider a best practice and isn't my preference, it doesn't inherently make the code less maintainable. It just makes some operations seem less natural to me when I write them.

Maybe it's a MG vs. M2 thing again - just a matter of feel?!

As to the tone, you know, I really think I sometimes take Mr. Corfields tag line too much to heart :->
# Posted By Peter Bell | 10/20/06 2:55 PM
Maybe bizarre wasn't the right word here, although referencing a DAO inside a bean actually is bizarre to me; it's like mixing active record and non-active-record style persistence, but whatever floats your boat.

What is bad or not about active record-style persistence depends on what you consider good practice. I personally think it's bad practice to have your business objects know anything about persistence. That's the bottom line for me. It's a fundamental belief I had ingrained in me long ago, it resonated with me and still does to this day, and for all the implementations of active record I've seen over the years, I still think it isn't the way to go.

That's just me--of course people are free to do whatever they want. There are plenty of proponents on both sides of that argument.
# Posted By Matt Woodward | 10/20/06 2:57 PM
Matt,

For those of us that are a bit slow, can you expand on why ActiveRecord necessarily means that a BO knows about persistence (I realize most implementations mean that, but I am not sure it is required of ActiveRecord).

Also, what are the practical drawbacks of a BO knowing about persistence. I know it isn't good practice, but I like to understand the risks.

For example, I generally make my code database-agnostic for portability. On some applications, however, I code for SQL Server because I know the odds of porting it to another database are remote (I'm talking generalities here, not code in the BO).
# Posted By Steve Bryant | 10/20/06 3:07 PM
Hi Matt,

Makes perfect sense to me. Like when I saw User.reportMonthlySignUps() in Ruby - just felt wrong as I didn't think a single user would know all the people who had signed up this month so I should call a service method (which I use for reporting and most get/delete object/collection methods depending on the use case).

For me, I have no opinion on whether an object should know how to persist itself and I would argue against anyone who argued that it fundamentally should OR shouldn't be able to do so - it doesn't seem to me to have a fundamental software engineering rationale either way.

That leaves me then with syntactical preference which is where I started this posting!!!

If it helps, the one thing that pushed me over the edge was the base associated() method. I REALLY don't want to go:

AddressService.getUserAddresses(User.getUserID()) to get the addresses for a user.

I much prefer the Reactor style approach of

User.getAddresses()

Well, to implement that, most natural solution for me is for User.getAddresses() to call AddressService.getUserAddresses() (this code it generated automatically for me). To do this, User needs to know about AddressService so I need to DI AddressService into the transient User object (I use the AddressService here to keep persistence choices for the address object shy - AddressService calls the package methods for AddressDAO.getbyFilter() method internally).

Once I'm DI'ing AddressService into User, I am not opposed to DI'ing UserDAO into User either.
# Posted By Peter Bell | 10/20/06 3:15 PM
Hi Steve,

I'll give you one perspective and Matt may give another. To me ActiveRecord doesn't REQUIRE the object to know how it is persisted (although I suppose you COULD throw SQL into your BO if you wanted to make your life more difficult :->). It does however require the object to know that persistence exists.

I have heard the argument that a business object should only be purely aware of real business methods as opposed to methods that are required artifacts of imperfections in how computers model the business process. Such an approach would suggest that as invoices don't have to worry about whether or not they are saved in a database, neither should invoice BO's. That should instead be handled by a service layer which takes care of (amongst other things) software specific artifacts such as persistence and transactions.

I get the argument and understand it, but it isn't fundamentally better than a well architected AR approach - it is just different.

Want another example of orthodoxy that is arguable? Gateways!!! If you really have too many methods to put in your DAO, WHY break up single record into one class and multi record into another? For example, getProduct(1,4,5) and getProduct(7) to me should call the same method as they are both returning a comma delimited list of products - one is just the special case where there is only one record in the list (I know the SQL is different, but you can handle that within the method or using a simple strategy pattern). The biggest reason to use gateways is if your gateways return a recordset and your DAO returns an object. ALL of my db methods return a recordset which is then wrapped in an IBO for all of them, so the DAO vs. table gateway distinction isn't wrong but neither is in fundamentally better for my use case than (say) breaking into a DAO class and a Report class for all of the reporting functions. The fact one is done more than the other makes it somewhat better as you shoudl generally follow the crowd unless there is a reason not to. The issue I have with is people following the crowd without knowing from first principles why they are doing so and what set of drivers might make them better off with a different approach!
# Posted By Peter Bell | 10/20/06 3:24 PM
Steve--there are various levels/degrees of "knowing" of course. I believe that having your BOs know anything about how they're persisted is bad. Maybe I'm an extremist, but even being able to call user.save() on a BO instance seems wrong to me. I much, much prefer having an external object take that BO in and persist it. To me it's more flexible and keeps a better separation of concerns between the persistence layer and the BOs. In an OO system persistence is just a way to permanently sotre something, nothing more, and sticking that funtionality in the BOs themselves just doesn't make sense to me. If we had unlimited RAM and the power never went out, we wouldn't even need persistence. ;-)

You can of course have an active record implementation be either more or less knowledgable about how the persistence actually works. In many implementations your BOs are required to extend a specific parent object, and that parent object contains the persistence methods. That's bad form in my mind because your business objects are dependent upon that parent object for their persistence. How much does the parent object know or not know about the specifics of persistence? It depends on the implementation. In some cases there's extremely tight coupling between the BOs and their persistence, in other cases not so much, but again, to me having this even in your BOs is wrong (again, my opinion) because that isn't where that stuff belongs.

The drawbacks to me are that it muddies the waters in the BOs with something that doesn't belong there. I also really like knowing that I could completely gut my persistence layer and not only would the BOs not know or care, they wouldn't be involved in any way.

That's my story and I'm sticking to it. ;-)
# Posted By Matt Woodward | 10/20/06 3:26 PM
Well Matt, at least we both agree that a BO shouldn't know HOW to persist itself with ersistence code in the BO base class. Nice to know we can agree on something :->

Seriously though, many thanks for taking the time to post - I know you are busy and really appreciate your experience and opinions. Looking forwards to catching your classes at the frameworks conference. It took Dave Ross' session on ColdSpring for me to start to get DI. Maybe in February I'll finally learn to love Mach-II!!!
# Posted By Peter Bell | 10/20/06 3:29 PM
You can call me a ColdFusion Weekly crony because I'm commenting here with Matt. I couldn't resist.

There are also a few other "downsides" to the active record approach in which I've recently had some experience with lately with Reactor. One of the problems if you don't want to tie yourself to a single persistence mechanism is that you end up writing more layers to abstract the persistence machanism. In my opinion, this is a PITA.

Secondly, using active record makes it very easy and tempting to call save() and read() is places in your code that you probably shouldn't be like views, controller level files, etc. I believe this becomes a problem for less experienced developers -- it allows people to de-centralize persistence code and unless you are very dedicated to methodology - it can make a maintence nightmare.

All in all, it's the choice of the developer. Sometimes no matter how much you research the subject -- nothing is better than real world practical experience. People of the CF world, learn by doing and by making mistakes!
# Posted By Peter J. Farrell | 10/20/06 4:45 PM
Yeah - you're CF Weekly crony - and I'm a CF Weekly fanboy (love it). So, you gonna give me the secret decoder ring or do I have to call in weekly for a year (what a frightening thought for you!) :->

Lets start by agreeing (I WANT that decoder ring, dangit). Making things easier is good except when people misuse those easier things. AR can be (and is) wildly abused. No argument there.

Secondly, I agree that it is a developer choice. I'm just trying to pull together pros and cons so people (well, me actually) can make more informed choices. Practical experience with the code was what moved me towards AR - I liked the way it flowed when writing the code, but it's still imortant to discuss just to check we're not giving up integrity for expedience (hands up anyone who ever put too many methods into their base class(es).

I'm not so sure about the persistence mechanism (not speaking about reactor but in general). Fundamentally UserService.save() has to do something. If Uer.save() does exactly the same thing in exactly the same way, it can't really be any easier or harder to refactor - it is just a matter of which of the two class files you edit - UserService.cfc or UserObject.cfc - that is it.

This assumes you're using a DI engine that allows you to DI equally easily into transients and singletons. If you're not then AR becomes harder to implement as you have to do base classes or something and one thing I believe we're both going to agree on is that persistence is something that should be composed, not inherited.
# Posted By Peter Bell | 10/20/06 5:00 PM
Matt,

Thanks for the feedback.I have to admit that on some level I see the idea of the BO knowing nothing about its own persistence to be a little "Ivory Tower" - probably because I haven't experience pain with it yet (pain is really the best teacher). Does that mean that you have to handle persistence for BO data outside of the BO? That seems like an extra step somehow?

Peter,

What is the worry with tying yourself to a single persistence mechanism? Does inheritence versus composition have a large impact in this context for you (I find that I shy away from any solution that relies on inheritence - "Don't mess with my inheritence tree!")?
# Posted By Steve Bryant | 10/20/06 5:02 PM
Steve,

Not to speak for Matt, but it isn't so much an extra step as some extra characters. UserService.save(ThisUser) instead of ThisUser.save() - not a big difference really.

In terms of tying to a single persistence mechanism, balance YAGNI (You Ain't Gonna Need It) with "encapsulate what varies" for your use case and decide. I try to implement my AR in such a way that changing persistence mechanisms would be easy. I'd probably use LightWire to just pass in a different DAO based on business rules if I needed to support multiple concurrent persistent mechanisms. In practice it's a problem I've never had so I'm hanging out on the YAGNI end of the spectrum for my use case.

In terms of inheritance vs. composition, I personally don't think a User IS a DAO/persistence object so then the question becomes whether a User should know how to persist itself. I find it perfectly acceptable and useful for a User to know who to ask to persist it as it provides me with User.save() which is really nice (to me). I do NOT think a User should know anything about data access, so I think the DAO methods should be composed.

Personally I have a base IBO with a save method that calls the appropriate DAO.save() for the given object as a User IS AN IBO so I have no problem extending a bunch of useful generalized classes for IBOs, DAOs and service methods.

So, I think persistence mechanisms should be composed rather than inherited as to me it solves some of the concerns Matt and Pete have while still giving me the syntax, readability, style and usability that I prefer. If I was working with a team of Java programmers, I probably wouldn't bother as the enhanced readability for me would be overcome by the decreased readability (and probably increased scorn) from them!
# Posted By Peter Bell | 10/20/06 5:21 PM
I might have completely failed to keep up with modern terminology - my last foray into OO was Smalltalk nearly 20 years ago - but one thing that seems to be completely missing from what I'm reading about CF OO (and by extension Java) is any consideration of class methods vs instance methods. User.reportMonthlySignUps() looks like a class method and doesn't bother me at all.

aUser = User.new();
aUser.reportMonthlySignups();

is weird. There is a difference, right? or is this concept just old-fashioned these days?
# Posted By Jaime Metcher | 11/12/06 6:19 AM
Hi Jamie,

Wouldn't know as I missed out on using SmallTalk, but what you describe as a class method sounds like the kind of method that is often put into a manager or service method singleton in the Java world and that is the pattern that most CF developers seem to be adopting. I personally like the distinction between UserService and User as it seems to me to aide cohesion as otherwise you'd end up with an awful lot of methods in your business objects.
# Posted By Peter Bell | 11/13/06 12:18 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.