Skip to main content
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Awdhesh Kumar
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Awdhesh Kumar ( @kawdhesh )

OOPhoto - Modeling The Domain In Steps (Round IV)

By on
Tags:

The latest OOPhoto application can be experienced here.

The latest OOPhoto code can be seen here.

When I first started modeling the domain for OOPhoto, my latest attempt at learning object oriented programming in ColdFusion, I had thought that it was such a small application that it would be finished in one day. Now, here I am like a week later, still working on the domain. This is definitely not an easy process and I can see why the majority of people stick with procedural programming. But, I feel like I am starting to make some headway.

Yesterday, I put a lot of thought into the Service layer of my application. I went through all the interfaces and I outlined what I thought would be needed by any View that used the application. I am now going to polish that off by defining the Service layer object and its methods. I am calling this the PhotoService.cfc. This is different than the PhotoService that I defined in previous steps:

PhotoService
--------------------------
+ CommitComment( Comment )
+ CommitGallery( PhotoGallery )
+ CommitPhoto( Photo )
+ DeleteGallery( PhotoGallery )
+ GetGalleryByID( ID )
+ GetGalleryByJumpCode( JumpCode )
+ GetGalleriesByKeywords( Keywords )
+ GetNewPhotoGallery()
+ GetNewPhoto()
+ GetNewComment()
+ GetPhotoByID( ID )
+ GetRecentPhotos( N )

Walking through the Add / Edit photo gallery page is the largest pain point in my application. Since the photos for a gallery are uploaded outside of the PhotoGallery creation (via AJAX), there needs to be a way to create and save a Photo object before we even save the new PhotoGallery object. This is why I added the GetNewPhoto() and CommitPhoto() methods. This will allow me to save photos that are not associated to a gallery just yet. Clearly, this will be the trickiest page in the whole application.

But, adding a photo without a photo gallery is just one example of another issue that I don't fully understand - saving data that is usually composed within another object. The photo Comment is another such example. On the photo detail page, I can add a comment to the system. To do this, I am going to be using the GetNewComment() and CommitComment() methods above; however, the Comment object has a pointer to the Photo object to which it is associated. So, does that mean that in order to save a new comment, I have to create a Photo instance, store it in the Comment, then commit the Comment?

This is a problem for which I keep having to slip back into a procedural mindset as I cannot visualize it in OOP terms. I am like 80% satisfied with the service layer at this point; I am not sure that I will be able to design anything better until I can wrap my head around the above problem.

This problem is my biggest constraint, and I think one of the largest hurdles in getting past my procedural mindset. I am not sure I can move on until I fully understand these kind of scenarios.

Reader Comments

36 Comments

Ben,

I feel your pain. Modeling is hard and annoying at times.

Sometimes the best thing you can do is to go ahead and bang out a section the fastest way possible, and once you have it working, refactor it into OO.

Sometimes, when I am on a rapid development phase, I start with a CFM page, put a query at the top and bang out the code. As I see fit, I wrap bits of logic here and there into functions. Then, when the functions (behaviour) are easily organizable into objects, I wrap them into objects.

Some painters believe in getting the vision fully in mind for what they want to paint. Then once they have the painting all mapped out, they wet their brush, then start to implement the painting.

I rarely do this. I am the second kind of painter, the one that has an inspiration, wets the brush and lets the design unfold as it goes.

Remember Ben, The Perfect is the enemy of The Good.

DW

15,640 Comments

@Dan,

This application is simple. I know that I could rip it out in a few hours without problem. But, that is in the procedural world. My goal right now is not to build the application, but to get a better understanding of OOP. I am afraid that building it so that it works and then refactoring to OO as needed is not going to teach me the principles effectively.

I understand and fully believe that perfect is the enemy of good, but I am looking to build core understandings at this time; I can take that and then optimize as needed later on.

This idea of having to create data that is composed within another object and before the parent object is created I think is an important problem to diagnose and fix; I believe that it will come up time and time again.

38 Comments

@Ben, I've been following your posts with interest as I'm also taking slow, painful tentative steps in the OOP world.

