For those of you who use gateways, if you have an admin system that has a “check here to edit” allowing you to edit a subset of fields for multiple records (say changing price for these 4 products in one go), where do you put your getByProductID() method? Do you just have ProductDAO.getbyProductID() and ProductGateway.getByProductIDList() as two separate methods and allow the second one to return 1..n records?
Also, is anyone writing their own “mini-ORM”? How do you do the ThisUser.getAssociatedAddresses() kind of functionality without injecting either the AddressService or the UserService into the User object (or do you use a UserService.getAssociatedAddresses(ThisUser) approach?)? And yes, I do know Reactor does this for you and I have played with it. Reactor is great. That’s not my question!
Best Wishes,
Peter
On 11/1/06 12:38 PM, "Peter Bell" <[EMAIL PROTECTED]> wrote:
Hi Eric,
Great overview! I’m just going to suggest a couple of optional decision points where you could do things differently (not generally better or worse – just different options depending on preference and use case).
Firstly, I tend to think of the #ObjectName#Service as an API as it either provides a full interface to the object or a partial interface in combination with the business object depending on how much you ask your business objects to do. For instance, you may well have a publish method on the service for publishing to a file so it is more than just data access (although I agree that most of what it does in most apps is CRUD).
You need to decide whether or not to have a Gateway. I personally use the DAO for all db persistence and it works well for me. The gateway is typically for collections of records (getAll(), getList(), etc.) and usually returns a cf recordset, but I prefer returning an IBO for both single and multiple records so I always have well encapsulated getters and setters. Also, I need a lot of getbyUniqueAttributeList() queries. For instance, I may want to getUserID(“12”), but in a multiple edit admin system I may want to getUserID(“2,4,6”). One is a single record and the other is multiple, but I find it more intuitive to have a single getUserID() in my UserDAO and to allow you to just pass in 1..n values in the list. I then use a private method in the DAO to handle the two different types of SQL (one needs IN clause) but encapsulate that complexity as that seems to me something that only the DAO needs to concern itself with.
So, I prefer to use DAO for all data access and that is pretty common in Java and other languages. Nothing wrong with Gateways if you find you need them, though. Some people find they get too many methods to be comfortable putting them into a single class, although a gateway isn’t the only solution to this (it is, however the most commonly used which is something in its favor right there).
There is also a choice as to whether the service or the business object should call the DAO. If you have gateways, it is a no brainer to me that service layer calls them as I don’t want to go to a single user object to ask for a list of users – that doesn’t make sense to me (although it is how Ruby on Rails works, so it isn’t wrong – it is just an extreme case of an active record pattern – bear in mind the service layer approach isn’t used much in Ruby and Python so it isn’t the only way to program). I use a modified AR pattern with composition of DAO in business object and use LightWire (subset of ColdSpring but optimized for performantly injecting dependecies into transients (in Spring they are called Prototypes) as well as Singletons) to inject the DAO into the object. Perfectly reasonable for you just to inject the DAO into the service layer, however, and that is currently the more common approach in the CF world.
I would think twice about using CS to inject dependencies into transient business objects. It is ABSOLUTELY CAPABLE of doing so (set singleton=”false”) but I have been told on a number of occasions that it is not really what CS was intended for and it just isn’t how Dave and Chris program – they use heavy service layers with business objects more like transfer objects which is a perfectly valid approach and mainly a matter of syntax when you get right down to it (that was something Dave Ross said to me and the more I think about it and work with it. The more true it seems to be).
I would start by using the service approach as outlined by Eric as there is no need to inject singletons into your transients unless you’ve tried it the other way and don’t like the syntax (I prefer ThisUser.save() to UserService.save(ThisUser) and especially love stuff like user.getCompany(“Co1”),getAddress() which either requires you to use Reactor or for you to inject either the UserService or the AddressService into your User depending on how you write your ORM methods). I’ll be posting a bunch on these approaches over next few days, and I’d recommend playing with things both ways and seeing how it works for you. If you are new to service layers, start with most of the API and knowledge in the service layer. Once you’ve got that down you can compare and contrast using a more active record approach and see what floats your boat!
Best Wishes,
Peter
On 11/1/06 12:08 PM, "Eric Knipp" <[EMAIL PROTECTED]> wrote:
If you look at some of the generated bean definition XML out of the code generator, you can see what it is doing with the dependencies.
The generator creates a {tablename}Service.cfc which is a bean for talking to the DAO and Gateway (you might think of it as a 'data access delegate'). This is the only piece of generated code that your business objects should consume. Hence, the business objects should not actually know anything about the DAO and Gateway, which keeps them decoupled from the persistence implementation - this allows you to sub in some other kind of database access layer without having to modify the business objects, all you have to do is change how your data access delegate works.
In order for the data access delegate to function properly it must have references to instantiated objects from the data persistence layer, being the DAO and Gateway. If you were to instantiate the data access delegate yourself, you would have to supply it with already-instantiated copies of the DAO and Gateway, or you would have to modify the delegate to create these objects itself. Neither of these is an ideal situation, the former because it requires your business object to know more than it should about the persistence layer, and the latter because it requires the service to know the mechanics for instantiating the DAO and Gateway (and presumably, the datasource information would have to be passed in somehow as well). This is where ColdSpring comes into play.
The ColdSpring BeanFactory will handle these kinds of messy dependencies for you, by injecting copies of your DAO and Gateway into the Service during instantiation. You can specify a datasource property that ColdSpring should use, and during the init() method of all the components it will resolve the dependencies for you. This way, none of your business objects have to know too much about layers from which they ought be decoupled.
The other thing you can do is even put simple bean definitions (where there are no dependencies) into your bean definition file, and ask a cached copy of the ColdSpring framework to do a getBean() whenever you would've created one yourself. This further abstracts the mechanism by which objects are created, and allows you to easily wire in dependencies if need be.
HTH,
Eric
On 11/1/06, Dan Vega <[EMAIL PROTECTED]> wrote:
I was wondering if anyone can help point me in the right direction. I have been writing cfc's since you were able to. I have never gotten into any type of design pattern. I am now using the Illudium PU-36 Code Generator to create my DAO's, Gateways, Service Layers, Colspring.xml etc... I am just getting into coldspring as well. I am just wondering if there are any tutorials out there that help explain how coldspring works and how it ties all of my cfc's toegether for each of my components. Any small apps would be great as well.
Thank You
Dan Vega
[EMAIL PROTECTED]
You are subscribed to cfcdev. To unsubscribe, please follow the instructions at http://www.cfczone.org/listserv.cfm
CFCDev is supported by:
Katapult Media, Inc.
We are cool code geeks looking for fun projects to rock!
www.katapultmedia.com <http://www.katapultmedia.com/>
An archive of the CFCDev list is available at www.mail-archive.com/[email protected] <http://www.mail-archive.com/[email protected]>
You are subscribed to cfcdev. To unsubscribe, please follow the instructions at http://www.cfczone.org/listserv.cfm
CFCDev is supported by:
Katapult Media, Inc.
We are cool code geeks looking for fun projects to rock!
www.katapultmedia.com
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
You are subscribed to cfcdev. To unsubscribe, please follow the instructions at http://www.cfczone.org/listserv.cfm
CFCDev is supported by:
Katapult Media, Inc.
We are cool code geeks looking for fun projects to rock!
www.katapultmedia.com
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
You are subscribed to cfcdev. To unsubscribe, please follow the instructions at http://www.cfczone.org/listserv.cfm
CFCDev is supported by:
Katapult Media, Inc.
We are cool code geeks looking for fun projects to rock!
www.katapultmedia.com
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
