Relato Sobre a Migração de uma Aplicação Legada para Rails

3,135 views
3,050 views

Published on

Palestra apresentada no dia 10/04/2010 no 8o Encontro do Grupo de Usuários Ruby de São Paulo, na Caelum.

Published in: Technology
1 Comment
12 Likes
Statistics
Notes
No Downloads
Views
Total views
3,135
On SlideShare
0
From Embeds
0
Number of Embeds
236
Actions
Shares
0
Downloads
78
Comments
1
Likes
12
Embeds 0
No embeds

No notes for slide











































































  • Relato Sobre a Migração de uma Aplicação Legada para Rails

    1. 1. Reescrevendo em Rails uma grande aplicação legada Um relato sobre as dificuldades encontradas Cássio Marques
    2. 2. http://surgeworks.com
    3. 3. cassiomarques.wordpress.com @cassiomarques cassiommc@gmail.com
    4. 4. Mundo ideal...
    5. 5. http://www.flickr.com/photos/venteco
    6. 6. http://www.flickr.com/photos/epleitez/
    7. 7. Migração de uma grande aplicação desktop para Rails
    8. 8. Aplicação para Controle de Serviços de Hemoterapia
    9. 9. • Desktop • C++ (Qt 3) • PostgreSQL • +150k loc em C++ • +60k loc em PL/PGSQL
    10. 10. Porque migrar? • Complexidade da linguagem • Dificuldade de integração com outras aplicações. • Dificuldade para acessar remotamente • Dificuldade para manutenção: nada de testes, +5 min para compilar, SPs, etc...
    11. 11. Rails? Ruby? WTF? • Quem programa com isso? • Não haverá quem dê manutenção • Não há uma empresa “por trás” do Ruby
    12. 12. O que eles não sabiam... • Já existiam 4 outras aplicações menores em produção escritas em Ruby • A dificuldade de encontrar um profissional bom em Java/C#/Whatever é a mesma de encontrar um bom em Ruby • Produtividade! • Comunidade open-source
    13. 13. O maior vilão de toda essa história...
    14. 14. Banco de Dados.
    15. 15. ActiveRecord
    16. 16. Ótimo se você seguir as convenções...
    17. 17. ... mas ainda interessante mesmo que você não as siga.
    18. 18. 1 class Product < ActiveRecord::Base 2   set_table_name 'produto' 3   set_primary_key 'codigo' 4 end
    19. 19. Bancos legados podem usar nomes de colunas estranhos...
    20. 20. estoquequantidade fornecedorcnpj requisicaoficha cobrancacodigo
    21. 21. Ou ainda mais estranhos...
    22. 22. PRD_CDG STT_USR codigoproduto codigoproduto_novo codigoproduto_novo2
    23. 23. 1 class Product < ActiveRecord::Base 2   alias_attribute 'description', 'produtodescricao' 3 end
    24. 24. Não funciona para queries...
    25. 25.  2 alias_attribute 'expiration_date', 'validade'  3 named_scope :expired,  4   :conditions => ['expiration_date <= ?', Time.now]  5 # PGError: ERROR: column "expiration_date" does not exist  6  7  8 class Hemocomponent < ActiveRecord::Base  9   alias_attribute 'acronym', 'sigla' 10 end 11 Hemocomponent.find_by_acronym 'CHE' 12 # undefined method `find_by_acronym' for #<Class:0x1033f2380>
    26. 26. Associações
    27. 27. 1 has_many :donations, 2          :class_name => 'Donation', 3          :foreign_key => 'doador_codigo' 4 5 belongs_to :donor, 6            :class_name => 'Donor', 7            :foreign_key => 'doador_codigo'
    28. 28. Views no banco?
    29. 29. +200 tabelas c/ média de 15 campos cada
    30. 30. VIEW MAPPING HELL
    31. 31. Três opções:
    32. 32. Conviver com as diferenças
    33. 33. Criar um novo banco e migrar os dados depois
    34. 34. Alterar o nome das tabelas e colunas
    35. 35. Criar base development
    36. 36. RAILS_ENV=production rake db:schema:dump rake db:schema:load
    37. 37. Duas bases para desenvolvimento
    38. 38. 1) development “normal” 2) dump da base de produção (com dados)
    39. 39. Para alterar nomes...
    40. 40.  1 class RenameDonationTableAndColumns < ActiveRecord::Migration  2   def self.up  3     rename_table :doacao, :donations  4     change_table :donations do |t|  5       t.rename :codigo, :id  6       t.rename :doador, :donor_id  7       t.rename :data, :date  8       t.rename :tipo_sanguineo, :blood_type  9       # ... 10     end 11   end 12 13   def self.down 14     raise IrreversibleMigration 15   end 16 end
    41. 41. Redefinir tipos dos campos
    42. 42.  1 class ChangeDonorsColumns < ActiveRecord::Migration  2   def self.up  3     change_table :donors do |t|  4       t.change :document_type, :string, :size => 15  5       t.change :document_number, :string, :size => 15  6     end  7   end  8  9   def self.down 10     raise ActiveRecord::IrreversibleMigration 11   end 12 end
    43. 43. Crie seus próprios métodos para migrations
    44. 44.  1 module ActiveRecord  2   module ConnectionAdapters  3     class PostgreSQLAdapter < AbstractAdapter  4       def change_sn_to_boolean(table_name, column_name)  5         execute %Q{  6           ALTER TABLE #{quote_table_name(table_name)}   7           ALTER COLUMN #{quote_column_name(column_name)}   8           type boolean  9           using (case when #{quote_column_name(column_name)} = 'S' 10           then true else false end) 11         } 12       end 13     end 14   end 15 end
    45. 45. 58 change_sn_to_boolean :donors, :send_sms
    46. 46. chaves estrangeiras
    47. 47. foreign_key_migrations composite_primary_keys
    48. 48. Alterar dados com migrations
    49. 49. Errado
    50. 50.  1 class PopulateAddressPrefixes < ActiveRecord::Migration  2   def self.up  3     Address.all.each do |address|   4       if address.prefix.blank?  5         address.update_attributes(  6           :prefix => AddressPrefix.find_by_code('R')  7         )  8       end  9     end 10   end 11 end
    51. 51. Melhor
    52. 52. 1 class PopulateAddressPrefixes < ActiveRecord::Migration 2   def self.up 3     prefix = AddressPrefix.find_by_code 'R' 4     execute <<-SQL 5       update addresses set prefix_id = E'#{prefix.id}' 6       where prefix_id is null 7     SQL 8   end 9 end
    53. 53. Enumerações no banco
    54. 54. foobar=# select * from education_level;    id     |  description     --------+---------------    1      | Nao Cursou      2      | 1o Incompleto    3      | 1o Completo      4      | 2o Incompleto    5      | 2o Completo      6      | 3o Incompleto  7      | 3o Completo  
    55. 55. 1 donor.education_level.description # => join no banco!
    56. 56. ok, vamos tirar as enumerações do banco...
    57. 57. problema com FK’s (tabelas com enumerações ainda referenciadas)
    58. 58. 1 donor.education_level = 5 # WTF?
    59. 59. enumerate_it github.com/cassiomarques/enumerate_it (jabá mode on)
    60. 60.  1 class EducationLevel < EnumerateIt::Base  2   associate_values(  3     :none                 => ['1', 'Não Cursou']   ,  4     :incomplete_1st_grade => ['2', '1º Grau Incompleto'],  5     :complete_1st_grade   => ['3', '1º Grau Completo'],  6     :incomplete_2nd_grade => ['4', '2º Incompleto'],  7     :complete_2nd_grade   => ['5', '2º Completo'],  8     :incomplete_3rd_grade => ['6', '3º Grau Incompleto'],  9     :complete_3rd_grade   => ['7', '3º Grau Completo'] 10   ) 11 end
    61. 61. 1 class Donor < ActiveRecord::Base 2   has_enumeration_for :education_level, :with => EducationLevel 3 end
    62. 62. 1 donor.education_level = EducationLevel::COMPLETE_3RD_GRADE 2 donor.education_level_humanize # 3º Grau Completo (sem join)
    63. 63. 1 <% form_for @donor do |f| %> 2   <%= f.select :education_level, EducationLevel.to_a %> 3 <% end -%>
    64. 64. schema.rb => banco de testes
    65. 65. ... mas se a sua PK for varchar...
    66. 66. 1 create_table "donors", :id => false, :force => true do |t| 3   t.string  "id", :limit => 6, :null => false 4   # ... 5 end
    67. 67. 1 class CreateAndAssignDonorsIdSequence < ActiveRecord::Migration 2   def self.up 3     suppress_messages do 4       execute 'create sequence donor_id_seq' rescue nil 5       execute "alter table donors alter column id set default nextval('donors_id_seq')" 6     end 7   end 8 end
    68. 68. 1 # spec/spec_helper.rb e/ou features/support/schema_fixes.rb 2 require 'migrations/schema_fixes' 3 4 [FirstMigration, SecondMigration].each { |m| m.migrate :up }
    69. 69. Stored Procedures, functions, etc...
    70. 70. • Dificeis de testar • Side effects • Código procedural • Lógica separada da app
    71. 71. ?
    72. 72. OBRIGADO!

    ×