SlideShare a Scribd company logo
1 of 71
Moving to repositories
no golden hammer
Arturs Meisters
Developer at Funding Circle
Classical approach
class LoanSignupController < ApplicationController
def create
loan = Loan.create(params[:loan])
if loan.persisted?
redirect_to loan_path(loan.id)
else
render :new
end
end
end
class Loan < ActiveRecord::Base
validate :amount, :title, persistence: true
end
Simple!
It becomes trickier
class Loan < ActiveRecord::Base
belong_to :borrower
has_many :loan_parts
has_many :loan_part_distributions, through: :loan_parts
has_many :investors
has_many :repayments
validates :amount, :title, :description, presence: true
validates :amount,
numericality: {
greater_than: 1000,
less_than: 100_000
}
end
...that’s without any methods
Why did this happen?
fields in DB that shouldn't be exposed
def show
loan = Loan.find(params[:id])
render :json
end
def show
loan = LoanPresenter.new(Loan.find(params[:id]))
render loan.to_json
end
class LoanPresenter
def initialize(loan)
@loan = loan
end
def to_json
{
id: @loan.id,
amount: @loan.amount.to_money,
title: @loan.title
}
end
end
read-only fields
● attr_accessible
● strong parameters
a lot of validations
user experience that doesn't map to DB
schema directly
def create
user = User.new(params[:user])
if user.valid?
user.save!
redirect_to :user_profile(user)
else
render :new
end
end
def create
user = User.create_with_profile_and_sequrity_questions(params[:user])
# and so on...
end
def create_with_profile_and_sequrity_questions(params)
profile = Profile.new(
first_name: first_name[:first_name],
last_name: params[:last_name]
)
user_params = params.reject{|k,_|
[:first_name, :last_name].include?(k)
}
create(user_params.merge(profile: profile))
end
complex domain logic
lengthy life cycle with many different states
validates :amount, :title, presence: true, if: "active?"
validates :closed_at, presence: true if: "closed?"
validates :recovered_amount, if: "defaulted?"
before :assign_goverment_bid if: "fully_funded?"
So what is active record?
“An object that wraps a row in database table
or view, encapsulates database access, and
adds domain logic on that data.”
Martin Fowler
Patterns of Enterprise Application Architecture (2003)
An object that wraps a row in database table
or view, encapsulates database access, and
adds domain logic on that data.
Martin Fowler
Patterns of Enterprise Application Architecture (2003)
Our ActiveRecord models had a lot of domain
logic in them
… and almost every change in persistence layer
caused problems within domain layer and vice
versa
It is fast and easy to start with ActiveRecord,
but it may not be enough
Moving forward
monolith to SOA
Goals
● process driven application
● manageable chunks of code
● moving in small steps
A way
API dictates how and what data needs to be
returned
class LoanEndpoint
#... skipped code
get "/investor/:id/loans" do
loans = LoanRepository.new.all_for_investor(params[:id])
render(loans)
end
end
separate domain logic from data access layer
class LoanRepository
def all_for_investor(investor_id)
loans = Loan.join(:investor)
.where(investor_id: investor)
loans.map{|loan| map_to_entity(loan) }
end
#... skipped code
end
class LoanRepository
#... skipped code
private
def map_to_entity(record)
Entities::Loan.new.tap do |loan|
loan.id = record.id
loan.title = record.title
loan.amount_cents = (record.amount * 100).to_i
end
end
end
class LoanRepository
#... skipped code
private
def map_to_entity(record)
mapper.map(record)
end
def mapper
@mapper ||= Loan::Mapper.new
end
end
class LoanEntity
attribute :id
attribute :status
attribute :owner_id
attribute :loan_parts
def principal_remaining
if status != "paid"
loan_parts.inject(0){|sum, loan_part|
sum + loan_part.principal_remaining
}
else
0
end
end
end
class LoanValidator < ActiveModel::Validator
MINIMUM_LOAN_AMOUNT = 5_000
def validate(loan_entity)
unless loan.amount >= MINIMUM_LOAN_AMOUNT
loan.errors.add(
:amount,
"must be greater or equal to #{MINIMUM_LOAN_AMOUNT}"
)
end
end
end
resource’s properties are combined from
different sources
class LoanRepository
def all_for_investor(investor_id)
loans = Loan
.join(:investor)
.preload(:borrower)
.where(investor_id: investor_id)
loans.map{|loan| map_to_entity(loan) }
end
#... skipped code
end
class LoanRepository
def all_for_investor(investor_id)
loans = Loan
.join(:investor)
.preload(:borrower)
.where(investor_id: investor_id)
loans.map{|loan| map_to_entity(loan) }
end
#... skipped code
end
class LoanRepository
#... skipped code
private
def map_to_entity(record)
Entities::Loan.new.tap do |loan|
loan.id = record.id
loan.title = record.title
loan.amount_cents = (record.amount * 100).to_i
loan.borrower = Entities::Borrower.new.tap do |borrower|
borrower.id = record.borrower.id
borrower.name = record.borrower.full_name
end
end
end
end
external service as data source
class LoanRepository
def all_for_investor(investor_id)
loans = LoanService.fetch_for_investor(investor_id)
.and_then do |records|
borrower_ids = records.map{|record| record[:borrower_id] }
borrowers = Borrower.find(borrower_ids)
records.map do |record|
map_loan(record, borrowers)
end
end
end
#...skipped code
end
class LoanRepository
#...skipped code
private
def map_loan(loan, borrower)
Entities::Loan.new.tap do |loan|
loan.id = record[:id]
loan.title = record[:title]
loan.amount_cents = record[:amount_cents]
loan.borrower = map_loan_borrower(loan, borrowers)
end
end
def map_loan_borrower(loan, borrowers)
borrower = borrowers.detect{|borrower| loan.borrower_id = borrower.id }
Entities::Borrower.new.tap do |borrower|
borrower.id = record.borrower.id
borrower.name = record.borrower.full_name
end
end
end
more focus on business processes than
resources
class LoanAcceptanceService
def call
end
end
class LoanDefaultingService
def call
end
end
class UserSignUpService
def call
end
end
class SellLenderLoanPart
def initialize(lender_source = nil, loan_part_source = nil)
@lender_source = lender_source || LenderRepository.new
@loan_part_source = loan_part_source || LoanPartRepository.new
end
def call(buyer_id, loan_part_id)
buyer = @lender_source.find(buyer_id)
loan_part = @loan_part_source.find(loan_part_id)
seller_loan_part = loan_part.snapshot
@loan_part_source.create(seller_loan_part.sell)
loan_part.change_ownership_to(buyer)
@loan_part_source.update(loan_part)
end
end
Problems and challenges
● more application layers
● a little less flexibility in how you access data
● mind-shift to work with in-memory objects
● responsibilities of entities
● validations
Repositories
Repository mediates between the domain and
data mapping layers using a collection-like
interface for accessing domain objects
Martin Fowler
Patterns of Enterprise Application Architecture (2003)
Summary
● Now we are able to move in small steps
● Abstract away from data sources
● Process driven applications
but…
● I would never start with repositories
Questions?
https://github.com/kalifs

