views:

530

answers:

3

I'm following a tutorial which I think is written by someone who doesn't know what he's doing (already caught 2 obvious mistakes and the rest of code is messy). But I don't want to discredit the guy completely, so I'm asking here about something else that I don't understand.

First of all, I will send a 100 brownie points, my 2 pets, and a box of chocolate to whoever can explain to me what is going on with this code.

He's using module-based architecture. Module name is frontmodule. Module has MVC. And module has an internal library of its own.

  /modules/    
      /frontmodule/
          /models/
          /views/
          /controllers/        -- the /module controller is here (undestandable)
          /library/            
             /Controller/      -- the /module/library controller is here (why?!)
                /Action/

First comes the confusing part. Why each module has an internal library, and why that intenal library has its own controllers and actions. Is this a best practice? I'm thinking this library could be moved to a plugin that the module can use. Not sure..

Now comes the interesting part.... in addition to each module having its own internal library, there's also a Common library shared by all modules (see it below at the same folder level as /modules) and that Common library also has its own controllers and actions (just like each internal libraries have their own controllers and actions)

  /modules
  /library/
      /Common/
          /Controller/         -- the /common/library controller is here (why?!)
              /Action/
                  /Helper/
              /Plugin/

So we have 3 controllers:

  • the module controller
  • the module internal library's controller
  • the common library's controller

Now here's the insane part that I think is over-complicating life

He says: A module controller extends the module’s library parent controller which also extends the Common library controller.

class IndexController 
       extends Frontoffice_Library_Controller_Action_Abstract { ... }

abstract class Frontoffice_Library_Controller_Action_Abstract 
       extends Custom_Controller_Action_Abstract { ... }

So I guess:

  • the module controller = IndexController
  • the module internal library's controller = Frontoffice_Library_Controller_Action_Abstract
  • the common library's controller = Custom_Controller_Action_Abstract

where module controller extends module internal library's controller

and module internal library's controller extends common library's controller

Has anyone seen anything like this before? My guess is that this code won't be easy to maintain, but maybe those more experienced with zend can tell me what this guy is trying to achieve. The app structure is a little too messy. I think he's abusing MVC instead of using it to simplifying the app and its maintainability.

+13  A: 

This is insane. You're making web pages, right? This isn't hard. I'd say the stuff you posted is the very definition of overengineering:

http://en.wikipedia.org/wiki/Overengineering

