I decided before Christmas break that I really want to learn a couple new languages. I decided on picking up Groovy/Grails & Python. Now obviously I am not going to pick this up in a week but over the next year I want to get good with both of these languages. This has nothing to do with ColdFusion, in fact my passion for CF is really driving this. It's important to understand how other languages tackle problems in case you find yourself stuck in a similar situation.
I have been doing a lot of work lately with Hibernate in ColdFusion 9. As I write more and more code I realized that I was writing the same functionality over and over for each of my domain objects. For the purpose of this article lets use an e-commerce application. When your working with anytime of application you must identify the domain specific objects in your system. In our e-commerce application we could have (but not limited to) the following objects.
Now we have an idea of what is going to make up my system I am going to start building out my domain objects and views. To keep it very simple I started out without incorporating any type of framework. For each of my domain objects I found out that there was common functionality between them. Here is a list of (but not limited to) some of the methods I need to perform on each object.
- New - I will create a new instance of the domain object.
- Load - I will load an existing object from a database based on the primary key.
- List - I will give you back a query of all the rows for this domain object.
- ListBy - I am a dynamic search that allows you to search by fields.
- Find - I will find rows based on hql
- FindBy - I am a dynamic finder that allows you to find exact matches dynamically (findByFirstAndLast("Dan","Vega"))
- Save - I will persist an object to the database.
- Delete - I will delete a row(s) from the database.
So now I have a list of methods that each domain object will use. On my first attempt I create a Service level component for each domain object. The real problem with this approach is that if an implementation of a method changes you need to change it in every domain object. Thanking along those lines my next stab at it was to create an abstract service level object that contained all of these methods that each of these domain objects would extend. Based on the entity attribute of the class my abstract service level would know which entity we would are working with. The great thing about this approach is our product service can now focus on the business logic that it needs to perform.
So that really started to work out well until I started looking at some other languages and really taking a like to how they approach it. My problem now is that I understand I need a place for this common functionality but I am not really sure on where it should go. Right now I store a static instance of the service level component in the application scope and then I can perform method calls like this. The
Lost in all of this is what a traditional service level object is used for. Typically a service level is used in an MVC framework to delegate common functionality. This helps us practice DRY (Don't Repeat Yourself) and cut down on controller bloat. In our e-commerce application we have an example of where this would fit. Say we need to email a invoice to both the customer and the store owner. We could create that functionality in 2 different places but that really does not make sense. The better solution would be to create an invoice service. This service component would have a send invoice method that accepts an invoice id and email address. Now we have common functionality in one place and we could pull that service level object into both our Store and Customer controllers. So typically that is what we use a service level for and I think that is what is causing some confusion for me.
There have been some suggestions that this functionality belongs on the domain level. While that approach works for methods like save and delete it really does not work for the others. I want to look at example of why that is. I have a product entity with a save and delete method that knows how to save/delete itself. Now when I create a new product and set some attributes I can just call save on itself. As we can see that works out great but if we sprinkle some of the other methods I talked about earlier in our domain object. Now I am going to add a new method list that will give me back an array of objects. Do you see the real problem with this? To get a list of products we need to create an instance of our product entity. We don't need an instance here because we are not working with a single instance object. The question I threw out there is what if ColdFusion had a static method? We then could call our list method on our domain object like this without ever needing to instantiate the product domain object.
I would like to conclude this article by emphasizing that this is not a why does one language do this and this one does not. ColdFusion certainly does some amazing things very well and other languages to take notes. I just find myself asking these types of questions when learning other languages. And with all of that said this was a very selfish article. I need to find out from all the smart developers out there on how I can solve my problem. I know I have common functionality that all my domain objects need and I know that each business object will have its own business logic. So where does it go?