I can see that the issue of adding a photo without a photo gallery is a major headache, but then again it's not the simplest thing to do in procedural code either - you're just used to it (what if the user uploads a photo and then fails validation - do you still store it? Where do you store it? In the database/file system/both? etc).

My point is not to get too frustrated with it (the CF community is behind you!). You must have had issues in the procedural world which you've overcome and now you're a certified developer with a lot of respect and credibility :)

I tend to use a blog as a good way to test new ideas and concepts as it is pretty basic, especially if you forget about adding/approving posts and comments. I don't think I'm at your level of CF guruness so can't offer any advice but hope this comment makes you feel a bit better!

36 Comments

Ben,

I hear what you are saying man, I really do. I'm just making the point that you might be struggling with too much unknowns right now.

The danger is getting caught in an endless thought loop. Does this cart come before the horse? What is a cart? What is a horse? If a cart is behind a horse, how do I know?

Any rational person could look at a horse and a cart and tell you which was where. If you can bang this out in a few hours procedurally, then go for it. Having a working application will help you work through the little implementation details that you are going to have to work through anyways. Then once you have something concrete, you can work on just the OO part without muddying the waters.

I know it seems like it is contrary to your goal, but I think building a working model (a prototype if you will) will go a long way towards helping you focus only on the OO part.

Take it or leave it...

DW

116 Comments

I think if you step back a bit you'll see this isn't really that complicated, Ben. Assuming I understand correctly, it's the same idea as: what if you let the user upload photos to a pool of photos, and then later let them create a Gallery and drag and drop photos from their pool into the Gallery? The same logic applies.

I'd probably save the Photos as they are added. When the user saves the Gallery, part of that information would be a list of photoIDs that are associated with the Gallery. Then, when you save the Gallery, you know which Photos to associate with it. (Under the hood this would probably be a simple link table that matches GalleryID to PhotoID).

Would that make sense or am I missing something crucial in the way you want to handle these?

3 Comments

Ben,

Can't you you create an empty photo gallery (it would still have an ID wouldn't it?) and load the photos into it and persist the photos separately from the photo gallery, then finally persist the photo gallery after some additional user definition? Maybe I'm not thinking in an OO way, but I don't see the problem.

55 Comments

Hi Ben,

From someone who's tried this both ways, let me strongly support Dan's statement. Hack out the simplest thing that will possibly work (procedural is fine) and then refactor to OO.

I understand your goal isn't to have a working app per se, but by starting with a working procedural app, you can refactor step by step to a more OO approach by using OO to solve specific problems related to the application.If you wanted to hack together a procedural app and then bring it over Thursday we could pair refactor to a more OO approach if that'd be of any interest. Let me know!

10 Comments

I think this particular problem can be managed by separating the model from the implementation. Looking at the model, all you're doing is creating objects (photos and comments) and associating them with other objects. As far as business logic is concerned, that's all you need to know!

Take the use case of uploading photos asynchronously before saving a gallery. Anytime the user uploads a photo, you create a new photo object. If you are adding it to an existing gallery, go ahead and add it. (Your service layer needs a method for adding a photo to a gallery, right?) If the gallery has not yet been created, just hang on to the photo object for a while.

Sure, there's some fun implementation challenges here, like storing the images until the new gallery is committed and deleting photos if the gallery never gets created. But your model should be blissfully ignorant of such details. Your implementation might change many times, but it shouldn't affect the basic business rules of how photos and galleries behave.

Similarly, changes to your objects and their relationships shouldn't affect the API exposed by the service layer.

4 Comments

I agree with Seth. I think photos that are uploaded without a gallery are in the default gallery. The default gallery should still have the properties of a gallery (photo order, etc.) but without the user naming it i suppose. As far as comments go, I don't see why a user would be trying to submit a comment without being associated with a photo.

15,640 Comments

@All,

Maybe you are right. Maybe I should just get this working old-school style first, to at least have something that proves the viability of the application itself. Then, I can take that and start to refactor the code into more object oriented stylings.

I just get nervous when I think about this because I don't want to get lazy and start to err on the side of procedural code. Plus, something like this might fly on a small application, but what about a huge application that is much more complicated? I can only assume that OOP best practices have to be applied from the beginning?