beamrider9
It isn't actually over-engineered, it is simply incorrectly engineered in terms of Zend Framework best practices.
wilmoore
As you said, that's pretty subjective. Some - myself included - would argue that the Zend Framework (like most web frameworks) is itself a prime example of overengineering.
beamrider9
@beamrider9, as someone who's done it the old way with 1 .php file per page for ages, I can say that I definitely did think the same way when I first started out with it. It's a paradigm shift initially, but once you cross that hurdle of why do I need to separate my code to logic/views/etc, you begin to experience the beauty of Zend (not just Zend, but the MVC architecture in general. Zend didn't invent MVC, it just uses it). So I'd say give zend or any other MVC framework a try. Zend happens to be maintained by the company that supervises PHP so it's for sure one of the better maintained ones
jblue
@beamrider9 of course this statement above doesn't mean that I think this particular example isn't over-engineered or mis-implemented. I think it is, but that's the fault of the guy who wrote it not the framework itself.
jblue
@jblue What gives you the impression I haven't tried using MVC frameworks? Would I be justified to have a strong opinion here if I hadn't? They have their place, but I've just seen too much MVC cruft/abuse, too steep of a learning curve for new developers, and too much MVC dogmaticism at the expense of developing actual features.
beamrider9
+7  A: 

Not insane at all. Perhaps badly or overly engineered, but it could be a useful setup.

It's just two "extra" levels of inheritance, which in some cases might make perfect sense.

  • properties or methods that should be available in every controller in the system go in the "common library controller".
  • properties or methods that should be available in every controller in a particular module go in the "module internal library's controller"-
  • properties or methods required by a single, concrete controller, live in that concrete controller.

But generally, this suggests packing an awful lot of logic into the controllers, which is generally bad design.

timdev
+1 but let's talk about this `properties or methods that should be available in every controller in the system go in the "common library controller".` Why define a "common library with controllers" when you can just use `application/controller` at the app level? You say it's `perhaps badly or overly engineered`.. Is here a better way to do the same thing?
jblue
@jblue - What's `application/controller`? In most ZF setups I've seen, you've got a directory called `application/controllers` that contains controllers that are essentially the controller of the default module, though in some module setups, there's nothing there, and there's a module named `default`.
timdev
@jblue - as for better ways to do this stuff, it depends on what you're doing. In most cases stuff that needs to shared between various controllers is better off living in the "model" (however you define it in your app) or in some kind of resource plugin.
timdev
Yes I meant `application/controllers` with an `s`, typo. So how about moving what's in `library/common/controllers` to `application/controllers`? The `default` module can stay as is. I'm still waiting for someone to really nail this, if it can be done, with a simpler best practice structure.
jblue
Can you edit your question and point at the tutorial you're looking at? It's impossible to tell what exactly is supposed to be going on without seeing some code. My big question: what controller/action gets called for "/"?
timdev
I wouldn't want the guy to take it personally. It's not my intention to criticize him publicly. So I'd prefer to give you the link here as a comment instead of in the main question. `http://phpdev.ro/zf-tutorial-series-part-1-module-based-app.html`
jblue
The tutorial doesn't include an applicaiton/ directory, like a typical non-modularized ZF app. Instead, his configuration sets up the 'frontoffice' module to be the default. Final verdict from me: not insane. Doing nicely-segmented modules in ZF is a pain. This tutorial is a reasonable attempt. I think his extra intermediary abstract classes are a distraction from the module configuration stuff that is mostly in the .ini file, and the modulelayout plugin stuff. You could just write your concrete controllers as extending Zend_Controller_Action and omit the abstract stuff, for now.
timdev
@timdev He also utilizes abstract controllers to deal with common action helpers usage in each controller, e.g. switching layout for AJAX request.
takeshin
@takeshin, so is that a good or a bad thing?
jblue
It's generally a good idea, but he should be using "composition over inheritance" (i.e. Action Helpers)
Tomáš Fejfar
@jblue Certainly not insane. This has it own purpose. There are many choices, one are good in one case, bad in other. DRY, but as Tomáš wrote: *favor composition over inheritance*.
takeshin
I had to down-vote this because it is actually a pretty insane architecture. Regarding the directory setup, I don't hate it, but I certainly don't love it. It is pretty superfluous. That isn't the deal-breaker. With Zend Framework, there is little-to-no need to use inheritance for controllers. ZF promotes composition over inheritance where it is possible and that is what Front Controller Plug-ins and action helpers are for. The multiple levels of inheritance is just ensuring that you have more technical debt than you need. There may be some gems hidden in this tutorial, but this part isn't it.
wilmoore
@timdev: you mentioned model logic then mentioned resource plug-ins as if these are interchangeable. They actually aren't. A resource plug-in is generally created as a way to encapsulate and re-use resource bootstrapping logic. Logic common to "controllers" belongs in an action helper or front controller plug-in. Either can "use" or delegate to the model if necessary.
wilmoore
@jblue: The tutorial also suggests stuffing multiple objects into the global registry. That is also a non-best-practice so to speak.
wilmoore
@takeshin: switching layouts can be done via a front controller plug-in or via the intrinsic context-switch action helper.
wilmoore
@wilmoore, can you add your comments also as a separate answer to get upvoted, and this way you could add more details or elaborate how you'd do it if you want. I've unselected this answer to keep it open for more ideas.
jblue
@jblue: I have now added a full answer. Thanks.
wilmoore
+7  A: 

The great thing about Zend Framework is that it is use-at-will which means you can use a single component or you can use them all. Most components are also very flexible either via configuration or extension (inheritance or composition, with ZF favoring the latter).

Zend Framework MVC is extremely flexible...even to the point where many have accused it of being over-engineered or bloated itself. This is subjective.

Sure, you probably won't want to use Zend Framework for a simple contact form; however, there is nothing stopping you from utilizing just Zend_Mail and Zend_Form without Zend MVC/Application. With flexibility in mind, there is currently no single methodology which is touted as the best in terms of organizing an application into modules. This is a task best left up to the implementer.

This brings us to the tutorial at hand. The tutorial writer has come up with a strategy for re-use. This is a good thing; however, there are some flaws with his approach.

  1. A library per module. This is not necessarily bad; however, it is in most cases not necessary. Lets explore what options we have just in case such a structure is needed for some reason.

    a. Build a general library (you most likely already do this) namespaced (pseudo if < 5.3 or actual namespaces if >= 5.3).

    b. Utilize the intrinsic "Resource Autoloader" http://framework.zend.com/manual/en/zend.loader.autoloader-resource.html

    NOTE: I personally haven't used the resource autoloading much. The one time that I did use it, I found that I could have just moved those items to my library. That being said, there are uses for this. It seems to shine when you expect to mix and match modules across projects or for distribution. ZF2 will address this in a less hacky way IMHO.

  2. Base controllers for re-use. Again, reuse is great; however, Zend Framework provides better alternatives to sub-classing (inheritance) controllers. First, here are some reasons NOT to use controller inheritance:

    a. Keeping things DRY is defeated when you have multiple modules that differ enough to need a base-class per module but functionality is copied/pasted across each module's base controller class.

    b. It becomes hard to manage inherited properties as it is harder to visualize what inherited functionality is being utilized by each controller/action

    c. With PHP allowing only single class inheritance, you blow your one chance at inheritance here -- use this only if there are no other options left

    Alternatives:

    a. Front-Controller Plug-ins Use these when the functionality/logic needs to run on every request regardless of module/controller/action

    b. Action helpers As mentioned by the ZF project lead, "They're a built-in mechanism in Zend Framework to allow you to extend your action controllers in a way that uses composition instead of inheritance." There isn't anything that you can do in a controller that you can't do via an action helper. Use these when the functionality needs to happen on a per controller and/or action basis.

So, was the example in the tutorial over-engineered? Not necessarily; however, it is certainly a candidate for incorrectly-engineered as it relates to best practices and provisions given by Zend Framework.

I need to digress for a moment and discuss the terms over-engineered and bloated for just a moment

When someone tells you that something is over-engineered and/or bloated without stating a context please take it with a grain of salt.

The Wikipedia article - http://en.wikipedia.org/wiki/Overengineering reads in part "...when a product is more robust or complicated than necessary for its application...".

So, when referring to something as Over-engineered/bloated one should be careful to qualify the context or application at hand. Blanket statements should be taken with a grain of salt and in most cases, not taken at all. This is akin to saying something like "I'd never use a 'Circular Saw' for woodworking since it has way too many features and those features confuse me". Sure, this tool may be over-kill for small home/side projects; however, since it is super flexible you will be happy you have this tool when you find yourself in situations where you never thought you'd find yourself.

Yes, most web frameworks are over-kill for a simple CRUD application such as a contact page or even a simple blogging application. It is unfortunate that most web frameworks use the blog example as their introductory example - go figure.

Extra Info:

  1. If you wanted to switch layouts based on the module/controller/action, you could write a Front-Controller Plug-in. Just call "Zend_Layout::startMvc" and pass it a layout name and a path.

  2. Switching the actual view script based on the Accept header (or URL parameter or X-HTTP-METHOD-OVERRIDE header) can be done with the context switch action helper (intrinsic to Zend Framework) - http://framework.zend.com/manual/en/zend.controller.actionhelpers.html

  3. Feel free to pass a model instance to an action helper. You can also configure your action helpers with configuration from the bootstrap. This way, there is no need to store things in the global registry which is just a glorified global variable. This is a hidden dependency that you don't need. For the best re-use, you can create your own custom plug-in resources by extending Zend_Application_Resource_ResourceAbstract - http://framework.zend.com/manual/en/zend.application.core-functionality.html#zend.application.core-functionality.resource-resourceabstract

wilmoore