During my recent presentation to the IECFUG, one of the parts we didn't really have time to touch on was a diagram I created of my general model architecture.

The diagram is below, and contains a good number of comments to explain the different pieces. Given the recent discussions going on in the blogosphere and on the lists, I thought this might be interesting to some folks. If anyone has questions, please feel free to ask and I'll do my best to answer. Comments are welcome as well, of course.

Note that this is really just a starting point, and that I tweak this base setup as necessary, depending on the project and the specific problem being addressed. But I've found that, in general, this kind of setup provides me with a lot of flexibility and is actually quite easy to create or even generate. If one chooses to look at ColdSpring and/or Transfer, much of this can be generated automatically and then tweaked as necessary. But even if those frameworks aren't suited to your development approach, each of these pieces is relatively straightforward to create.

Comments Comments (13) | del.ico.us del.icio.us | Digg It! Digg It! | Linking Blogs Linking Blogs | 6519 Views

Comments

  • # Posted By Jason Dean | 7/16/08 12:43 PM

    Thanks Brian. I found this to be very helpful in visualizing some of these concepts. It also helped me better understand how to visualize some of my own ideas with UML. I still have a lot to learn, so mid-level posts like this are really helpful to me. Thanks.

  • # Posted By Tony Garcia | 7/16/08 1:58 PM

    Ditto what Jason said. This is very helpful. I notice you use just a Gateway for your DB interface instead of both a DAO and a Gateway. I've been leaning towards switching to this approach myself. How do you handle security for requests that don't come through the remote proxy?

  • # Posted By Brian Kotek | 7/16/08 2:53 PM

    @Tony - I'll either send everything through the remote proxy (including Controller calls to the model), or apply a security AOP advice to the actual service. Generally I only want to apply security to the requests coming from outside of the model, so using the remote proxy is a good option. Otherwise, one may incur the overhead of checking security for any internal model calls to other services, which I usually don't want.

  • # Posted By Tony Garcia | 7/16/08 3:13 PM

    Gotcha. I later read your notes on the diagram a bit more closely and realized the answers were right there in front of me! Sorry for making you repeat yourself. That's an interesting approach, as I always thought of HTML controllers usually accessing the service layer directly and remote proxies were only for things like Flex or AJAX calls. But I see how your way makes a lot of sense. I'll have to keep it in mind when I start doing more Flex and Ajax stuff. Again, thanks for the great post.

  • # Posted By Mark Mandel | 7/16/08 6:14 PM

    Nice one Brian, this is pretty much how I'm setting things up these days as well.

  • # Posted By Bob Silverberg | 7/16/08 8:44 PM

    Thanks for the picture Brian, it's worth a thousand words, as they say. I'm glad to see that it's fairly similar to what I'm doing. I've just got a bit more logic in my service layer, but I'm moving away from that. I have to admit that I'm a bit lost by the whole Remote Proxy thing, not having touched Coldspring AOP yet. I understand that even calls from your HTML controllers access the remote proxy, rather than the service itself, but I'm not sure I understand why. If you have time could you walk through a couple of use cases that show the reasons that there are advantages in doing it this way?

  • # Posted By Yanic | 7/17/08 3:01 AM

    Some tips to improve your diagram :
    - drop the aggregation symbol, it has no standard meaning
    - multiplicities on those associations might be useful
    - remove the attribute compartment if there are no attributes.
    - a well-chosen sequence diagram or two might make most of those big comments unnecessary.

  • # Posted By Brian Kotek | 7/17/08 11:22 AM

    Thanks Yanic. You have to understand that this diagram isn't meant for a hard-core UML person. Most people understand the aggregation symbol as representing a composition relationship, so that is why it is there. Using multiplicities or a sequence diagram is also not where I wanted to go. My target for this is people who know a but about class diagrams but not necessarily more involved UML notation. You're right about removing the attribute area, I'll try to remember that in the future since it doesn't apply here. I appreciate the input.

  • # Posted By Elliott Sprehn | 8/14/08 4:54 AM

    Keeping the factory separate from the gateway is really bizarre to me a this level. Seems like it'd be better to wrap that into the Gateway instead to keep those responsibilities together. They're quite dependent on each other anyway. Certainly you can't getByFilter() in the gateway without transfer present, so I think create() should really be there too.

    To be honest though, this level of abstraction is really quite overkill for a huge number of applications from small to medium in size. Using transfer directly in the Services or keeping most of the logic in the business objects themselves does just fine.

    This kind of (very) complicated solution has it's merits, and I've certainly seen places where it solves a number of problems, but it also does wonders to make application design much harder for small applications and really scare new developers away from OO.

    That said, this diagram is lovely and really communicates this model of design so much better than all the mailing list posts and blog entries I've seen combined. I'll definitely use this as a teaching reference.

    I do wish there were more voices about other approaches to application design in the CF community. This really isn't the only (right) way, we just don't hear much about how Django, Pylons, TurboGears, Rails, the various php frameworks, and even other CF based apps are designed very much. At least not on this side of the fence.

  • # Posted By Brian Kotek | 8/14/08 8:57 AM

    Elliott, the Factory is separated out because I didn't want this to be specific to Transfer. Any Factory will do, many people just happen to use Transfer as the factory for their business objects. One can certainly have a getByFilter() method in their Gateway without Transfer. It's just a method name. You could code it yourself to do anything you like (and many people do).

    Some might think it is overkill but I really don't. Moving Transfer into the Gateway is not really any extra effort since you need a Gateway anyway for normal SQL. And keeping logic in the business objects is something I would do regardless of project size. I suppose the bottom line is that it really isn't a very complicated solution. There are a number of objects in use but no one of them is particularly complicated. In fact, many of them are very simple or are things that I usually don't even have to write at all (like the Remote Proxy or the Factory) because they are part of other frameworks that I use.

    Still, I'm glad you think it is a decent overview of how one might approach at least a fairly large or complex application (which I suppose is what I generally am working on so I could be biased). Thanks, feel free to use the image as a reference!

  • # Posted By Elliott Sprehn | 8/14/08 12:53 PM

    @Brian

    It seems I was confusing because I mentioned Transfer. InvoiceGateway.getLineItem() / newLineItem() makes much more sense to me than using some kind of external factory for creating objects. We're putting the save/ delete/ and update behavior in the gateway, so throwing in an artificial separation with a "factory" to do create doesn't make sense, at least to me. Sean has also said before that having separate DAOs and Gateways doesn't always make sense. Though you don't really have that here. Instead you have a gateway that does most of the DAO logic, and pushed the create() to something separate.

    Replace transfer with "persistence implementation". Since you pass the id into the create() method the Factory must know how the objects are stored, or delegate to someone who does. Similarly, since you can getByFilter(), the Gateway needs the same knowledge. So breaking these things apart isn't gaining you any extra abstraction, just more classes. You can't swap either out without changing both of them.

    That's just my opinion on the design though.

    The comment "the Gateway is not really any extra effort since you *need a Gateway anyway for normal SQL*" (emphasis mine) is really what I was getting at. Having a gateway at all is considered totally ridiculous in some communities. If we were doing this in the rails mindset we'd just do:

    item = LineItem.new
    item.populate(params)
    result = item.save

    The object delegates back to the persistence mechanism. It knows all about it's own validation, and anything else it might be interested in. There's no need for the services, and there's also no need to create Gateways. In fact you mention the word Gateway or DAO over there and you'll get laughed out of the room.

    This is not specific to rails though. This is a huge number of communities that include languages from python, to perl, to ruby, to php and beyond. And even essentially the large majority of the CF world.

    This *is* the reason that we get called JavaLite though. This kind of application design is very rooted in the Java "enterprise" world, and to most outside it is considered terribly over complicated. Try asking around freenode, you'll get the picture fast.

    Is it over complicated? Well that's preference, and there's a holy war just waiting to get started there...

    In any case, I'm really not trying to argue which way is better (I sure hope I didn't come across saying either way is right/wrong here). What I'm trying to express that the mindset expressed in this post is very very specific to one very specific way of developing applications. And from an OO perspective certainly not the only right way.

    Not a jab at you. Just a thought.

  • # Posted By Brian Kotek | 8/18/08 12:31 PM

    Sure Elliott, whether the object factory is composed into the service, or composed into the gateway is really a very minor difference. The real point is that the logic to create new objects is encapsulated somewhere other than the service or the gateway.

    I suppose to someone who was used to just piling everything into a service layer, didn't have the need for AOP or remote calls, etc., this would be over complicated. Personally, I would build things this way even if the project didn't necessarily require all of it yet. Because in my experience these kinds of issues and the solutions this setup provides (factories, gateways, etc.) are things that almost universally impact every project I work on. I am against making too many assumptions or over-engineering in general, but separating object construction from object use is an example of something that is not negotiable (for me). Separating interaction with external resources like databases is not negotiable. If I fail to do these things, there's about a 99% chance that I will regret it later.

    So, yes, this may be rather specific to the kinds of projects I work on, but most of this is there because of something I didn't do and learned the hard way that I should have. ;-)

  • # Posted By Ryan | 8/27/08 9:17 PM

    Brian,

    This was a very helpful posting to help get my mind around good model architecture. I am just starting out with OO concepts and am looking for a good model to adopt as a starting point.

    Without infringing on any intellectual property, do you think you could post a downloadable zip file of an example of all these CFCs in action. To take what you done with a picture a step forward with as an example would really drive it home for me. (but only if you have something laying around that you don’t mind sharing.)

    I can’t tell you how tired I am of seeing Hello World examples

    Ryan