More Related Content

Similar to Moving to repositiories

The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015Fernando Hamasaki de Amorim
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelpauldix
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelpauldix
 
Engines: Team Development on Rails (2005)
Engines: Team Development on Rails (2005)Engines: Team Development on Rails (2005)
Engines: Team Development on Rails (2005)lazyatom
 
PyCon KR 2018 Effective Tips for Django ORM in Practice
PyCon KR 2018 Effective Tips for Django ORM in PracticePyCon KR 2018 Effective Tips for Django ORM in Practice
PyCon KR 2018 Effective Tips for Django ORM in PracticeSeomgi Han
 
Developing functional domain models with event sourcing (oakjug, sfscala)
Developing functional domain models with event sourcing (oakjug, sfscala)Developing functional domain models with event sourcing (oakjug, sfscala)
Developing functional domain models with event sourcing (oakjug, sfscala)Chris Richardson
 
Dont call me cache java version
Dont call me cache java versionDont call me cache java version
Dont call me cache java versionAvisi B.V.
 
More to RoC weibo
More to RoC weiboMore to RoC weibo
More to RoC weiboshaokun
 
Disregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_FormDisregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_FormDaniel Cousineau
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick SmackdownAlexander Ioffe
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful RailsViget Labs
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful RailsBen Scofield
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperGiordano Scalzo
 