But, that being said, I do need a bit of an upper right now since all this OOP domain modeling had me frustrated. I'll see what I can get done quickly and with the least amount of effort.

15,640 Comments

@Dustin,

As far as the comments were concerned, I kind of didn't explain myself very well; I didn't mean to imply that comments would not be associated with a photo. The point I was trying to stress is that comments are created after the Photo is created (totally separate actions). However, the model I had in my head had Comments as being aggregated by the Photo object. So, my question was - to save a comment, do I have to stuff it in a Photo object and then commit the whole Photo? Or, can I just save the commend independently of the Photo (using just the Photo object reference inside of the Comment)?

In procedural code, this is wicked easy - I just pass along a Photo ID with the comment values. However, I am so nervous to touch IDs in OOP because I keep thinking that that violates the OOP ideals (shouldn't I be passing objects rather than IDs).

15,640 Comments

@David,

It was this concept that I was having a lot of trouble with:

"If the gallery has not yet been created, just hang on to the photo object for a while."

Hang onto it where? They are different page requests. Are they hanging out in the Controller? Brian Kotek mentioned that the session would be encapsulated in the Model. Would I need a way to tell the Service Layer how to hang onto the non-persisted object? Stress :)

55 Comments

Hi Ben,

I think throwing together a working procedural app is a good choice. It's important to remember that there is no "right way" to do OO. There are patterns which are proven solutions to recurring problems, but unless you have a specific problem, you don't need to use a pattern to solve it.

By refactoring to OO, you can look at specific problems with procedural code and solve them one at a time. Now, for the scope of the current app, you may not HAVE those problems, but as long as you can see where those problems would develop if the app was a little bigger, you can apply those refactorings understanding the specific problems you're trying to solve rather than just trying to come up with a set of rules divorced from the coding problems they're trying to handle.

A good example to me is the comment question. A very common OO approach to that would be to load the photo, and then photo.addComment(Comment). Personally I just use the identity of the Photo as part of my adding of a Comment and I haven't come across any problems in my use cases to date where that didn't work more simply and elegantly than the alternative.

The fact that something is "more OO" (whatever *that* means), doesn't make it a better solution to a problem. The fact that something makes your code simpler, more elegant, more maintainable, more loosely coupled - those are the things you're looking for. But until you're working on a specific use case it is really hard to have those discussions, so it often makes sense to develop the architecture along with your scenarios.

You are right that if you are building a huge app, you probably don't want to code the whole thing procedurally and then refactor it to OO. But that's why you're doing this with OOPhoto - not with a port of EBay to ColdFusion. As you build more apps you start to notice patterns and "the simplest thing that works" might become a little more complex, but it'll have grown from your experience.

I tried writing an app from basic principles without my framework a little while ago and within a couple of hours, I just moved it to the framework, because there were so many patterns it implemented that were driving me nuts having to build them all from scratch again. I wouldn't consider writing a non-trivial app without using LightWire or ColdSpring. I wouldn't consider not having separate model-view and controller. I wouldn't (usually) consider not having service classes and DAOs. I wouldn't consider not having a certain set of base classes with methods I depend on. I wouldn't consider not loading my views from CFC's so that I could take advantage of DI of service classes where my views needed direct access to them. I wouldn't consider writing an app where I didn't have custom data types, some kind of metadata infrastructure and a way of generating or synthesizing most of my code, so clearly if someone came to me tomorrow to get an app, I wouldn't write it procedurally and then port it once I was done.

But that doesn't mean the above patterns are right or appropriate. They have consistently worked for me, I know the problems they solve and have a pretty good handle on the trade offs of each, and I only came up with the "good" patterns (for me) by messing with a bunch of others that didn't work for me. If you don't have a set of OO patterns that you've personally used successfully and that you're very comfortable with the strengths and weaknesses of, you probably aren't ready to be architecting large scale OO apps just yet. But a couple more OOPhoto like projects where you start simply and then refactor to better designs as the code dictates and you wouldn't even be able to conceive of writing your apps the old way. Just all part of the learning experience (or at least how it's working for me . . .)

4 Comments

I think the Comment would only have get/set functions fo the most part. I think the Photo should have the functions to get all Comment objects associated, or save/create another Comment object, passing along its PhotoId and the comment.

