[practical] functional programming in rails
DESCRIPTION
Tired of writing messy messy "if" statements for all your exception handling? Learn how to take full advantage of functional programming theory, without the theory, Ruby-style! https://github.com/mindeavor/solid_use_caseTRANSCRIPT
FUNCTIONALPROGRAMMINGPr
actical!
RAILSRUBYIN
ON
BY
Overview
• What is a Service Layer? • Railway Oriented Programming • Creating Beautifully Composable Services
What is a
LAYER?
SERVICE
What is a
LAYER
SERVICE??
A Service is an object that represents a process.
A process is something you do.
• Typically initiated by the user • Usually implemented as a dedicated class • Named with verbs, not nouns • Examples: SignupUser, PurchaseItem, etc.
SERVICES
SignupUser
Purchase Item
RAILS
SERVICES
SERVICE LAYER
Payout Account
A Service in Rails
SERVICES
CODE EXAMPLE
A ServiceUsage in Controller
[Naive] ValidationsCODE EXAMPLE
A Service
Usage in Controller
SERVICES
Why a Service useful?
• It encapsulates domain logic • It thins out your models & controllers • It ties together cross-cutting concerns • It makes your Rails app easier to test.
SERVICES
A service layer is about clear separation.
• Rails depends on the service layer • The service layer knows nothing about Rails (routes, controllers, & views)
• Other things could depend on the service layer (REST API, web sockets, rake tasks, etc.)
• ActiveRecord is unavoidable, however.SERVICES
SERVICES
Quick Recap
• A service is an object that represents a process • Services encapsulate domain logic for specific
processes • No functional programming (yet).
SERVICES
RAILWAY ORIENTED
PROGRAMMING
http://fsharpforfunandprofit.com/posts/recipe-part2/
Services: Another Perspective
A service is a sequence of steps • They often involve a lot of setup • Authentication, authorization, validation, etc.
Any one of these steps could fail • How to handle failure??
RAILWAY
Naive ValidationsCODE EXAMPLE (REVISITED)
A Service RAILWAY
Can we do be!er?
RAILWAY
What if each step was an independent method?
How would each step communicate?
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming RAILWAY
The “Switch”
Input → Success!
Failure.
fetch_item
DIAGRAM
RAILWAY
The “Switch”
Input → Success!
Failure
fetch_item
CODE EXAMPLE
RAILWAY
The “Switch”
Input → Success!
Failure.
fetch_item
CODE EXAMPLE
Failure.Success!
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming RAILWAY
Our Target Flow
on successSuccess!
Failure.
Input →
fetch_item validate
on failure
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming RAILWAY
Our Target Flow
Success!Failure.
Input →create_ordervalidatefetch_item
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming RAILWAY
Our Target Flow
Success!Failure.
Input →create_ordervalidatefetch_item
IT’D BE COOL IF WE COULD CONNECT THESE TOGETHER
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming RAILWAY
Connecting Switches
Success!Failure.
Input →create_ordervalidatefetch_item
create_ordervalidatefetch_item
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming RAILWAY
Connecting Switches
Success!Failure.
Input →create_ordervalidatefetch_item
create_ordervalidatefetch_item
create_ordervalidatefetch_item
Success!Failure.
RAILWAY
Connecting SwitchesCODE EXAMPLES
create_ordervalidatefetch_item
?? ??
RAILWAY
Connecting SwitchesCODE EXAMPLES
create_ordervalidatefetch_item
?? ??
HOW DO WE CONNECTIT ALL TOGETHER?
? ????
??
?
RAILWAY
Answer:
RAILWAY
¯\_(ツ)_/¯ Answer:
RAILWAY
¯\_(ツ)_/¯ Answer:
IT DOESN’T MATTER!
RAILWAY
¯\_(ツ)_/¯ Answer:
IT DOESN’T MATTER! :D
RAILWAY
¯\_(ツ)_/¯ Answer:
IT DOESN’T MATTER! :D
(actual answer: monads)
There’s a gem for that!Doesn’t matter.
RAILWAY
github.com/mindeavor/solid_use_case
There’s a gem for that!Don’t worry.
Handling failures?
RAILWAY
What about
github.com/mindeavor/solid_use_case
Quick Recap
• Split steps into methods • Return a success or failure in each one • Elegant (with gem) error handling! • Steps can be tested independently
RAILWAY
COMPOSING SERVICES
FOR MAXIMUM COOLNESS
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming COMPOSE
Remember This?
Success!Failure
Input →create_ordervalidatefetch_item
create_ordervalidatefetch_item
create_ordervalidatefetch_item
Success!Failure
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming COMPOSE
Input → Success!Failure
create_ordervalidatefetch_item
What if we treat it like a single thing…
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming COMPOSE
Input → Success!FailurePurchaseItem
What if we treat it like a single thing…
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming COMPOSE
Input → PurchaseItem Success!Failure
What if we treat it like a single thing…
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming COMPOSE
…and then connect it to other things??
Input →PurchaseItem
Success!
Failure
Source: http://www.slideshare.net/ScottWlaschin/railway-oriented-programming COMPOSE
Input →PurchaseItem EmailReceipt
Success!
Failure
…and then connect it to other things??
Service CompositionCODE EXAMPLE
COMPOSE
Service CompositionCODE EXAMPLE
Another Service!
COMPOSE
Service CompositionCODE EXAMPLE
Another Service!
COMPOSE
Recap
• We can compose services like we can steps (in fact we can compose anything that returns our success or failure)
• Functional programming rocks!
COMPOSE
Conclusion
• Service layers are great for apps more complex than CRUD
• Railway oriented programming is great for seamless error handling
• Functional programming gives us composable services!
In the end, don’t take my word for it.
TRY IT YOURSELF!
Thanks!Hurrah!
END.THE
BY
- Gilbert (@mindeavor)
Further Reading
• http://martinfowler.com/eaaCatalog/serviceLayer.html • https://gist.github.com/blaix/5764401 • http://fsharpforfunandprofit.com/posts/recipe-part2/ • The gem: https://github.com/mindeavor/solid_use_case