Ask A Question

Notifications

You’re not receiving notifications from this thread.

Single Responsibility Principle Discussion

Great series. I would love if you continue with this, plus with testing. Testing is something that you haven't really touched upon much here. 
Also with some design patterns that follow along with this principles.
Reply
We will finally be diving into testing this year soon. 🤓
Reply
+1
Reply
+1

Reply
Very nice, Chris. It's helpful to hear and see how principles and best practices translate into real world examples. It's easy to fall down a rabbit hole and more difficult to dig yourself out than it is to avoid these pitfalls in the first place.  Looking forward to the rest of this series. Also, congrats on Hatch.io. I will be making the move in the near future.
Reply
Thanks Ron!
Reply
Great episode.  Look forward to seeing more!  I do something similar, but for domain objects, I generally namespace them by functionality and then throw it in lib.  app/ for rails specific stuff /lib for domain stuff
Reply
Great one! Would be great to see a series of all the famous design patterns from Design Patterns in Ruby book with Rails specific implementation.
Reply
I am glad you are doing this SOLID series.  

Awesome content.
Reply
I don't want to sound harsh, but please explain in what Universe is it appropriate to use Rails to configure servers, nginx, SSL, Sidekiq, when there is Chef (ruby), Puppet (ruby), Ansible (python/yaml), Salt (python), and Terraform (C/C++/HashiCorp), not to mention Docker (go) — that already do that incredibly well with a wide community support to pull from? To say that this example comes from a real-world Rails app, and then to proceed to describe what is effectively a very basic cloud automation — as an appropriate use of Rails Web Framework, is (IMHO) ludicrous. It completely flies in the face of the design principles you are touting. Perhaps it would help if you spent a bit of time explaining why Rails is used in such an unusual context.
Reply
I think this comment misses the mark, you should use whatever tool you'd like to solve your problems. 

Chef/Ansible/Puppet/Salt/Terraform are all added DSLs/other things to learn. If all you need is a basic automation tool, and you're not going to need more, than doing it in Rails will be fine. 

Looking forward to seeing your videos on those other tools for how you would do it. 
Reply
You don't sound harsh - just badly researched. Chris Oliver is literally the proprietor of an instant-on server configuration company that is an alternative to choosing Chef/Puppet/Ansible et al. It's like telling a young Sergey Brin he should just use Alta Vista because no-one needs another search engine.

Your second mistake was thinking that the application mattered, when clearly it just serves as a vehicle for illustrating the SRP.

Your final mistake is in looking at the result and seeing Rails, when one of the major outcomes by the end of the episode is that the provisioning code is now decoupled from Rails.

Reply
Hi Konstantin, welcome to the gorails community.
It seems like you've completely misunderstood the point of this video and point of hatchbox.io. (without meaning to sound harsh of course).
To answer your question, it is perfectly appropriate to use rails when building something like hatchbox.io when you're a rails developer. In fact it would be a strange decision to build it in anything else, do you not agree?
To say this flies in the face of the Single Responsibility Principle suggests you've not yet grasped it but keep trying, we're all here to learn! Good luck with your development! 
Reply
Great episode!

Some people might misunderstand what happened here and end up with the (unfortunately common) misunderstanding that SRP means "one method per class".

What Chris did is refactor a set of domain-specific behaviours from being collected in one class, to each being classes in their own right. The next step might be to compose them back together as necessary, without over-engineering the infrastructure that does so.

The example I personally like to give is extracting business rules implemented as "just code" into Rule classes, and then delivering new and useful outcomes by re-ordering rule objects or using subclasses and variants.

For the Rails programmer this is hopefully all in contrast to using Concerns which are basically just a way to chop up fat models into separate files and don't lead to new and interesting runtime structures.

Reply

Interesting. I'm having trouble knowing how small to make my classes. I've seen different articles saying you should only have one public method per class.

Should I create one PriceCalculator service where I can ask it for things like sale_price, material_cost, fixed cost, variable_cost, production_cost...

Or should I make independant classes for each of these? That way sale_price would be one calling the other classes to get to the final price.

My biggest concern separating them all into classes is that material_cost, for example, is is called in different classes, repeting calculations that I have already done.

Under one big SalePrice class, I avoided calculating the same thing multiple times because I would set class variables that would be accessible by all methods.

Reply

I usually anticipate having more than one such calculator, because very often, pricing is a classic case for the Strategy pattern. This immediately informs the granularity because each strategy has a name, and the methods are the things that might vary from one namable strategy to the next.

A typical scenario is a tax calculator. A given invoice has a specific tax treatment depending on the customer or the location. The calculator object is instantiated for the invoice and embodies all the expertise necessary to be responsible for answering tax-related questions such as rate per item, handles special rates, due dates, can list the available rates, and calculates various answers as required.

That is one responsibility: it represents the domain knowledge of a specific tax treatment.

One-method-per-class is a naive rule for SRP that takes a mechanical, not domain-centric view of software and I disregard any such advice. Its a slippery slope from there into service objects and other antipatterns.

Chris's objects in the screencast naturally gravitate to one method, not due to the SRP, but because he's also implementing the command pattern for which the fundamental method is usually #perform. Notwithstanding which, command pattern implementations often have a bunch of other methods because they may also participate in a framework with logging, transactions, progress reporting, undo etc.

NB: for calculators, I'd suggest at most one instantiation parameter, usually a domain entity, and avoid internal state except for that entity object and maybe some value caching, because responses from calculators like should be consistent and nullipotent, or at least idempotent.

Reply
Bravo Chris! You did excellent job. I am waiting for next parts 
Reply
Thanks, great and really interesting episode  (as always...) !
I have two questions: 
1) What do you think about turning the Server::SSH class into a module, and mixing it in into the two other sever classes (instead of using inheritance).
2) Using service objects to do stuff is a similar technique right ?

Thanks
Reply

I agree with you on the first point. Assuming Server::SSH class represents an SSH connection, the two other server classes are not really "types" of an SSH connection. So it's probably better to use a module to extract the common start_ssh behavior and then include that module in the server classes.

Reply
Great series Chris!
Reply
Amazing. Thanks !
Reply

Fantastic, Chris! Thanks!

Reply

How do we organize the model folder according to this example ? Should we make another folder app/scripts or whatever ? How these new classes are imported into models in rails ? I am trying to reorganize my app according to this rule but I run into unknown variable problems. Thanks for your help !

Reply

if you placed those classes in app/scripts wouldn't you have to wrap them in a module Scripts?

Reply

No, top level under app is not namespaced. Same reason why app/models/users doesn't get wrapped in a Models module.

Reply

🤦🏻‍♂️ oh yeah. Thanks.

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 86,946+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.