I think you can have Photo functions controlling the Comments because the Photo contains objects of type Comment, Comments are not a decendant of Photo.

132 Comments

@Ben

There's really nothing wrong with having some kind of resource that exists on the file system and thefile name is being stored in the database. Of course don't store things like C:/WebServer/ etc. ;)

This isn't bad practice at all, in fact it's quite common.

<a href="/presentations/#conference.getPath()#/#topic.getPresentationFileName()#">Presentation Download</a>

The conference knows about what unique identifier should be used, in this case it's the year. The topic knows what it's presentation file is called, but not where it is.

Sure we can do all kinds of extra abstractions around this, like adding a service that takes a conference and topic and returns the proper path, or any number of other things, but start simple. Never ever add more abstractions than you need to start. You can refactor this later if you have issues, but it's more important to not over complicate things before it's actually necessary.

Remember: Working Code > Perfect OO Code... which you'll never write anyway, no one can.

36 Comments

I want to be able to deadlift 585 pounds. I saw a bunch of people at the gym doing it and it looks easy. Load up 12 plates on the bar and just lift...

Why bother to take it a step at a time, I told myself. I don't want to lift just 200 pounds so why bother. I want to lift 585 so thats what I did.

I'm writing this post from the doctors office. He's just told me my spleen fragmented and I have a hernia the size of a small country. Lifting weights sucks.

I'm being a little silly, but there is a point somewhere in there....

140 Comments

@Dan,

Wow, way to hit the KinkyMan right where it'll impact ...

[grin]

At least the path to increased lifting is somewhat predictable ...

132 Comments

(Thanks for fixing my formatting Ben, that was supposed to be on the other post in response to the Image stuff, confused myself with lots of open blog tabs! sorry.)

@Ben

I think you're making this much more complicated than it needs to be, and getting stuck because you're trying to do OO the "right way", which there isn't one, as Peter Bell pointed out.

I've been watching as you've been working through this stuff and I think you're getting very confused because you're doing the application design backwards. I've seen many developers get stuck here.

In my opinion, thinking about things in terms of "persistence of objects" is a HORRIBLE idea. The database is very very good at what it does, storing data. It can relate information, has features like triggers, procedures, joins, foreign keys, and all of that together makes a good, fast, and maintainable database.

A lot of time I see people doing "OO applications" thinking that now that they're doing this they need to think in terms of "persisting objects" into the database. So they start by writing up object definitions and methods for CF components. This is, however, a terrible idea. This is why ActiveRecord (part of rails) handles all kinds of things that should be in the database right in ruby code, because the developers don't use the database the way it was intended. And then it's slow...

Objects should have behaviors that deal with the data that they represent, and you can figure all this out after you've designed the database.

To bring this back to this issue, instead of thinking about this in terms of "objects" in ColdFusion, go start at the database instead. Write up a proper database for what your application needs, don't even think about what needs to happen in CF, think about what your application is going to do, and what kind of data it needs to store, and how that should link together.

Once this is done, then create objects that represent that data stored there, and finally design services that coordinate behaviors between objects, or complex tasks. Once your database is designed properly, figuring out what behaviors you'll need and how to write them is trivial. If you need to, write it procedurally, and then factor it out into objects piece by piece.

There's nothing wrong with this from an OO perspective, the database stores information, your objects provide behavior around that information. The database does *not* store the objects, it does *not* persist them. The objects *represent* data stored elsewhere. They're merely collections of behaviors around this data. Some of that behavior might even be represented in stored procedures! But to your app you can just have a method like findAssociatedCogWheels(). You can figure this out after you've designed the database. And you'll figure your service layer out after you've figured out your models and UI.

The idea of a relational database is not procedural, it is not out dated, and is not in any opposition to writing an application in an OO manner.

So yeah, don't stress about passing an ID to a method, or where some data should be, do what makes sense for the database, do what makes sense to you. You can always fix it later if you decide it's better to do something else! :)

7 Comments

"However, I am so nervous to touch IDs in OOP because I keep thinking that that violates the OOP ideals (shouldn't I be passing objects rather than IDs)."

@Ben,

