MVCS folder vs Modular folder structure

glidias's Avatar

glidias

07 Sep, 2010 04:28 PM

After doing various projects using the following folder structure-type,
http://knowledge.robotlegs.org/faqs/reference-mvcs-implementation/r...
, many people in my company have complained it's very hard to find stuff with such a file structure. I realised this is because such a structure is based off a generic and abstract model/view/controller pattern, and not based off the concrete specific features & requirements of a particular project. There's a tendency to dump all your view component code into the view.ui folder( which can lead to a huge bloat in files in just one folder), or otherwise having to create many sub-folders within each core root view/model/controller folder to narrow down specifics within each core folder. It becomes impossible to find out which ui component class is being used for which page of a given Flash site, and the only way is to check the library symbol linkages in the Flash IDE, which makes it extremely troublesome. (you know how bad the Flash IDE can be...).

The thing is: Why not do away with the abstract core "model", "view", "controller", "event", "services" folders? They tend to present a monolithic triad of classes that are all spread all over the place, having to view all these separate folder branches (which aren't really meaningful except to follow an obscenely abstract MVCs structure) just to find out how a particular view component, page, or module implementation works in relation to the rest of the application. What's the point of having such folders when such folders hold no purpose whatsoever as far as an application/website is concerned? Shouldn't folder structures reflect how a project is like and what features it has ? In a typical MVCS folder structure, the user won't know how a project is like at a simple glance (from the folder structure alone), but only until he looks into all these tier-based sub-folders, views the main context mappings (and class implementations with mapped commands), than he only gets a rough idea of it's features and the connected dependencies. For those not familiar with such a structure, it becomes a total loss in being unable to find anything.

Wouldn't it be better to place all model, view, command, service/controller classes and events in specific theme-based folders with a specific purpose? For example, say your application/website needs to manage the loading and handling of post data, you simply create a folder like:
com.project.posts.
PostContext
PostModel
PostEvent
PostService
com.project.posts.cmd.

SendPost
AddPost
CancelPost

And for specific Calendar management:
com.project.calendar.
CalendarModel
CalendarEvent
com.project.calendar.cmd.

PostToCalendar

And for specific Facebook management.
com.project.facebookconnect.*
FaceBookAccount

This way, all dependencies for that particular module are all located in one place.

For general stuff like utilities and services that can be reused across different projects as well, you dump those here

com.project.utils.*
(Any generic utilities and services that aren't application-specific go here. Note that I don't have a services folder. A standard utils folder would suffice for that.)

Base/abstract classes and such go here in the following format:

com.project.baseui.
com.project.basepage.

Any specific view implementations within each page are all organised as below in a thematic format. For example:

com.project.pages.eventcalendar.
EventCalendarContext
EventCalendarPage
CalendarDetailsPage
CalendarDetailsPageMediator
CalendarListMediator
com.project.pages.eventcalendar.ui.

CalendarList
CalendarListItem
EventListItem
EventList

com.project.pages.forumpage.
ForumPageContext
ForumPage
ForumPageMediator
com.project.pages.forumpage.ui.

.... (do likewise as above, any specific ui elements for the forum page)

In this way, you narrow down specific implementations used for a particular project into modules. Also, this forces developers to duplicate unique view component class implementations for other pages to allow further customization across the various pages (even if their behaviours are similar). Every view component is encouraged to be tied to it's own page module in most circumstances (except for baseui sharing..., though this should be kept to the minimal because code duplication allows for better standalone customisation/monkey patching compared to extending from a base class which is more restrictive), so that each view component within each page can have varying degrees of uniqueness and customisation when requirements change for different pages. The MVC pattern doesn't need to be followed strictly in each of the specific folders, 'except it should be organised with clean-cut separation of concerns.

Also, there's no need to use model/view/controller sub-folders within each module folder since each module is meant to be kept as small as possible and involves classes prefixed with the usual "Model"/"Event"/"Service" to denote specific implementations, and a convenient "cmd" sub folder to represent the Commands triggered mainly through the module package's core Event payload. In this way,it becomes easier for the user to (at a single glance), view all the tiers of operations being used (event,command,etc.) for a module, without having to open 3-4 separate folders just to see how it's being done. In some cases, there is no command tier and the service is used directly. Since each module can be particularly small, the command tier can be skipped if the service tier is sufficient enough.

In fact, this means it's also right to place models/services/controllers in the same module folder, assuming such models/services/controllers are tied to that module only, such an approach is still fair game. There's no need to filter stuff into meaningless "mvcs/event" sub-folders. Let the viewer see all the relevant classes at once for that module, since in reality, all tiers are important as a whole.

Additionally, not to be confused with Modular Contexts in RL, the "Context" class within each page/module folder doesn't necessarily need to use the above-mentioned in RL, it may just be a static class to help quickly wire up the mappings within that package.

For example, in PostContext.as:

import com.project.posts.cmd.*
public static function wireToInjector(injector:IInjector):void {

injector.mapSingleton(PostModel);
injector.mapSingleton(PostServices);
com.project.utils.AutoWireCommands(
     AddPost, 
    SendPost,
    CancelPost
);

}

And the above static function can be called from the MainContext, so the MainContext doesn't need to be crowded with too many mappings . It just needs to import the com.project.posts.PostContext class to perform the static mapping.

The thing is, why not do it the regular RL way and trigger a Command through an event to perform module-specific mappings? Yes, that can be done, but this means having to map 1 startup command and 1 event signature for each module. Also, if one places all these commands in a generic "commands" folder, you tend to get a over-crowded commands folder. (Of course, one can place this in a subfolder like com.project.controller.startups), but this forces the user to know that such a sub-folder exists within the controller folder. Again, it's all just too vague and too much work to simply wire up dependencies.

What's the point of MVCS if it has no clear purpose? Ultimately, even though the purpose of MVCs is to provide a clean seperation of concerns between all these 4 tiers, yet it's ultimately purpose is to have all these tied together to fufill a particular agenda (so those files should be placed together in the same place). As a result, I feel the thematic folder-package approach is far more meaningful and makes things easier to find for outside parties. When one needs to change a particular module in an application, that particular module's folder is right there at the project root in front of his nose. You'd know explicitly which view components, commands, and application services are tied that module, rather than having to open up 3-5 generically-named folders "just to see how it's all connected" through those "grrrr...mappings" on a "grrrr...MainContext".

When one simply dumps any Event-based class into the "event" folder...or dumps any view component-based class into a "view.ui" folder or dumps any Service-based class into the "services" folder, or dumps a Controller-based command into the "commands" folder, it is done with the assumption that such classes are extremely generic and is normally used for any purpose across the entire site. Or is it? It turns out, in order to determine how these "generics" work together in (usually-specific) application-contexts, all these folders (and the MainContext) must be opened and I must "spot the connection" between all these files which are distributed across various folders that don't mean much except for what they're based upon (which is normally stated in the class name suffix anyway). For me, that's easy. But for others, not too obvious. Why not place all these in a single purpose-driven thematic package folder which others can use if required? That way, there's a "clear objective" to the "program" being created. A package is created with a specific purpose.

Generally, as far as folder structure is concerned for most projects, 80% of the files/folders would turn out specific to the project, while the remaining 20% is just generic. So, why create a folder structure that "pretends" every file within it is generic(ie. through root "services, views, models","controller","events" etc.) when in reality, it rarely isn't? Such a folder structure (a forced MVCS pattern) on a generic MainContext, obfuscates the code, makes things harder to find and encourages laziness on the developer's part in actually not arranging his folder structure in a proper modular format from the root location. After all, isn't good coding about clearly defining specifics, not obscuring it? Just my 2 cents.

I haven't tested the direct modular folder structure approach for RL, but I think it might be better since that worked for other projects, since it allowed everyone to find whatever is needed for each particular module/subject.

  1. 1 Posted by Stray on 07 Sep, 2010 05:28 PM

    Stray's Avatar

    Hi Glidias,

    I use the

    functionalarea-> restricted-> mvcs approach

    and of course

    functionalarea-> api

    package for classes that can be used by other functional area (which can be checked using FlexPMD).

    You'll find a long post with people discussing just this somewhere back a few months ago.

    If my functional area has only a handful of classes, they sit just there. mvcs only comes in where there's enough to justify it (most of the time there is).

    I think you'll find that on anything except the smallest projects projects most people would agree with (and already be using) what you're describing. It's the AS3 'best practice' instruction I believe.

    Stray

  2. Support Staff 2 Posted by Shaun Smith on 07 Sep, 2010 09:49 PM

    Shaun Smith's Avatar

    Hey Glidias,

    Agreed! Nice post. I think a good portion of the RL documentation (best practices, recommendations etc) needs re-visiting.

    Regarding this bit: "why not do it the regular RL way and trigger a Command through an event to perform module-specific mappings? Yes, that can be done, but this means having to map 1 startup command and 1 event signature for each module."

    You could also just invoke the command directly from your "main" context, something like: commandMap.execute(SomeModuleConfigCommand); - just a variation on the static method approach in the same number of files & lines.

  3. Support Staff 3 Posted by Shaun Smith on 17 Sep, 2010 12:40 PM

    Shaun Smith's Avatar
  4. Support Staff 4 Posted by Joel Hooks on 20 Sep, 2010 03:50 PM

    Joel Hooks's Avatar

    I always use a functionalArea->mvcs approach. If you are consistant within a project it all shakes out in the end. I do agree that monolithic applications are poor, and think that we should be "modular" in our development generally.

    The problem manifests in these trivial example applications because they are only one module by nature.

    I like this article in the Cairngorm 3 documentation.

  5. 5 Posted by Enrique on 12 Jan, 2011 08:33 PM

    Enrique's Avatar

    Here's is a good explanation about the advantages of Package-by-Feature (Functional Areas)
    http://www.javapractices.com/topic/TopicAction.do;jsessionid=0BF484...

    I think is neccesary to update the best practices, and to put some examples with the new structure.
    I've read about it, but I couldn't find any good and complete example so I'm a bit confused about the implementation, for example in the article posted by Joel, the recommendation is to use a thin API for the communication between packages, but that's very confusing to me without an example.

  6. Support Staff 6 Posted by Till Schneidere... on 13 Jan, 2011 10:06 AM

    Till Schneidereit's Avatar

    Thanks for the link, Enrique, that's a helpful explanation indeed. I
    especially like the comparison to other domains.

    And you're right: The examples need updating to reflect packaging best
    practices. If you're feeling up to it, please fork the examples and
    have a go at refactoring them accordingly.

    It's not like we don't want to do it ourselves, we just don't have the
    time to actually do it. So any help in that area would be greatly
    appreciated. And if you feel like you need additional guidance or
    simply want advance feedback before sending out a pull request: Just
    shout.

  7. 7 Posted by Enrique on 13 Jan, 2011 04:14 PM

    Enrique's Avatar

    Hi tschneidereit, I think I can't help with this because I don't know how.
    I was hoping an example, tested and working, from someone on the list.
    I never used Package-By-Feature in my projects, but I think is the way to go...
    No body works with Package-By-Feature before in any project?

  8. Support Staff 8 Posted by Till Schneidere... on 14 Jan, 2011 02:58 PM

    Till Schneidereit's Avatar

    Hi Enrique,

    don't worry: We'll eventually get around to it. I'm afraid I can't
    guide you to any up-to-date Robotlegs-specific examples right now,
    though.

  9. Stray closed this discussion on 16 Feb, 2011 09:26 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac