Aside from all the rudimentary folders, we have a few that are generated by gems we use, and then there's the custom folders we've created. It's not obvious sometime what should go where. We have folders called "labor" and "services" which are for plain-old-ruby-objects; We had some purpose to these patterns but it never really materialized. Now it's just confusing.
app
├── assets
├── controllers
├── dashboards (created by administrate gem)
├── decorators (created by draper gem)
├── fields (created by administrate gem)
├── helpers
├── javascript (created by Webpacker gem)
├── labor [** we created **]
├── lib
├── liquid_tags [** we created **]
├── mailers
├── models
├── observers [** we created **]
├── policies (created by Pundit gem)
├── sanitizers [** we created **]
├── services [** we created **]
├── uploads (created by carrierwave gem)
└── views
Or check it out here on GitHub
How do you organize your Rails app
folder? Any and all opinionated suggestion welcomed!
Top comments (12)
I didn't think about it but peaking into other Rails's open source apps might help.
Discourse for example has a services folder with a clear structure and I see that they also centralized async jobs: github.com/discourse/discourse/tre...
Just signed up to +1 this excellent suggestion, thanks!
Thanks for this advice rhyme!
This is our current app folder...
I think we do things similar to what @andreasklinger says...
But instead of using the models folder we use app/services... We use namespaces for each distinct domain logic concept and we use the root-module as the API for each domain concept.
For example if dev.to where structured like our app, maybe in app/services, instead of having something like...
You would have something like...
And we have as an internal rule/convention not to call anything nested inside each namespace outside of it. So in a controller you could have something like...
This help us to refactor the internals without breaking a lot of things =)
re folders:
i used to use
app/services
- eg ProductHunt is still structured that wayat some point the separation between a few classes in app/models and app/services just became weird - especially if they work super close together - eg a concern calling a service object
so in newer projects i just use
app/models
- keep in mind that models stands for classes that model your domain logic - not just persisted statere namespaces:
i do the same setup as benito w/ the namespaces
usually the logic starts in the method - and when it becomes more than a 3 liner i extract it to some class so it easier to extend
eg
@bhserna :
my issue with this style (which I have used until recently and trying to move away from) is that you end up with giant modules that contain methods for everything related to "article", it's not that different from the "fat model" pattern.
A better way would be to group stuff by function, not by object, like they do in the labor folder in a way
Hi @rhymes =)
More than a module for everything related to "article" (or active record object).... I think that the term "Domain concept" that Andreas used was more accurate...
I don't think is the same as the "fat model" pattern, because also as Andreas said this modules should be used as the API for a domain concept, but you are not forced to implement the behavior there... you can delegate the behavior to other objects/modules inside... maybe something similar to what they do on the labor folder...
And also another difference with the "fat model" is that these modules should not have any state there. This can help you to move the behavior to other modules in and easier way that when you want to move behavior from an stateful object.
Even though is true that this module can grow to something that you don't want and you should be careful...
Two patterns that we have used are...
Have modules for more "concrete concepts"... for examples we have one module
Payments
but as the application has a lot of behavior in this concept... for some specific concepts related also to payments, we added some other modules likePaymentsStatus
,PaymentProjection
,MovementsTable
, etc...Delegate to internal module... For example we have a
Projects
module that have some behavior to keep a budget... At the beginning this behavior was implemented calling classes just one level deep, but then the behavior started to grow and we move the behavior to aProjects::Budgets
but we are still calling the behavior through theProjects
module...... And well this method have worked for us, that is why I wanted to share it, but maybe there are other ways that can be better, but we have not tried yet =)
Thanks for the explanation! Now I understand better :-)
It's more or less what I ended up doing with service objects/modules and "business objects/modules" but something left me unsatisfied and I want to try a better approach the next time around.
I wonder if a tool like dry-auto_inject could help cleaning up the code.
Our
app
folder structure:@andreasklinger We're finally getting around to doing something about this. I've definitely taken to your past comments on this issue but you're welcome to go into it here as well if you like.
IMHO:
move everything into app/models
use namespaces for each distinct domain logic concept
use the root-module as api for that domain logic concept
I usually rename the webpack Javascript fielder to frontend and use it also for my css