I totally agree! Whenever I try doing OOP, I get caught up thinking I need to do it "the right way." And I'm fine until I start encountering relationships.

Like you said, I get to a point where I'd usually use an ID. And I've seen "OOP" code elsewhere using IDs. But in my mind, I feel like I'm supposed to be passing objects around instead of IDs (and referencing objects instead of IDs).

It seems like if I'm passing around IDs, then I've "broken" the OOP I'm attempting and just started mixing procedural and OOP. Then b/c I don't have time to figure it out on that project, I give up until the next time I can try it and just assume I don't understand.

There's one thing bugging me related to this. When I look at examples, I see people using the CFC's variables.instance approach and then having a GetInstance() method to return variables.instance. My problem with this is unless I return Duplicate(variables.instance), then I'm just passing a reference which could result in my CFC variable instance variables getting modified directly (instead of via methods).

However, once I start having objects nested with objects (which I feel like I'm supposed to be doing in OOP), then I can't use the Duplicate() function, b/c it can't duplicate objects.

Like I mentioned above, with OOP I thought I should have objects referenced within objects instead of using IDs. But that doesn't work with the Duplicate() method, so I feel like I must be missing something.

132 Comments

@Rick

There's not anything super wrong with passing an id around. Sometimes this is the best and most efficient means to deal with different kinds of data. For instance, if you have an ProgramManager object (like the person), and that object represents the current manager who's logged in. And they choose a user from a drop down list to add as an employee managed by this manager.

Now we could do a query to bring that selected person into the application as an Object, potentially brining in a lot of other information, and then we could call some method that takes a Person object and associates it with the manager, and then save this object back, doing another query against the database.

Or, since the drop down list has <option value="{PersonId}">{PersonName}</option> we could just call some service method that associates that person with the passed in manager with a single query.

Neither approach is more correct, except the first one ends up being slower and uses more memory.

That brings us into complex caching layers to try and help solve this "issue", which isn't so much an issue of any kind of OO design principle, but rather an issue with the way we designed the application. Making things more complicated than they needed to be in the first place.

(Yeah, there's lazy loading and proxies too, but that makes all this even more complicated...)

We don't really gain anything by requiring passing an object. If you already had the object, you could just get it's Id anyway to make the method call. You could also have two methods, one for each way to abstract away how this thing works.

All we get is some false sense of abstraction at the expense of slower pages, more memory usage, and more complex applications.

(I'm not knocking stuff like Transfer, I love the framework, I'm saying use the right tool/design approach for the job!!! :))

55 Comments

@Elliott,

I agree that if you are getting completely confused by objects, starting off the a db design and a procedural application will get you a starting point that works from which you can refactor to a more OO solution to solve specific problems.

With that one caveat aside, starting an application design from the database schema is usually a really bad idea from an OO perspective, and if you ask most people with extensive experience of designing OO applications, they start with a rich object model with behaviors and attributes and then come up with appropriate solutions to persistence concerns once they've fleshed out the object model.

Objects are nothing to do with just providing behavior around tables, and if you start from that perspective you will not end up with the best application design. I think this is something that is pretty well universally agreed by experienced OO architects. Don't let a data model drive your object model. You'll often have things like value objects that represent a few columns within a given table, you'll have objects that are persisted across multiple tables and you'll have different types of objects sharing the same database table. The object model for a non trivial app will end up being very different to the db model and the purpose of ORMs is to handle that impedance mismatch. The purpose of an experience OO architect is to handle first the object model and then the db model and ensure that the mapping between them does't stop the application from meeting non-functional requirements such as page load time under load.

7 Comments

@Elliott,

Thanks for the suggestions. I think I may have worded my thoughts incorrectly, though. (I'm not attempting OOP currently, as I'm in a "give up and wait for next time" phase. :)

I think I did have some code passing around IDs (as arguments to method calls). I guess what I'm referring to more is having an object property (or instance variable) that needs to reference a foreign key.

So let's say I have a Person object and the person has an address. Would my Person object have an 'address_id' property, or would it have an 'Address' object stored as a property?

And I chose addresses as an example, b/c what if they have three addresses stored? Do I use an array to hold each address id, or do I hold an array of Address objects?

I feel like I should be storing objects instead of IDs here, but doing this always seems to be where I run into issues.

Does this also apply to what you were saying?

55 Comments

@Rick,

In your example, from an object modeling perspective, your Person has-an address. That is it. How you choose to persist the relationship is a completely different concern and not necessarily something your object would know about.

If it is a required has-one relationship, you'll probably have a tbl_Person.AddressID column although you might use a joining table. However, in your object, if you call Person.getAddress() it'll probably return an Address business object. The implementation details will vary depending on whether you're using and ORM and if so, which one. I have a general getAssociated() method that uses metadata to call the appropriate service class, so my Person.getAssociated("Address") will look up some metadata, see that the Address is managed by AddressService and will pass the appropriate ID to the AddressService.getByID(ID) method which will return an Address object, but use something like Transfer or Hibernate and this will all be taken care of for you seamlessly.

The important thing to realize is that the relationship between objects and how those relationships are persisted are separate concerns and all often be managed separately. Start with a good object model and then figure out an appropriate means of persisting the relationships based upon the requirements.

7 Comments

@Peter,

Thanks. So would it be appropriate then if I createe variables.instance.AddressID to store the address, but then had Person.GetAddress() return an Address object?

In that case, I suppose the GetAddress() method would simply use the variables.instance.AddressID value and pass it into the Address.GetByID() method (or some similar method).

I think I've always been caught up in assuming that whatever I wanted my GetAddress() method to return, then that's what the underlying instance variable had to be holding. If I do it this way, then I can store the simple ID, but return the actual object? Is that correct?

If so, then this makes much more sense to have a more intelligent method actually find and get the object to return, rather than a more basic method that simply returns a stored value.

Does this make sense? If so, then maybe this was the awakening I needed! :)

