Ruby on Rails - domain objects without fat models in ruby on rails - ruby on rails tutorial - rails guides - rails tutorial - ruby rails
- "Fat Model, Skinny Controller" is a very good first step, but it doesn't scale well once our codebase starts to grow.
- Think on the Single Responsibility of models. What is the single responsibility of models? Is it to hold business logic? Is it to hold non-response-related logic?
- No. Its responsibility is to handle the persistence layer and its abstraction.
- Business logic, as well as any non-response-related logic and non-persistence-related logic, should go in domain objects.
- Domain objects are classes designed to have only one responsibility in the domain of the problem. Our classes Scream Their Architecture for the problems they solve.
- Practice, we should strive towards skinny models, skinny views and skinny controllers.
- The architecture of our solution shouldn't be influenced by the framework we are choosing.
Example
We are marketplace which charges a fixed 15% commission to our customers via Stripe. If we charge a fixed 15% commission, that means that our commission changes depending on the order's amount because Stripe charges 2.9% + 30ยข.
- The amount we charge as commission should be: amount*0.15 - (amount*0.029 + 0.30).
- As soon as we integrate with a new payment method, we won't be able to scale this functionality inside this model.
- Also, as soon as we start to integrate more business logic, our Order object will start to lose cohesion.
Domain objects, with the calculation of the commission completely abstracted from the responsibility of persisting orders:
Using domain objects has the give below architectural advantages:
- it's extremely easy to unit test, as no fixtures or factories are required to instantiate the objects with the logic.
- works with everything that accepts the message amount.
- keeps each domain object small, with clearly defined responsibilities, and with higher cohesion.
- easily scales with new payment methods by addition, not modification.
- stops the tendency to have an ever-growing User object in each Ruby on Rails application.
- We personally like to put domain objects in lib.
- If we do so, remember to add it to autoload_paths:
- We may also prefer to create domain objects more action-oriented, following the Command/Query pattern.
- Such case, putting these objects in app/commands might be a better place as all app subdirectories are automatically added to the autoload path