Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Single Responsibility Rails

Single Responsibility Rails

An extended version of the talk where I introduce Hexagonal Rails as an extension of SRP.

Rae Bonfanti

January 22, 2015
Tweet

More Decks by Rae Bonfanti

Other Decks in Programming

Transcript

  1. S I N G L E R E S P

    O N S I B I L I T Y R A I L S @ W H O I S R A E Hello! I’m Rae. I’m a developer and, I just got hired here at Carbon Five as a matter of fact—really excited about that! Today I’m going to talk about Single Responsibility Principle and how it makes developing Rails apps easier.
  2. A M I A L O N E ? Who

    in here works on a Rails app? Okay, great. Who pair programs full time? Don’t worry, I’m not trying to sell you anything. Alright, now keep them up if you don’t write code unless you have a failing test first. Okay, I’m like more of you than I thought! Ok, great! I’m going to show some code samples and there’s not enough time to show all the passing tests and do a live refactor, so I’m not going to show that but imagine the tests running in the background even though you won’t see them.
  3. C O M PA R I N G N O

    T E S When I meet another developer at a party and we talk shop, I'll describe the structure of codebases I prefer to develop and I might say things like:
  4. S M A L L , R E S T

    F U L C O N T R O L L E R S “our controllers are only 30 lines long”,
  5. P E R S I S T E N C

    E S E PA R AT E D F R O M D O M A I N “we keep logic out of our ActiveRecord objects”,
  6. M A N Y S M A L L O

    B J E C T S and “we break everything out into small objects”
  7. A R C H I T E C T U

    R E A S T R O N O M Y Sometimes people’s jaws hang open. And, you know, maybe the architecture sounds a little crazy.
  8. – K E N T B E C K “Software

    Design is about change” Paraphrasing Kent Beck, Software Design isn’t about impressing people at parties. Software Design lets us adapt to changes.
  9. S I N G L E R E S P

    O N S I B I L I T Y One of my favorite software design principles is SRP. SRP has its roots in Rebecca Wirfs-Brock and Brian Wilkerson’s idea of Responsibility- Driven Design (RDD).
  10. - R E B E C C A W I

    R F S - B R O C K “A class has responsibilities that fulfill its purpose.” Wirfs-Brock said “A class has responsibilities that fulfill its purpose.” SRP requires that a class be cohesive— that everything the class does be highly related to its core purpose.
  11. B U T W H AT D O E S

    I T L O O K L I K E ? So, this all sounds good, but what does SRP look like? Well, not like this:
  12. Here is an ActiveRecord model that I wrote about a

    year ago that makes me wanna cry. If you just look at the varying shapes and colors of the lines of code, you can see that this class is doing a lot. As you might imagine, it would be painful to add functionality to this code and I want to get it to a place where it’s easier to do that. Let’s take a closer look. This class holds some constants, including a hash lookup of data that looks pretty important. It’s also responsible for collecting query results. So, this all looks really overloaded, right? It doesn’t have to be that way. Someone you may have heard of, Sandi Metz, is a huge advocate for keeping things simple.
  13. – S A N D I M E T Z

    “Your first obligation is to take a deep breath and insist that it be simple.” And we can make it simple. How can you tell if your class isn’t simple enough, or if it’s in violation of SRP? Sandi Metz suggests that a good indicator for classes following SRP is if you're able to describe their role in one sentence without saying ‘and' or ‘or’. In the code sample I just showed you, we already discovered that I broke this rule horribly. I’m gonna show you a refactor that I did to make it all better—well, somewhat better :D
  14. QUERY_SOURCES is holding what looks to be some critical data.

    A look around the application reveals that other objects rely on it, some referring to it by index. Wow. This means that if I add a new query source, I could totally offset that index. Well, this sounds like a recipe for tears. I mean, it’s super likely that I’ll want to modify this collection of search engines over the life of the project, and more rapidly than I might be changing the surrounding code. Does anyone have an idea for how I might solve this issue? A low hanging fruit is giving this hash its own table. So, first step, I made that table and extracted the data with a data migration. There’s an interesting thing about change…
  15. M A K E C H A N G E

    C H E A P Change is really the only thing we can predict in software development: product suddenly changes their requirements, the gem you were relying on before needs to be swapped out, etc. So, if we know we’re going to be doing a lot of it, why not make it cheaper? By separating things that change for different reasons and at a different pace—like the QUERY_SOURCES hash from surrounding code—we increase the resilience of those things in response to change.
  16. Next, I targeted the #collect_results method and related constants from

    that same Query class. This method here is doing several things too many: it’s searching on a search engine, parsing the response with Nokogiri and an html selector, and saving the results. This is where I decided to do a method-to-method-object refactor.
  17. M E T H O D - T O -

    M E T H O D - O B J E C T Method-to-method-object is a specific refactoring recipe that you can reproduce with some practice. The objective is to turn a big method into its own object—or in this case, many objects—so that all the local variables become fields on that new object. Also these new objects tend to be more singularly responsible ;D
  18. The first move I made with that method-to-method-object refactor was

    to copy and paste the #collect_results code from Query into the SearchEngine model. This is actually the same model that I created a step before when I turned that nasty QUERY_SOURCES hash into a DB table.
  19. Once I copy-pasted that refactor from Query into SearchEngine, there

    was clean up to do in Query. The new interface for the #collect_results code in SearchEngine is the #search method, so I swapped in that method call here. Right now you might be asking, “how is this better, Rae?” Ok, I’ve basically moved the junk from one place to another. But surely searching is something that you do on a search engine. The logic is a little closer to home in the SearchEngine. But we’re totally not done!
  20. M O A R M E T H O D

    T O M E T H O D O B J E C T ! ! We can now break the #search method apart into smaller objects with more method-to-method-object refactoring.
  21. With a close look, I see that all of these

    lines are related to querying a search engine. … The lines below it…
  22. Are more related to parsing the response that we get

    back from that search engine. … With the parsing, I want to select information from a specific element on the search engine response. With these two responsibilities identified—the searching and the presenting—I knew I wanted to create a Searcher and something that presents the number of results.
  23. C O P Y M E T H O D

    L O G I C I N T O N E W O B J E C T Again, the first step with both of these method-to-method object refactors is to copy and paste the method logic into a new object.
  24. Here, I’ve moved that Searcher object I was imagining out

    of the SearchEngine and then broke apart that code into smaller methods. And, I want to point out that, even methods can reflect SRP and, as a result, become more cohesive units. I’m able to do things like delete comments because the method names I’ve written already describe what’s happening.
  25. WA S H , R I N S E ,

    R E P E AT You’ll notice after doing a few of these refactors that they start to feel rather mechanical. Maybe even relaxing or therapeutic. And now, the code is a little cleaner. Next, I’ll extract that second object I was seeing in the SearchEngine.
  26. This is what I did to it. You can see

    again that I extracted and refactored the corresponding chunk of code that was in SearchEngine into smaller more cohesive methods that follow SRP. So what does the SearchEngine look like now?
  27. The SearchEngine#search method now calls to Searcher, ResultsCountPresenter, and creates

    a QueryResult at the end. It’s a lot cleaner, and we’re going to leave it there for now, but there is more work to be done. For instance, I’m not totally comfortable with SearchEngine calling all three of those objects. We’re going to move on from my code for now, but to recap…
  28. I T S TA R T E D W I

    T H Q U E RY I created 4 objects out of what was just the one Query at the beginning.
  29. QUERY SEARCH ENGINE SEARCHER RESULTS COUNT PRESENTER This gave the

    responsibilities that were in Query room of their own and cleaned up the code significantly, however there is definitely still work to be done. So, enough about my code; what about the code you all develop? Do SRP and method-to-method-object seem like they could be useful? I hope so…
  30. TA R G E T C O N T R

    O L L E R S A N D A C T I V E R E C O R D M O D E L S Where can you even start with all this SRP stuff with your code? If you’re like me and have seen controllers and ActiveRecord models that are over 200 lines long—yeah… You understand quickly why one class in an application is called the "God object": it does so much that you end up speaking in tongues while thinking about it. That’s where you start!
  31. S M A L L E R O B J

    E C T S = = S C O P E D C O N C E R N S Break apart the bulky controllers and ActiveRecord models into multiple smaller, and easier to grasp classes. Imagine—scoping your concerns and not having to consider the whole world of the application at once and in one class. Ever notice how hard it is to appropriately name that one monster class?
  32. E A S Y N A M I N G

    You'll find that objects following SRP are easier to name because their responsibilities are better-defined. How do you like the class name SignIn::Bouncer? You can tell right off what this class’ responsibility is. But what about a more mature, potentially much more complex application than we saw in my code earlier? How would a team go about looking at that through the SRP lens? That’s a great question!
  33. H E X A G O N A L R

    A I L S Who in here has heard of something called Hexagonal Rails? It’s totally ok if you’re like “what the heck is that?”
  34. A N AT U R A L E X T

    E N S I O N O F S R P One way to support SRP at a higher level is by investing in an architecture like Hexagonal Rails.
  35. W H Y H E X A G O N

    S ? Hexagonal Architecture is just a fancy term for a set of design practices that can be seen as an extension of the SRP. Hexagonal is just the first shape that Alistar Cockburn—who coined the term—drew to illustrate it. It's relevance to describing the design ends there. Another name for it is Ports and Adapters. Matt Wynne first used it in a Rails context in 2012.
  36. P E R S I S T E N C

    E At a higher layer of architecture, HR is about segmenting responsibilities according to Persistence (the DB)
  37. B U S I N E S S Business (the

    logic that addresses and is unique to your business needs),
  38. I N T E R FA C E and Interface

    (anywhere a human or software driver interacts with the business logic). You might ask why the separation between Persistence, Business, and Interface. We change each of these for different reasons and at a different pace. For instance, the rate at which I decide to swap in a new UI has nothing to do with the release of new Rails versions. So, I prefer not to tangle those things. Some other benefits of this architecture include…
  39. R E U S A B L E L O

    G I C — D RY You put your logic in objects instead of controllers and ActiveRecord Models. It will be re-usable. No more duplication across controllers!
  40. E A S I E R T O U P

    G R A D E R A I L S You won't have to pour hours into repairing the business logic in your controllers and ActiveRecord models every time there's a new Rails version.
  41. B U I L D A N A P I

    Creates an API, enabling you to drive the application equally from multiple points including via a Backbone front-end, the Rails console, unit tests, and rake tasks. It's easier to add a new UI. Substitute any driver with another as long as the required data is fed to the API.
  42. FA S T E R T E S T S

    And lastly, because your logic isn’t bundled into controllers, you can create fast unit tests that never touch the DB, but instead mimic your DB with doubles, hashes, and arrays. So much faster!
  43. G O O U T A N D P L

    AY So, Hexagonal Rails is just one way to meet SRP and organize your code in a larger application so that it’s easier to manage change. Maybe my talk introduced you to some new tools to achieve SRP in your Rails apps, or demystified Hexagonal Rails a little bit. Either way…
  44. S I N G L E R E S P

    O N S I B I L I T Y R A I L S @ W H O I S R A E Thanks so much for listening. If you have any questions or comments, find me after this or on Twitter at @whoisrae.
  45. E X C E L L E N T R

    E S O U R C E S • [Сергей Лепехин, Ben Ten Toy Photos](https://500px.com/Lepser_ru) • [Sandi Metz, Practical Object-Oriented Design in Ruby: An Agile Primer](http://www.poodr.com/) • [Katrina Owens](http://www.confreaks.com/videos/1071-cascadiaruby2012-therapeutic- refactoring) • [Martin Fowler, Replace Method with Method Object](http://refactoring.com/catalog/ replaceMethodWithMethodObject.html) • [Matt Wynn, GoRuCo 2012 Hexagonal Rails](https://www.youtube.com/watch?v=CGN4RFkhH2M) • [Alistar Cockburn, Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) Here are some great resources if you’d like to learn more about some of the things I covered here.