140 Comments

@Rick,

Not to further complicate things, but another reason to design from the OO model outward (rather than from the DB), is that the persistence may be variable as well. As noted elsewhere in these posts, the view may be in HTML or it may be in Flex, and the OO model should handle either one, whether via a Remote Factory or whatever is needed to translate objects into HTML use or Flex use. Similarly, the model should be able to exist separate from the decision about persistence, which is where Ray Camden started when he prompted Ben to think about working with Service layers. This approach would allow Ray to leave Ben's Service layer untouched while building a parallel layer, for instance using Transfer or some other ORM hooks.

A simpler example might be that the Service objects could be refactored to persist data in XML files, to be used in an AIR application for instance, instead of a database without changing anything in the domain model. Again, goes back to what Hal Helms has always taught about having Objects know how to "do" things, rather than thinking of them as 1-to-1 interfaces to database tables.

Sorry for the run-on ... tough thinking clearly at the end of the day.

55 Comments

@Rick, The easiest solution would be to let Transfer or Hibernate do the heavy lifting, but yep, you could potentially store the ID in the instance variable and in the Person.getAddress() method write something like:

return AddressService.getAddressByID(variables.instance.AddressID)

You need to make sure the Person object gets the AddressService injected into it by ColdSpring or LightWire, but basically you should be good to go.

@jfish, Rather than a service class or a business object, you'll probably want a DAO to handle the persistence concerns. If you have multiple possible persistence strategies, you may have multiple different DAOs for a given business object and an abstract factory that returns the appropriate DAO to the business object or service class using it depending on the relevant runtime parameters.

116 Comments

I don't want to throw cold water on the discussion here but I'm afraid that it is spiraling out of control. There is a lot of interesting stuff coming up here but I'm not sure how much of it is actually helping Ben at this point.

I'd say lets try to keep things as focused as we can on helping him and answering his specific questions. For expanded or tangent discussions, maybe CFCDev would be a better location? I'd love to keep this going but at a certain point trying to keep track of who is replying to whom in blog comments gets pretty difficult. In the mailing list we can actually break things up into separate threads, as well as opening it up to a potentially wider group of people.

Ben, I think once you actually start trying some of this stuff out you'll see more quickly what works and what doesn't, and where some of the approaches being discussed here can fit in. But trying to wrap your head around all of it at the same time (especially conflicting versions of the same advice) is really not going to work.

