This document discusses refactoring fat components in Ruby on Rails applications. It defines fat components as those with too much logic and many responsibilities in one place, making the code difficult to read and maintain. The document recommends following the single responsibility principle and extracting logic out of fat components using techniques like mixins, value objects, service objects, form objects, and policy objects to organize code better and make components more focused and reusable.
2. Who am I?
Tutti Quintella (@tuttiq online)
From S達o Paulo, Brazil
Major in Computer Engineering
Software Engineer @ Mercari
Director @ Women Who Code Tokyo
Software Engineer since 2011
Main experience with web development
3. DEAR WOMEN, WE NEED YOU
https://meetup.com/Women-Who-Code-Tokyo/
4. Disclaimer
This presentation touches controversial topics like:
Code readability
Design patterns
Coding principles / "good practices"
Most of the content is opinion based and subjective to
exceptions / counter-arguments
5. What are Fat
Components?
Too much logic in the
same place
Too many
responsibilities in the
same component
6. The problem with fat components
Makes it harder to find logic when looking at the file
structure
Makes it harder to read and understand what a single
component does
Makes the component too specific and less reusable
Makes it harder to break the app in separate modules
Makes it harder to unit test
7. Old advice
"Controllers should only concern
themselves with receiving data
from the client and passing data
to the views. Logic should be on
the models"
"Skinny controllers, fat
models"
Result: Skinny controller, obese
models.
User.rb - 700+ lines...
9. Other ways to extract logic
Mixins
Value objects
Service objects
Policy objects
Form objects
Query objects
etc...
10. Mixins: a controversial approach
Pulling sets of methods out of a large model into a
module/interface: makes the model smaller, but does it
really organize the logic?
x
5
12. Value Objects
Most languages already have some native value objects
like Date, Time, URI, File.
Any attributes that are more than basic strings or
numbers are good candidates to be domain specific value
objects
Ex: Phone, Address, Currency, Rating, Score, Color...
14. Service Objects
Moving actions to objects dedicated only to perform them. Good
candidates:
Complex actions that have a lot of specific logic
Actions that reach across multiple models (User + Order + CreditCard
to perform a purchase)
Actions that interact with external services / APIs
Actions that are not a core concern of the underlying model (e.g. data
cleanup, parsing, normalization)
Actions that can be performed in multiple ways (Strategy pattern)
16. Form Objects
Form Objects are useful (and recommended) when a form
doesn't map directly and simply to a single model. E.g:
Data needs parsing or formatting
Data has complex validations
More than one model are updated through the same
form (nested models)
18. Others
Policy objects (define rules /
complex validations for other
models)
Query objects (objects to
perform complex queries, that
depend on multiple relations)
etc...
#6: Explain controller with authentication logic and parsing attributes logic in it.
#7: Initially for small projects you may think it's okay, but with time, the codebase gets bigger, and sometimes the development team gets bigger too. So a lot of problems start showing up.
#8: So at some point the saying "Keep your controllers skinny and your models fat" became popular. That basically say you should move all the logic from the controllers to the models, to leave controllers with the only responsibility of receiving data from the client and passing data from models to views.
But the result of that within time in a large codebase is: OBESE models. That basically have the same problems mentioned before.
#10: So there are actually several ways of extracting logic from your classes to keep then thin and try to be as compliant as possible with the single responsibility principle.
#11: So the concept of mixins is basic extracting sets of methods and shove them in a module or interface, and then include/implement this interface on your models. But if you just follow this blindly, you're not really componentizing and organizing your code, your basically just dividing a large messy file into many smaller messy files. The analogy here is to clean up a messy room but shoving all the mess into 5 drawers. On the outside it will look tidy and clean, but you haven't really organized the contents of the drawers, so it's still hard to find stuff.
#12: But, just so you don't think I'm totally against interfaces, I do think they're very useful when done in the right way. For example an interface that implements a behavior that should be shared among many classes. Like this one for example, Notifiable, an interface that implement methods to notify users about certain things across the app, so you probably want to add the "notify" method to several different classes, depending on what you're notifying about.
#13: Another way to extract some logic from your models is to extract Value Objects. You probably already know Value Objects, maybe just not by this name. A lot of programming languages (or its respective libraries) are full of Value Objects, it's basically classes (or objects) that represent a value, like Date, Time, URI, File, etc And to tidy up your system, one basic step you can do is to transform some of your attributes into domain specific Value Objects.
Any attributes that are more than basic strings or numbers are good candidates to become Value Objects. I'm pretty sure a lot of systems out there have values like Phone numbers, Addresses, Currencies, Ratings, Scores, Colors, etc...