25
Refactoring Legacy Code Learnings from RedBubble Paul Coia

Refactoring product model

Embed Size (px)

DESCRIPTION

A presentation given in early 2011 at Ruby on Rails Oceania about a conceptual refactoring of legacy code written to handle a reality that didn't eventuate.

Citation preview

Page 1: Refactoring product model

Refactoring Legacy CodeLearnings from RedBubblePaul Coia

Page 2: Refactoring product model
Page 3: Refactoring product model

What is Legacy Code?Untested code

Dead code

Large classes/methods

Spaghetti architecture

Models too rigid to meet new requirements

Page 4: Refactoring product model

Product model

Page 5: Refactoring product model

Enumeration table

Print Size,Frame Colour,Matte Colour,Frame Style

Small, Medium,Large,Red,etc.

FramedPrint

Page 6: Refactoring product model

Enumeration tableEasy to add new products or options

No new columns, just insert rowsNo code changes

Can query against it“Find all artworks available on red t-shirts”

Populate web form elements easily

Page 7: Refactoring product model

Domain model is in the DBProduct class is a generic type

What about domain logic?

‘Instances’ need a separate set of tables

Page 8: Refactoring product model

Buying a product

Page 9: Refactoring product model

Preparing the product formframed_print = ProductType.find_by_name(“FramedPrint”)

# Get the Framed Print productfp_product = artwork.products.detect do |p| p.product_type == framed_printendaovs = fp_product.available_option_values

# Group these into the optionsoptions_hash = {}aovs.each do |aov| options_hash[aov.option_value.option] << aov.option_valueend

# Order the option values, so we have S, M, L, etc.# Populate form elements

Page 10: Refactoring product model

SQL view1. Load products by artwork_id2. Load available_product_options by

product_id3. Load option_values by option_value_id4. Load options by option_id

Page 11: Refactoring product model

Troublesome requirementsSome products have default

optionsT-Shirt default colour and style

Options that are not constrainedCalendar start month

Inter-option constraintsLongsleeve Tee colours

Page 12: Refactoring product model

Default optionsFlag on available_product_optionNeed to enforce one default per

option type

Render artwork with default options

Page 13: Refactoring product model

Rendering default options

Page 14: Refactoring product model

Rendering the defaults# Get the default producttee_product = artwork.get_default_product

aovs = tee_product.available_option_values

defaults = aovs.select {|aov| aov.default? }

# Group these into the options (assuming one per option)options_hash = {}defaults.each do |aov| options_hash[aov.option_value.option] << aov.option_valueend

# Need to validate that we have all required defaults# and only one per option, etc.

Page 15: Refactoring product model

SQL view1. Load products by product_id2. Load available_product_options by

product_id3. Load option_values by option_value_id4. Load options by option_id

Same joins as the first example

Page 16: Refactoring product model

Rendering lots of products!

Page 17: Refactoring product model

Data volumes

Works Products Available Option Values0

50000000

100000000

150000000

200000000

250000000

300000000

350000000

Page 18: Refactoring product model

Inter option constraintsRoundneck T-shirts can have all

colours

V-Neck can only have a few

How do you model that in the DB?Do you want to?

Page 19: Refactoring product model

Original driverWant to add new products weeklyReally?

Real-world constraintsProduct preparation takes monthsCannot cover all eventualitiesNew products always required new

code anyway

Page 20: Refactoring product model

Alternative approachModel product types as classesDefine options and constraints

(DSL)

Available products is a list of keys on Artwork

Available product options is a hash on the ArtworkSo is the default options

Page 21: Refactoring product model

Product classclass TShirtProduct option Style, "mens", "T-Shirt" option Style, "vneck", "V-Neck T-Shirt" option Style, "longsleeve", "Long Sleeve T-Shirt" option Style, "womens", "Girly Fitted T-Shirt" option Style, "mhoodie", "Hoodie"

option Size, "xs", "XS" option Size, "small", "Small" option Size, "medium", "Medium” ...

AVAILABLE_COLORS = { "mens" => all_colors, "vneck" => [”black”, ”grey”, "navy", "white”] }

end

Page 22: Refactoring product model

Database model

Page 23: Refactoring product model

What have we gained?An expressive, flexible product

modelSimpler code, overallActually easier to add products nowAnd easier to modify existing ones

Nearly 25% average speed increase

Page 24: Refactoring product model

What have we lost?“Find all red t-shirts”Utilise a search engine

“How many large framed prints have we sold?”Reporting via Data Warehouse

Online product addition

100s of millions of rows from the DB!

Page 25: Refactoring product model