Anyway, just my 2 cents. Ben can feel free to tell me to mind my own business hehe but I was already worried that it was becoming "opinion overload", and I'm even more concerned now. Let the man breathe! ;-)

140 Comments

@Peter,

Yes, thank you for the clarification, that's what I was trying to get to. I think I ended up lumping everything below the BOs into the 'Service', but I was definitely thinking of a perhaps a Service factory selecting among one or more DAOs. Ah, the day's end ...

132 Comments

@Peter

"With that one caveat aside, starting an application design from the database schema is usually a really bad idea from an OO perspective, and if you ask most people with extensive experience of designing OO applications, they start with a rich object model with behaviors and attributes and then come up with appropriate solutions to persistence concerns once they've fleshed out the object model."

I strongly disagree with this. When you only consider the database a "persistence of objects" in the application you completely ignore the fact that the database might be used by more than one application, possibly written in totally different languages, possibly over a very long time.

This leads you into ridiculous strategies like handling validation logic totally in the application and not adding constraints to the database. Not adding foreign keys and proper views, using stored procedures when they're appropriate, cursors, or other features that the database was designed to do, and do well.

People start doing things like looping over object collections to delete objects from the database, doing tons and tons of queries when a single DELETE query with a IN clause could have done it hundreds or times faster, and would have scaled much better. Or dragging in tons and tons of objects, one query at a time, instead of doing a single list() query that would have given you the information all at once.

The database is *not* object persistence. This is not what it does well, and this isn't how it should be viewed.

And certainly some objects represent more than one table, you might have objects that represent views, or the result of stored procedures, or queries, or files on the file system, or any number of things, but this is specific to the application you're designing.

Viewing the database as just some object persistence, ignoring what it does well, and thinking only in terms of application objects is why, for instance, ActiveRecord is so slow, and why Twitter crumbled after it got real load on it.

Try reading this...
http://thedailywtf.com/Comments/The-Mythical-Business-Layer.aspx?pg=5

I'm sure it'll light huge fires in the "OO is way!" crowd, and I know it'll probably bring all kinds of hell on me, but it speaks to a real issue. An application that puts as much work down in the database as possible will be faster and scale better than one that tries to reinvent the wheel in the application logic because the database is being viewed as some kind of "abstract persistence" layer for objects.

And yeah yeah, bring on the "persistence might change in the future" arguments, and bring on the "but abstraction is better arguments". This doesn't mean your application needs to use tons and tons of RAM and processing power to do simple tasks that the database should have done.

The database server in general will be hundreds or even thousands of times faster than your application level logic could ever be. It'll use less memory than your CF application will, and can store the data in a WAY more efficient manner (indexed, clustered, paged, partitioned and cached).

Is ORM evil? Nah. But trying to shoe horn object persistence and objects as the solution to everything is a massive anti-pattern.

132 Comments

And seriously people, what's with being afraid of writing SQL queries and using the database as it was intended?

Some very very smart people, probably much smarter and more knowledgeable than any of us on this thread about performance and scaling wrote these database engines.

Don't redo all their hard work because you want "abstraction." :/

15,640 Comments

@Brian,

Definitely a bit of overload :) But then again, it is the end of the day.

@Elliott,

I like your point of view. I have never considered the idea that the database itself might be used in different applications with different touch-points. Although, since I still so data-centric, I am not sure that it would change the way I design my database tables... but I like your point.

15,640 Comments

@Elliott,

Just to touch on the idea of writing SQL - one of the biggest leaps in my head has been to go from writing rather large, complex SQL queries, that I pretend are very optimized in terms of compound JOIN clauses and conditionals into an OO-driven SQL interface. While I have only thought about this, it feels like it will be much harder to write optimized, highly efficient queries when dealing with objects.... I might be way off, but it is a fear of mine.

55 Comments

@Elliott,

