clean architecture @ 1º hanami sao paulo meetup

21
Clean Architecture Architecture driven by intent

Upload: ruby-hanami-sao-paulo-meetups

Post on 08-Apr-2017

193 views

Category:

Software


2 download

TRANSCRIPT

Clean ArchitectureArchitecture driven by intent

Why?

Why?

• Independent of Frameworks

• Testable

• Independent of UI

• Independent of Database

https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

Communicate intent through

source code

How?

How?Separation of concerns

https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

The Dependency Rule

"...source code dependencies can

only point inwards."

https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

Show me the code!At least some of it.

This user knows too much

class User < ActiveRecord::Base # ...

def password_required? # Password is required if it is being set, but not for new records if !persisted? false else !password.nil? || !password_confirmation.nil? end endend

https://github.com/plataformatec/devise/wiki/How-To:-Override-confirmations-so-users-can-pick-their-own-passwords-as-part-of-confirmation-activation

Interactors for the rescue

class EmailSignUp < Interactor def call(request, response) # ... validate data without password # ... application rules through domain objects # ... build response endend

class PasswordSignUp < Interactor def call(request, response) # ... validate data, password included # ... application rules through domain objects # ... build response endend

What is this?def update with_unconfirmed_confirmable do if @confirmable.has_no_password? @confirmable.attempt_set_password(params[:user]) if @confirmable.valid? and @confirmable.password_match? do_confirm else do_show @confirmable.errors.clear #so that we wont render :new end else @confirmable.errors.add(:email, :password_already_set) end end

if [email protected]? self.resource = @confirmable render 'devise/confirmations/new' #Change this if you don't have the views on default path endend

https://github.com/plataformatec/devise/wiki/How-To:-Override-confirmations-so-users-can-pick-their-own-passwords-as-part-of-confirmation-activation

Cleaning it up!class Controller def update @presenter = build_response interactor.call(user_params, @presenter)

if @presenter.valid? redirect_to dashboard_path else render :new end endend

class SetPassword < Interactor def call(request, response) # ... validate data and fetch user user.set_password(request[:password]) # ... save user and other application rules endend

class User def set_password(new_password) raise PasswordAlreadySetError unless encrypted_password.blank?

self.encrypted_password = encrypt(new_password) endend

Business Objectclass User def set_password(new_password) raise PasswordAlreadySetError unless encrypted_password.blank?

self.encrypted_password = encrypt(new_password) endend

Use caseclass SetPassword < Interactor def call(request, response) # ... validate data and fetch user user.set_password(request[:password]) # ... save user and other application rules endend

Delivery mechanismclass Controller def update @presenter = build_response interactor.call(user_params, @presenter)

if @presenter.valid? redirect_to dashboard_path else render :new end endend

Interface adapter

Wrap up

Wrap up• Your application should be framework

agnostic

• Your application should be infrastructure agnostic

• Web is a delivery mechanism (so is a rake task, the console)

• Testing should be fast

And what about Hanami?

And what about Hanami?

• Web delivery: Web::Controllers

• Use cases: Hanami::Interactor

• Infrastructure: Hanami::Repository

• Business objects: Hanami::Entity

Thank you very much!

Questions?