Refactoring in Practice - Sunnyconf 2010
Refactoring in Practice - Sunnyconf 2010Refactoring in Practice - Sunnyconf 2010
Refactoring in Practice - Sunnyconf 2010Alex Sharp
 
Service Objects in Rails apps
Service Objects in Rails appsService Objects in Rails apps
Service Objects in Rails appsDawid Cichuta
 
From CRUD to messages: a true story
From CRUD to messages: a true storyFrom CRUD to messages: a true story
From CRUD to messages: a true storyAlessandro Melchiori
 
Advanced Internationalization with Rails
Advanced Internationalization with RailsAdvanced Internationalization with Rails
Advanced Internationalization with RailsClinton Dreisbach
 
Building and deploying microservices with event sourcing, CQRS and Docker (Me...
Building and deploying microservices with event sourcing, CQRS and Docker (Me...Building and deploying microservices with event sourcing, CQRS and Docker (Me...
Building and deploying microservices with event sourcing, CQRS and Docker (Me...Chris Richardson
 

Similar to Moving to repositiories (20)

The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModel
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModel
 
Engines: Team Development on Rails (2005)
Engines: Team Development on Rails (2005)Engines: Team Development on Rails (2005)
Engines: Team Development on Rails (2005)
 
PyCon KR 2018 Effective Tips for Django ORM in Practice
PyCon KR 2018 Effective Tips for Django ORM in PracticePyCon KR 2018 Effective Tips for Django ORM in Practice
PyCon KR 2018 Effective Tips for Django ORM in Practice
 
Developing functional domain models with event sourcing (oakjug, sfscala)
Developing functional domain models with event sourcing (oakjug, sfscala)Developing functional domain models with event sourcing (oakjug, sfscala)
Developing functional domain models with event sourcing (oakjug, sfscala)
 
Dont call me cache java version
Dont call me cache java versionDont call me cache java version
Dont call me cache java version
 
More to RoC weibo
More to RoC weiboMore to RoC weibo
More to RoC weibo
 
Disregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_FormDisregard Inputs, Acquire Zend_Form
Disregard Inputs, Acquire Zend_Form
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick Smackdown
 
SOLID Ruby SOLID Rails
SOLID Ruby SOLID RailsSOLID Ruby SOLID Rails
SOLID Ruby SOLID Rails
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful Rails
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful Rails
 
Introduction to-ddd
Introduction to-dddIntroduction to-ddd
Introduction to-ddd
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapper
 
Refactoring in Practice - Sunnyconf 2010
Refactoring in Practice - Sunnyconf 2010Refactoring in Practice - Sunnyconf 2010
Refactoring in Practice - Sunnyconf 2010
 
Service Objects in Rails apps
Service Objects in Rails appsService Objects in Rails apps
Service Objects in Rails apps
 
From CRUD to messages: a true story
From CRUD to messages: a true storyFrom CRUD to messages: a true story
From CRUD to messages: a true story
 
Advanced Internationalization with Rails
Advanced Internationalization with RailsAdvanced Internationalization with Rails
Advanced Internationalization with Rails
 
Building and deploying microservices with event sourcing, CQRS and Docker (Me...
Building and deploying microservices with event sourcing, CQRS and Docker (Me...Building and deploying microservices with event sourcing, CQRS and Docker (Me...
Building and deploying microservices with event sourcing, CQRS and Docker (Me...
 

Recently uploaded

Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 

Recently uploaded (20)

Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 

Moving to repositiories

  • 3.
  • 5.
  • 6. class LoanSignupController < ApplicationController def create loan = Loan.create(params[:loan]) if loan.persisted? redirect_to loan_path(loan.id) else render :new end end end class Loan < ActiveRecord::Base validate :amount, :title, persistence: true end
  • 9. class Loan < ActiveRecord::Base belong_to :borrower has_many :loan_parts has_many :loan_part_distributions, through: :loan_parts has_many :investors has_many :repayments validates :amount, :title, :description, presence: true validates :amount, numericality: { greater_than: 1000, less_than: 100_000 } end
  • 10.
  • 12. Why did this happen?
  • 13. fields in DB that shouldn't be exposed
  • 14. def show loan = Loan.find(params[:id]) render :json end
  • 15. def show loan = LoanPresenter.new(Loan.find(params[:id])) render loan.to_json end class LoanPresenter def initialize(loan) @loan = loan end def to_json { id: @loan.id, amount: @loan.amount.to_money, title: @loan.title } end end
  • 18. a lot of validations
  • 19. user experience that doesn't map to DB schema directly
  • 20. def create user = User.new(params[:user]) if user.valid? user.save! redirect_to :user_profile(user) else render :new end end
  • 21. def create user = User.create_with_profile_and_sequrity_questions(params[:user]) # and so on... end def create_with_profile_and_sequrity_questions(params) profile = Profile.new( first_name: first_name[:first_name], last_name: params[:last_name] ) user_params = params.reject{|k,_| [:first_name, :last_name].include?(k) } create(user_params.merge(profile: profile)) end
  • 23. lengthy life cycle with many different states
  • 24. validates :amount, :title, presence: true, if: "active?" validates :closed_at, presence: true if: "closed?" validates :recovered_amount, if: "defaulted?" before :assign_goverment_bid if: "fully_funded?"
  • 25. So what is active record?
  • 26. “An object that wraps a row in database table or view, encapsulates database access, and adds domain logic on that data.” Martin Fowler Patterns of Enterprise Application Architecture (2003)
  • 27. An object that wraps a row in database table or view, encapsulates database access, and adds domain logic on that data. Martin Fowler Patterns of Enterprise Application Architecture (2003)
  • 28. Our ActiveRecord models had a lot of domain logic in them
  • 29. … and almost every change in persistence layer caused problems within domain layer and vice versa
  • 30. It is fast and easy to start with ActiveRecord, but it may not be enough
  • 33.
  • 34.
  • 35.
  • 36.
  • 37. Goals ● process driven application ● manageable chunks of code ● moving in small steps
  • 38.
  • 39.
  • 40. A way
  • 41.
  • 42.
  • 43.
  • 44.
  • 45. API dictates how and what data needs to be returned
  • 46.
  • 47. class LoanEndpoint #... skipped code get "/investor/:id/loans" do loans = LoanRepository.new.all_for_investor(params[:id]) render(loans) end end
  • 48. separate domain logic from data access layer
  • 49.
  • 50. class LoanRepository def all_for_investor(investor_id) loans = Loan.join(:investor) .where(investor_id: investor) loans.map{|loan| map_to_entity(loan) } end #... skipped code end
  • 51. class LoanRepository #... skipped code private def map_to_entity(record) Entities::Loan.new.tap do |loan| loan.id = record.id loan.title = record.title loan.amount_cents = (record.amount * 100).to_i end end end
  • 52. class LoanRepository #... skipped code private def map_to_entity(record) mapper.map(record) end def mapper @mapper ||= Loan::Mapper.new end end
  • 53. class LoanEntity attribute :id attribute :status attribute :owner_id attribute :loan_parts def principal_remaining if status != "paid" loan_parts.inject(0){|sum, loan_part| sum + loan_part.principal_remaining } else 0 end end end
  • 54. class LoanValidator < ActiveModel::Validator MINIMUM_LOAN_AMOUNT = 5_000 def validate(loan_entity) unless loan.amount >= MINIMUM_LOAN_AMOUNT loan.errors.add( :amount, "must be greater or equal to #{MINIMUM_LOAN_AMOUNT}" ) end end end
  • 55. resource’s properties are combined from different sources
  • 56.
  • 57. class LoanRepository def all_for_investor(investor_id) loans = Loan .join(:investor) .preload(:borrower) .where(investor_id: investor_id) loans.map{|loan| map_to_entity(loan) } end #... skipped code end
  • 58. class LoanRepository def all_for_investor(investor_id) loans = Loan .join(:investor) .preload(:borrower) .where(investor_id: investor_id) loans.map{|loan| map_to_entity(loan) } end #... skipped code end
  • 59. class LoanRepository #... skipped code private def map_to_entity(record) Entities::Loan.new.tap do |loan| loan.id = record.id loan.title = record.title loan.amount_cents = (record.amount * 100).to_i loan.borrower = Entities::Borrower.new.tap do |borrower| borrower.id = record.borrower.id borrower.name = record.borrower.full_name end end end end
  • 60. external service as data source
  • 61. class LoanRepository def all_for_investor(investor_id) loans = LoanService.fetch_for_investor(investor_id) .and_then do |records| borrower_ids = records.map{|record| record[:borrower_id] } borrowers = Borrower.find(borrower_ids) records.map do |record| map_loan(record, borrowers) end end end #...skipped code end
  • 62. class LoanRepository #...skipped code private def map_loan(loan, borrower) Entities::Loan.new.tap do |loan| loan.id = record[:id] loan.title = record[:title] loan.amount_cents = record[:amount_cents] loan.borrower = map_loan_borrower(loan, borrowers) end end def map_loan_borrower(loan, borrowers) borrower = borrowers.detect{|borrower| loan.borrower_id = borrower.id } Entities::Borrower.new.tap do |borrower| borrower.id = record.borrower.id borrower.name = record.borrower.full_name end end end
  • 63. more focus on business processes than resources
  • 64.
  • 65. class LoanAcceptanceService def call end end class LoanDefaultingService def call end end class UserSignUpService def call end end
  • 66. class SellLenderLoanPart def initialize(lender_source = nil, loan_part_source = nil) @lender_source = lender_source || LenderRepository.new @loan_part_source = loan_part_source || LoanPartRepository.new end def call(buyer_id, loan_part_id) buyer = @lender_source.find(buyer_id) loan_part = @loan_part_source.find(loan_part_id) seller_loan_part = loan_part.snapshot @loan_part_source.create(seller_loan_part.sell) loan_part.change_ownership_to(buyer) @loan_part_source.update(loan_part) end end
  • 67. Problems and challenges ● more application layers ● a little less flexibility in how you access data ● mind-shift to work with in-memory objects ● responsibilities of entities ● validations
  • 69. Repository mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects Martin Fowler Patterns of Enterprise Application Architecture (2003)
  • 70. Summary ● Now we are able to move in small steps ● Abstract away from data sources ● Process driven applications but… ● I would never start with repositories

Editor's Notes

  1. consider moving this up, before examples
  2. because of testing because everything touch db because of chain calls from one method to another in models
  3. migration to SOA before this slide - maybe one slide
  4. straight forward mapping
  5. straight forward mapping
  6. straight forward mapping
  7. straight forward mapping
  8. new problems that this pattern introduces
  9. desired end goal where we want to be after x months
  10. performance issues, persistance, validation problems we have. to go back to ideas slide and show how this solves it . summarize