OK, as usual the disagreement comes from the use cases. One class of use case is where you have a business intelligence initiative to build long term data assets that will add value to the business and that happen to have some applications that use them. In such a case, the design of the database schema can be much more important than the design of any given application that happens to pull from it (although I'm seeing more SOA style initialitves these days with a single application "owning" a given data source and providing a web service API for interacting with it). You're also assuming that performance is more important than maintainability which can also be true in certain cases.

With many web applications, a given database primarily exists to facilitate that application and has a lifetime similar to that of the application (no doubt the data would be exported if the application was killed but the db schema including consraints and the like would probably not survive untouched). With many web applications, the critical dimension is the ability to build a quality application quickly and to be able to continually refine and refactor the application in which case maintainability may be more important than performance. Most web apps aren't twitter and twitter was a really bad use case for Rails. Also, most ORM's aren't ActiveRecord - Hibernate is much more sophisticated in terms of the kind of applications it can support.

There is no question that with the current state of the art, hand optimized SQL will perform better for plenty of use cases that that generated by (say) Hibernate. But for many web applications, that simply isn't as important as the productivity gains and maintainability improvements you get from starting with a rich object model. As with anything, it is a trade off depending on the specifics of your use case.

.

55 Comments

@Ben,

> While I have only thought about this, it feels like it will be much harder
> to write optimized, highly efficient queries when dealing with objects....
> I might be way off, but it is a fear of mine.

And why is that important to you? I notice you allow Java to do garbage collection for you rather than dropping down to C++ and using malloc's. Given your better knowledge of the application, couldn't you optimize the garbage collection for your specific app and minimize the memory footprint out your apps on the server?

If you have a credible reason to believe that the db is going to be a performance choke point, you're dealling with large data sets, complex joins and/or really high loads, it obviously makes sense to optimize the performance of your queries. If you DON'T have that need, it isn't being thorough - it's premature optimization.

Of course, if you have the choice between writing good and bad SQL in about the same amount of time, write the good SQL, but if you can generate almost all your SQL in very little time (whether using a generator or a framework like Hibernate or Transfer), do so (unless you KNOW it's going to be a problem), and then if your load testing reveals problems with specific queries, hand write those few queries that are a problem rather than hand writing all of the queries that won't materially affect the ability of the app to meet the agreed non-functional requirements.

Talking about performance before you have a good reason to expect there to be a problem is usually a case of locally optimizing (the performance of the code) at the cost of globally sub optimizing (by blowing time fixing problems that don't matter instead of delivering quicker, cheaper and/or more features to provide more business benefit).

I was at an IASA meeting in NYC last night and one comment really struck home. An architect shouldn't be proud of their technology (at least primarily). They should be proud of the business value that their technology enables. It's not a bad rule of thumb!

15,640 Comments

@Peter,

You make a good point. Right now, I am only talking from my gut, not from my experience. Plus, I think what is important to remember is that "reporting" style queries are very different from CRUD-style queries. A "report" style query may join many tables with lots of conditionals and optional joins depending on certain criteria and optionals conditions based on joins, etc.

CRUD on the other hand is fairly straight forward.

Although, some things that I get hung up on sometimes is the cross-table integrity. For example, let's say you need to select a contact; but, in order for a contact to be valid it has to have (for example):

- contact record
- contact_information record
- registration record with non-expired registration

I guess when you load the Contact object, the query that gets the initial build-data can still create a query with those joins to ensure that the record is valid?

Or am I over thinking it? What I don't want to do is end up allowing the user to pull a Contact record that does not have a valid registration simply because I only queried the contact table.

55 Comments

@Ben, As Brian said earlier, lots of cool comments here, but we're covering too much ground in terms of you getting a working app.

Code something simple and then you can look at the issues and patterns to resolve specific problems. There are a number of ways of handling the question you asked, but once you have working code the various approaches and their trade offs will make more sense.

15,640 Comments

@Peter,

I got most of the code for a procedural-style application done last night and this morning. I hope to have it completed and posted by the end of the day.

55 Comments

Sweet! We can play tomorrow. FWIW, I'm actually doing exactly the same thing right now. I'm rewriting my framework and have given myself two weeks to get it sorted from first principles again. I just wrote an index.cfm with "hello world". Then I added a simple statement that includes the /controller/#action#.cfm file. Next I'm going to add a query at the top of a page, then refactor to a service class, then refactor to base classes and metadata, etc. Sometimes starting over from the basics with a real world scenario is just the best way to go . . .

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel