How do I avoid using Concerns when designing Rails database schemas? - ruby-on-rails

I read many blogs, and one of the themes that comes across often is that concerns (at least the way Rails defines them) are damaging to software. On balance I agree - simply including behaviour into models is violating the single responsibility principle. You end up with a god-class that does too much.
But as with many of the opinions gleaned from blogs, an alternative architecture is rarely provided.
So let's take an example app, loosely based on one I have to maintain. It's inherently a CMS, as many Rails apps tend to be.
Currently each model has a large number of concerns. Let's use a few here:
class Article < ActiveRecord::Base
include Concerns::Commentable
include Concerns::Flaggable
include Concerns::Publishable
include Concerns::Sluggable
...
end
You can imagine that 'Commentable' would require only a small amount of code added to the Article. Enough to establish relationships with comment objects and provide some utility methods to access them.
Flaggable, allowing users to flag inappropriate content, ends up adding some fields to the model: flagged, flagged_by, flagged_at for example. And some code to add functionality.
Sluggable adds a slug field for referencing in URLs. And some more code.
Publishable adds publish date and status fields, with yet more code.
Now what happens if we add a new kind of content?
class Album < ActiveRecord::Base
include Concerns::Flaggable
include Concerns::Sluggable
include Concerns::Publishable
...
end
Albums are a bit different. You can't comment on them, but you can still publish them and flag them.
Then I add some more content types: Events and Profiles, let's say.
I see a few problems with this architecture as it stands:
We have multiple database tables with exactly the same fields
(flagged_by, published_on etc.)
We can't retrieve multiple content types at once with a single SQL query.
Each model supports the duplicated field names with the included concerns, giving each class multiple responsibilities.
So what's a better way?
I've seen decorators promoted as a way to add functionality at the point where it's needed. This could help solve the issue of included code, but the database structure isn't necessarily improved. It also looks needlessly fiddly and involves adding extra loops to the code to decorate arrays of models.
So far my thinking goes like this:
Create a common 'content' model (and table):
class Content < ActiveRecord::Base
end
The associated table is probably quite small. It should probably have some kind of 'type' field, and maybe some things common to absolutely all content - like a type slug for URLs perhaps.
Then rather than adding concerns we can create an associated model for each behaviour:
class Slug < ActiveRecord::Base
belongs_to :content
...
end
class Flag < ActiveRecord::Base
belongs_to :content
...
end
class Publishing < ActiveRecord::Base
belongs_to :content
...
end
class Album < ActiveRecord::Base
belongs_to :content
...
end
...
Each of these is associated with one piece of content, so the foreign key can exist on the feature's model. All the behaviour relating to the feature can also exist solely on the feature's model, making OO purists happier.
In order to achieve the kind of behaviour that usually requires model hooks (before_create for example) I can see an observer pattern being more useful. (A slug is created once a 'content_created' event is sent, etc.)
This looks like it would clean things up no end. I can now search all content with a single query, I don't have duplicated field names in the database and I don't need to include code into the content model.
Before I merrily unleash it on my next project, has anyone tried this approach? Would it work? Or would splitting things up this much end up creating a hell of SQL queries, joins and tangled code? Can you suggest a better alternative?

Concerns are basically just a thin wrapper around the mixin pattern. It is a very useful pattern for composing pieces of software around reusable traits.
Single Table Inheritance
The issue of having the same columns across several models is often solved with Single Table Inheritance. STI however is only really suited when the models are very similar.
So lets consider your CMS example. We have several different types of content:
Page, NewsArticle, BlogPost, Gallery
Which have pretty much identical database fields:
id
title
content
timestamps
published_at
published_by
# ...
So we decide to get rid of duplication and use a common table. It would be tempting to call it contents but that is extremely ambiguous - content of the content ...
So lets copy Drupal and call our common type Node.
class Node < ActiveRecord::Base
include Concerns::Publishable
end
But we want to have different logic for each type of content. So we make subclasses for each type:
class Node < ActiveRecord::Base
self.inheritance_column = :type
include Concerns::Publishable
end
class NewsArticle < Node
has_and_belongs_to_many :agencies
end
class Gallery < Node
has_and_belongs_to_many :photos
end
# ...
This works well until the STI models start to diverge for too much from each other. Then some duplication in the database schema can be a far smaller problem than the massive complications caused by trying to shoehorn everything into the same table. Most CMS systems built on relational databases struggle with this issue. One solution is to use a schemaless non-relational database.
Composing concerns
There is nothing in that says that concerns require you to store on the models table. Lets look at several of the concerns you have listed:
Flaggable
Sluggable
Commentable
Each of these would use a table flags, slugs, comments. The key is making the relation to object that they flag, slug or comment polymorphic.
comment:
commented_type: string
commented_id: int
slugs:
slugged_type: string
slugged_id: int
flags:
flagged_type: string
flagged_id: int
# ...
class Comment
belongs_to: :commented, polymorphic: true
end
module Concerns::Commentable
# ...
has_many: :comments
end
I would suggest that you look at some of the libraries that solve these kind of common tasks such as FriendlyId, ActsAsTaggedOn etc to see how they are structured.
Conclusion
There is nothing fundamentally wrong with the idea of parallel inheritance. And the idea that you should forgo it just to placate some kind extreme OO purity ideal is ridiculous.
Traits are a part of object orientation just any other composition technique. Concerns are however not the magic-fix all that many blog posts would have you believe.

Related

Designing multi-tenant app in Rails

I'm implementing a multi-tenant app with Rails. My approach is not to use the postgres inbuilt multi-tenant feature and add a column to record the subdomain. That is where the question is :)
Let's get this example
class Organisation < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :organisation
end
I'm thinking about two approaches here:
Approach 1
add a subdomain column only to organisations
pros - The way how relational databases should work \0/
cons - When I have more complex queries , that will make my code slow
Approach 2
add a subdomain column to both organisations and users
pros - This will make queries faster
cons - I'm going against the relational databases
So the question is, what sort of a method I should follow between above two, or are there a different approach that I didn't think about?
We run a multi-tenant Rails app with slightly fewer than 500 table-backed classes, and if I had to guess I'd say around 400 of them relate to client data.
Client-specific attributes are held in the Client model, but we add the client_id to every client table with a not null constraint on the database. Only a minority of them are indexed, though because they are generally only accessed through a parent record.
We do not have to worry about setting the client id because the model will generally have:
class Child
after_initialize do
self.client ||= parent.client
end
end
We add the client_id to many tables because we have a lot of code that does:
#books = current_user.client.books
... so in those cases we'll have an association directly from Client to the model, and client_id is indexed.
We add the client_id to all tables, though, because we very often, for operational or unusual reasons, want to be able to find all of the relevant records for a client ...
MarketingText.where(client: Client.snowbooks).group(:type).count
... and having to go through a parent record is just inconvenient.
Also, because we made the decision to do this on all client-specific tables, we do not have to make the decision on each one.
So to get to your question, what I would do is add the subdomain to the Organisation only. However, I would add the organisation_id column to every table holding Organisation-specific data.
If you have a lot of clients but you are going to be generally familiar with their subdomain, then I would write a meta-program method on the Organisation that lets you use:
Organisation.some_subdomain
... to get the required organisation, then find the child records (in any table) with an association directly from the Organisation model ...
Organisation.some_subdomain.users
Organisation.some_subdomain.prices
Organisation.some_subdomain.whatevers
my opinion will go Approach number one, couple reasons for this
using relational database provided with activerecord + scopes will make writing software easier, also if you have more objects under organization later for example transactions, items (beside users),
I have a project with multi tenant capabilities and below is sample of design in my project
class Company < ApplicationRecord
has_many :users
# transaction
has_many :transactions
has_many :journals , :through => :transactions
# item
has_many :items
# other has_many ...
end
and in controller you can use eager loading to minimize query (includes / joins)
#company.includes(:users).scope_filter_here.search(params[:q])
approach number 1 is more user friendly compared with approach number 2 as it's more simple to user writing your url address, the less url to type is better (personal opinion).

creating database fields without „nifty generators” rails

Is there a way in Rails to manipulate database fields and corresponding accessor methods without the „nifty generators” ?
I want users, insofar they are privileged, to be able to manipulate the database structure, that is, at least, to add or delete columns. The privileged user should have the possibility to „Add new” some columns.
Say I have an Object/Table artist and it should “dynamically” receive columns as "date of birth", "has played with", "copies sold"
Not sure if it's a dup. It takes a preliminary decision whether Rails discourages from letting the user do this to begin with or or not. (if that's the case => certainly some noSQL solution)
In pure ruby at least it is easy to dynamically add an attribute to an existing Model/Class like this
Test.class_eval do; attr_accessor "new_attribute"; end
and
Test.new.new_attribute = 2
would return => 2 as expected
In order to create or manipulate a customized input mask / model: can I not manually go the same way the generators go and manually call ActiveRecord::Migration methods like add_column as well as create getter/setter-methods for ORM ?
If yes or no, in both cases, which are they to begin with?
Thanks!
I am not aware of any elegant way to allow an Object to dynamically create new columns. This would not be a good application design and would lead massive inefficiency in your database.
You can achieve a similar type of functionality you seek using ActiveRecord associations in Rails. Here's a simple example for your Artist model using a related Attributes table.
class Artist < ActiveRecord::Base
has_many :attributes
end
class Attribute < ActiveRecord::Base
belongs_to :artist
end
With this association, you can allow your Artist class to create/edit/destroy Attributes. ActiveRecord will use foreign keys in the database to keep track of the relationship between the two models.
If that doesn't work for you, your next best option is to look into NoSQL databases, such as MongoDB, which allow for much more flexibility in the schema. With NoSQL, you can facilitate the insertion of data without a predefined schema.

Split model with many attributes

I have a model Foo with a lot of attributes (generated by Paperclip, Devise and other things). Now my table have 43 columns.
The advantages and disadvantages are:
+ I respect the standard OOP.
- The creation of each model instance after a database select is longer.
- The .inspect method is hard to read (not the most important I think, and easy to override).
- The source code of the model takes more than 700 lines (can be solved using Concern)
My problem is that I don't need all theses informations on 90% of my website pages.
I thought of a solution, but need some expertise to know if this is a good or bad idea.
The idea is to cut the model into 2 parts: Foo and FooInfos, where:
Foo contains all the main informations
FooInfos contains all the informations for the 10% of the remaining pages, only loaded when necessary (with delegate method, e.g.)
a foo object has_one foo_info
With this method, all the advantes noted above becomes disadvantages and vice versa.
Is this a good idea ? Do you have some cases where this is not ? Do you have any other ideas ?
Regards
You're correct in that, as you described, your model Foo has too much information.
Just moving information into a generic FooInfos class/table will only mask the original problem behind an indirection, not solve it.
To point you into the right direction: asking "how to make this model smaller" is a first step, but you need to ask a slightly different question:
Does this class do exactly one thing? And if not, how do I separate concerns?
A variant of this would be: "What separate concerns are hiding inside this class?" There are multiple ways to split up the information and concerns hiding in a single page object.
Some tools:
1) isolate information
2) separate information into multiple models
3) STI (Single Table Inheritance)
4) Mixins
and many, many more
1) isolate information
Analyse your code and be watchful of the following: a set of methods is using a set of attributes and is using a separate view element. Or is generally accessed on model-level through a set of related interface methods.
Everytime you write a (bigger) new feature, ask yourself if that's really a feature belonging directly to the current model or if it's a model of it's own (or a helper model).
2) Multiple Models
You can split up some information like you described in your question: introduce another model (like FooInfo, but actually not like this!):
Don't just extract some meta-information, but make the model name matter!
For example:
Paperclip stuff can be extracted into an Attachment model, where a Page model can have one or many attachment models.
devise access rights stuff can be managed via CanCan
SidebarContent as a method to represent a page's information inside the sidebar
...
I'm sure you will find a lot of stuff inside your Page object that in reality is a separate concern.
You can still render the additional information via a partial and pass for example #page.sidebar to it, this way your Page view doesn't get too big. Also, in the backend you can use nested forms and partials, so on the outside the solution with several models will be no different from your current solution, but split up nicely into several concerns.
I did a quick google search on nested forms, and this came up: http://iroller.ru/blog/2013/10/14/nested-model-form-in-rails-4/
Don't know if it's any good, but in doubt you can always google yourself or get a book with examples on rails.
If you unload information to a different model, it will not only be still OOP, but even more so:
class Page < ActiveRecord::Base
has_one :sidebar
accepts_nested_attributes_for :sidebar
end
class Sidebar < ActiveRecord::Base
belongs_to :page
end
#page.sidebar # => very OOP!
3) STI (Single Table Inheritance)
Here's an official documentation page: http://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html
Basically, you can move the information that's not needed on every page into some special cases:
class Page < ActiveRecord::Base
end
class ImprintPage < Page
def do_something_with_terms_and_conditions
end
end
class IntroPage < Page
has_one :logo
end
class Logo < ActiveRecord::Base
# paperclip stuff
end
You can even derive the Logo from Image, making it a special kind of Image (where Image contains all the paperclip stuff).
ActiveRecord will store the class type in the database, so you can do something like this in your view (pseudocode):
if #page.is_a?(IntroPage)
# render intro partial
4) Mixins
You can move concerns into mixins:
class Page
include PageTools::UserMethods
include PageTools::ImageHandling
end
But be very careful, since this might lead to information being all over the /lib folder, which might again be just disguising the original problem.
Conclusion
There's much more you can do to make a class/model less monolithic (for example Service Objects, Concerns). These are all just tools to implement some important principles: don't let your classes get big, and try to separate information/concerns/functionality often.
Most of it will come through practice, just never add a bunch of methods for a feature without asking yourself first if it has to really be inside this exact model - refactoring a huge Page monster model after it has grown too much is always painful!

Restructure bulky class

I have an ActiveRecord model Reservation.
It got to the point that the class is to large and does too much.
I would like to split it into a few different ones and place those under the Reservation module.
Unfortunately this will break the app.
At this moment I see following options:
namespace it to something like ReservationConcerns or similar
add the functionality to the Reservation class itself, but physically move it to the subdir (Reservation would be in app/models/reservation.rb, Reservation::Pipeline would be in app/models/reservation/pipeline.rb etc).
So the question is how to structure the different concerns of a feature already having it as one single, bulky class without breaking the app.
If you want to split up a Ruby class into different components without changing its public interface, one solution is to use modules:
# app/models/reservation.rb
class Reservation < ActiveRecord::Base
# associations, validations, etc.
include Pipeline
end
# app/models/reservation/pipeline.rb
module Reservation::Pipeline
def some_pipeline_method
# ...
end
def other_pipeline_method
# ...
end
end
ActiveRecord also provides observers, which "is a great way to reduce the clutter that normally comes when the model class is burdened with functionality that doesn't pertain to the core responsibility of the class". Observers often make heavy use of the ActiveModel::Dirty methods.
These suggestions are complimentary: modules can help you group your interface into more local chunks, while observers can make backend details more self-contained. From here, it's difficult to be more specific without knowing exactly what pieces you have that you're trying to break out.

Is there a (code-design) reason to not embed classes when designing MongoMapper models?

This question relates to using Ruby on Rails 3 with MongoMapper and EmbeddedDocument. Also, the ManyAssociation.
http://mongomapper.com/documentation/embedded-document.html
The MongoMapper examples (in the link above) show two separate classes:
class Order
include MongoMapper::Document
many :line_items
timestamps!
end
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
But this pollutes the global namespace with LineItem. Out of context, LineItem for what? And what if I want another model, say WishList, to also have a LineItem set?
So it is possible to embed the LineItem class inside Order, like this:
class Order
include MongoMapper::Document
many :line_items, :class_name => "Order::LineItem"
timestamps!
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
end
While this may be technically fine (yes?), will I run into design issues later on? Does it just make the code too ugly? Too complex?
Presumably, the existence of this in the Ruby language means someone thinks it's an ok idea?
One thing I always liked about Django is how it uses "apps" to group related model classes (and separate the namespaces). So my code above is also achieving that in Rails.
I don't see any technical problems to either approach. The problem I've run into is that when a class is embedded in it's parent and is also in the same file, I'll forget it's there. So if your namespaces it as Order::LineItem you can make an "order" folder in your "models" folder and put "line_item.rb" in there.
The other trouble is if you want to have a controller for Order::LineItem, you also have to namespace it and put it in a folder, and in the router it will look like:
resource :orders do
resources :line_items, :controller => "order/line_items"
end
Unless you know your app is going to have multiple types of line_items, I'd recommend not namespacing it—you could be over-coding if you did. For example, if you were later to need two types of line_items you might even find that some of your code could be re-used between models—and if you namespaced your first line_item you may find yourself de-namespacing it.
In Python, namespaces are considered a great thing that there should be more of. But when you think about the global namespace of your Rails app, it's not that cluttered. Gem authors are pretty good about keeping all of their classes namespaced in their one gem module, so in your Rails app you're going to have one namespace for each gem you're using (Rails itself is 5 gems, not sure how many global constants though), plus and a bunch of files included by Rails (e.g. SecureRandom). It turns out to be really nice to have those files "just there" and I've found in practice that namespace collisions are rare and you can easily work around them. I've only run into a namespace issue maybe once, but several times I've accidentally defined a "send" method on a model—a much more common problem with similar repercussions.

Resources