An extensive example of project organization

I’d love an official example of big app following the Rails way or an official guide of what goes where outs of the simples MVC. David started this via his On Writing Software Well series, but this was never part of official documentation.

I see a lot of confusion around basic Rails project structure, because many devs consider ./app/models folder to be aimed only from DB abstraction. Therefore, I see

  • random classes in ./lib, which are 100% domain
  • ./app/services folders
  • ./app/doman folders (sometimes consisting of libraries)
  • ./app/creators|destroyers|updaters folder

I am sure all bigger project face the same problems and an official guide seem very beneficial. Even if someone decides that, for whatever reason, ./app/services is the best way for this particular project, it will be a conscious decision and not something that just seems natural.

Also, the definition of what goes in ./lib is very vague.

13 Likes

This is interesting! I’ve gotten similar feedback from devs I work with who are coming to Rails from React. My instinct is that the Rails community is sufficiently anarchic that people would have a hard time coming to consensus, but maybe there’s an opportunity for Rails to uplift some of the specific common patterns as “things people do.”

3 Likes

Something like this would be absolutely awesome! Seeing examples of how more experienced devs solve complex business-related problems within the ethos of the Rails Way would be great!

1 Like

Rails has documentation that states that a new instance of a Controller is created for every request. What seems to be lacking in the documentation is how Rails decides to spawn other types of classes.

With the rising popularity (I think?) of Rails developers utilizing POROs and implementing a Service layer, it seems important to give some pointers on how those classes are initialized.

This depends on a bunch of things, like multi-threaded or multi-process app server, and how the app server might re-spawn processes in the case of multi-process, but I do think it’s Rails responsibility to provide some helpful tips for things to think about.

For example, a lot of developers use class-level methods for their Service layer, and they don’t create an instance of that Service. It would be a helpful tip to let them know that when doing so, if they use class-level variables, they’re going to see data-sharing issues across requests, since that Service class lives for more than one request.

1 Like

I’d love to see something like this. It could even take some common project (something like todomvc, or maybe a little bit more complex) and make it in rails following all the standard ways to show it off with the best practices (concerns, cables, storage, testing, etc)

I wish Writing software well / getting real channel was something more broad and still active, I learned so much even after many years of rails. It’d be awesome to have it/something like it as part of the official rails guides

5 Likes

This! I learned more cool Rails techniques in an hour of watching DHH screencasts than probably a year of random blog posts around the web and whatever. :smile:

4 Likes

@jaredcwhite Those DHH screencasts were awesome, that’s how I learned about ActiveSupport::CurrentAttributes and Current.user, although that was incidental to the video.

Having those tips and a general guideline on how to organize an app would solve the problem of people wondering “where they should put their code” and how they should organize it. I spent so much time as a junior developer pondering such questions, and a lot of them still seem to be hot or emotional topics in the community.

Currently whenever I wonder how to organize something in my app, I do global searches on the eliotsykes/real-world-rails collection of projects. This gives me a good idea about what the general consensus is. Of course “a lot of people doing something” doesn’t mean that their solution is right, but it is the best approximation I could find so far in the absence of anything else.

1 Like

Maybe there’s an opportunity to work with the real-world-rails maintainers here to Sherlock it?

1 Like

Agree with @Betsy_Haibel that uplifting common patterns would be super helpful. I like using naming and conventions that others would intuitively recognize, but I don’t want a structure imposed that doesn’t fit my app or preferences.

My code doesn’t care what folder it is in, but I obsessively need this, while reminding myself that perfectly groomed folders don’t ship my app. :blush:

Some folders are staples for me, and I’d expect them to be a pattern shared by many others:

  • app/models
    • DB-table models
    • DB-view models (scenic)
    • PORO and ActiveModel::Models as long as they encapsulate a “thing” (noun) that is like a DB-backed resource model
    • (all others are imposters and given dirty looks when I see them) :unamused: :roll_eyes: :stuck_out_tongue_closed_eyes:
  • app/forms
    • e.g. a search form wrapping fields and validations. I don’t like putting these in app/models.
  • lib/types
    • custom types (ActiveRecord::Type and ActiveModel::Type). These are loaded in an initializer so cannot live in app/types.

Some folders are murky for me, but a good exemplar might convert me:

  • app/services There are many opinions on what to put in app/services. I often think I want Service Objects but can’t decide what one is. Does it wrap an external API call, an action across a couple of models, or any verb on any noun?

Some folders are rising out of my apps, and might become new staples. I’d like to look to see if they’re a pattern for others:

  • lib/extensions
    • overrides for gems or rails. Honestly, I usually just stick the implementation in an initializer, but feel I should put them in lib and just apply them in an initializer.
    • Examples common for me:
      • a local backport of an unreleased fix to ActiveSupport::Deprecation::DeprecatedConstantProxy
      • my DelayedJobExtensions implementation, which an initializer loads: Delayed::Job::prepend DelayedJobExtensions
      • my PgSearchExtensions implementation, which an initializer loads: PgSearch::Document.include PgSearchExtensions
1 Like

If we agree on common structure, and make a guide of it, maybe adding generators would be also a good idea? Nothing says officially approved like being in rails codebase.

1 Like

Is there any "official" way to organize one-off scripts? deserves a mention, with several helpful ways people use script/

2 Likes