SlideShare uma empresa Scribd logo
Reescrevendo em Rails
  uma grande aplicação
         legada
Um relato sobre as dificuldades encontradas

               Cássio Marques
http://surgeworks.com
Relato Sobre a Migração de uma Aplicação Legada para Rails
cassiomarques.wordpress.com

     @cassiomarques

   cassiommc@gmail.com
Mundo ideal...
http://www.flickr.com/photos/venteco
http://www.flickr.com/photos/epleitez/
Migração de uma
 grande aplicação
desktop para Rails
Aplicação para
Controle de Serviços
  de Hemoterapia
• Desktop
• C++ (Qt 3)
• PostgreSQL
• +150k loc em C++
• +60k loc em PL/PGSQL
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...
Rails? Ruby? WTF?

• Quem programa com isso?
• Não haverá quem dê manutenção
• Não há uma empresa “por trás” do Ruby
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
O maior vilão de toda
   essa história...
Banco de Dados.
ActiveRecord
Ótimo se você seguir
  as convenções...
... mas ainda
interessante mesmo
que você não as siga.
1   class Product < ActiveRecord::Base
2     set_table_name 'produto'
3     set_primary_key 'codigo'
4   end
Bancos legados podem
usar nomes de colunas
      estranhos...
estoquequantidade
  fornecedorcnpj
  requisicaoficha
 cobrancacodigo
Relato Sobre a Migração de uma Aplicação Legada para Rails
Ou ainda mais
 estranhos...
PRD_CDG
       STT_USR
    codigoproduto
 codigoproduto_novo
codigoproduto_novo2
1 class Product < ActiveRecord::Base
2   alias_attribute 'description', 'produtodescricao'
3 end
Não funciona para
    queries...
 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>
Associações
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'
Views no banco?
+200 tabelas c/ média
 de 15 campos cada
VIEW MAPPING HELL
Três opções:
Conviver com as
  diferenças
Criar um novo banco e
migrar os dados depois
Alterar o nome das
 tabelas e colunas
Criar base
development
RAILS_ENV=production rake db:schema:dump

           rake db:schema:load
Duas bases para
desenvolvimento
1) development “normal”

2) dump da base de produção (com dados)
Para alterar nomes...
 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
Redefinir tipos dos
    campos
 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
Crie seus próprios
métodos para migrations
 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
58 change_sn_to_boolean :donors, :send_sms
chaves estrangeiras
foreign_key_migrations
composite_primary_keys
Alterar dados com
    migrations
Errado
 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
Melhor
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
Enumerações no banco
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  
1 donor.education_level.description # => join no banco!
ok, vamos tirar as
enumerações do
     banco...
problema com FK’s (tabelas
 com enumerações ainda
      referenciadas)
1 donor.education_level = 5 # WTF?
enumerate_it
 github.com/cassiomarques/enumerate_it




(jabá mode on)
 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
1 class Donor < ActiveRecord::Base
2   has_enumeration_for :education_level, :with => EducationLevel
3 end
1 donor.education_level = EducationLevel::COMPLETE_3RD_GRADE
2 donor.education_level_humanize # 3º Grau Completo (sem join)
1 <% form_for @donor do |f| %>
2   <%= f.select :education_level, EducationLevel.to_a %>
3 <% end -%>
schema.rb => banco de
        testes
... mas se a sua PK for
        varchar...
1   create_table "donors", :id => false, :force => true do |t|
3     t.string  "id", :limit => 6, :null => false
4     # ...
5   end
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
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 }
Stored Procedures,
  functions, etc...
Relato Sobre a Migração de uma Aplicação Legada para Rails
• Dificeis de testar
• Side effects
• Código procedural
• Lógica separada da app
?
OBRIGADO!

Mais conteúdo relacionado

Semelhante a Relato Sobre a Migração de uma Aplicação Legada para Rails (20)

PDF
Ruby on Rails I - Modelos
Tiago Lima
 
DOCX
Modelo entidade-relacionamento - SIGEM (sistema de gestão de materiais)
Marcos Pessoa
 
PDF
O que você acha que sabe sobre banco de dados
Matheus de Oliveira
 
PPTX
Apresentacao banco de dados
Rafael Prallon
 
PDF
Refactoring Databases - Estrategias
Ismael
 
PDF
GURU SP - Design de aplicações orientadas a objeto
Elaine Naomi
 
PDF
Workshop Ruby on Rails dia 2 ruby-pt
Pedro Sousa
 
PDF
Migrations for Java (Javou #4 - JavaCE)
Rafael Ponte
 
PDF
Apresentação cassandra
Richiely Paiva
 
PDF
Design de aplicações orientadas a objeto
Elaine Naomi
 
PDF
TDC2018SP | Trilha Ruby - Design de aplicacoes orientadas a objeto: uma visao...
tdc-globalcode
 
PDF
Ruby On Rails Regis
elliando dias
 
PDF
Dojo banco de dados
Fernando Fagonde
 
PPTX
Refactoring Data base parte 2
Ismael
 
PDF
Tutorial session
bcoverston
 
PDF
Sql
Tiago
 
PDF
Utilizando Rails e PostgreSQL
Diogo Biazus
 
KEY
Qualidade de código - a qualidade que faz a diferença
Caelum
 
PDF
Boas praticas em um Projeto de Banco de Dados
Juliano Atanazio
 
PDF
ODI Tutorial - Desenvolvendo Procedures
Caio Lima
 
Ruby on Rails I - Modelos
Tiago Lima
 
Modelo entidade-relacionamento - SIGEM (sistema de gestão de materiais)
Marcos Pessoa
 
O que você acha que sabe sobre banco de dados
Matheus de Oliveira
 
Apresentacao banco de dados
Rafael Prallon
 
Refactoring Databases - Estrategias
Ismael
 
GURU SP - Design de aplicações orientadas a objeto
Elaine Naomi
 
Workshop Ruby on Rails dia 2 ruby-pt
Pedro Sousa
 
Migrations for Java (Javou #4 - JavaCE)
Rafael Ponte
 
Apresentação cassandra
Richiely Paiva
 
Design de aplicações orientadas a objeto
Elaine Naomi
 
TDC2018SP | Trilha Ruby - Design de aplicacoes orientadas a objeto: uma visao...
tdc-globalcode
 
Ruby On Rails Regis
elliando dias
 
Dojo banco de dados
Fernando Fagonde
 
Refactoring Data base parte 2
Ismael
 
Tutorial session
bcoverston
 
Sql
Tiago
 
Utilizando Rails e PostgreSQL
Diogo Biazus
 
Qualidade de código - a qualidade que faz a diferença
Caelum
 
Boas praticas em um Projeto de Banco de Dados
Juliano Atanazio
 
ODI Tutorial - Desenvolvendo Procedures
Caio Lima
 

Último (8)

PDF
Apresentação sobre Funções Matemáticas e o módulo.pdf
Gabriel Vitor
 
PDF
SENAC Modelagem de Dados - Aula01 do curso de ADSpdf
JhonataLamim1
 
PPTX
NR-13.pptx treinamento sobre a norma regulamentadora
SimoniBorges1
 
PDF
Assistente de Suporte e Manutenção de Computadores.pdf
EudesAlvesPessoa
 
PDF
Zeebo: Uma brevíssima introdução. - David Glotz
BluePanther6
 
PDF
Apresentação de Manipulação de strings em Python .pdf
Gabriel Vitor
 
PDF
SENAC Modelagem de Dados - Aula02 curso de ADS.pdf
JhonataLamim1
 
PDF
IA - Grupo J.pdf para trabalho de inteligencia artificial
juanaraujo139815
 
Apresentação sobre Funções Matemáticas e o módulo.pdf
Gabriel Vitor
 
SENAC Modelagem de Dados - Aula01 do curso de ADSpdf
JhonataLamim1
 
NR-13.pptx treinamento sobre a norma regulamentadora
SimoniBorges1
 
Assistente de Suporte e Manutenção de Computadores.pdf
EudesAlvesPessoa
 
Zeebo: Uma brevíssima introdução. - David Glotz
BluePanther6
 
Apresentação de Manipulação de strings em Python .pdf
Gabriel Vitor
 
SENAC Modelagem de Dados - Aula02 curso de ADS.pdf
JhonataLamim1
 
IA - Grupo J.pdf para trabalho de inteligencia artificial
juanaraujo139815
 
Anúncio

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

  • 1. Reescrevendo em Rails uma grande aplicação legada Um relato sobre as dificuldades encontradas Cássio Marques
  • 4. cassiomarques.wordpress.com @cassiomarques cassiommc@gmail.com
  • 8. Migração de uma grande aplicação desktop para Rails
  • 9. Aplicação para Controle de Serviços de Hemoterapia
  • 10. • Desktop • C++ (Qt 3) • PostgreSQL • +150k loc em C++ • +60k loc em PL/PGSQL
  • 11. 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...
  • 12. Rails? Ruby? WTF? • Quem programa com isso? • Não haverá quem dê manutenção • Não há uma empresa “por trás” do Ruby
  • 13. 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
  • 14. O maior vilão de toda essa história...
  • 17. Ótimo se você seguir as convenções...
  • 18. ... mas ainda interessante mesmo que você não as siga.
  • 19. 1 class Product < ActiveRecord::Base 2   set_table_name 'produto' 3   set_primary_key 'codigo' 4 end
  • 20. Bancos legados podem usar nomes de colunas estranhos...
  • 21. estoquequantidade fornecedorcnpj requisicaoficha cobrancacodigo
  • 23. Ou ainda mais estranhos...
  • 24. PRD_CDG STT_USR codigoproduto codigoproduto_novo codigoproduto_novo2
  • 25. 1 class Product < ActiveRecord::Base 2   alias_attribute 'description', 'produtodescricao' 3 end
  • 26. Não funciona para queries...
  • 27.  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>
  • 29. 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'
  • 31. +200 tabelas c/ média de 15 campos cada
  • 34. Conviver com as diferenças
  • 35. Criar um novo banco e migrar os dados depois
  • 36. Alterar o nome das tabelas e colunas
  • 40. 1) development “normal” 2) dump da base de produção (com dados)
  • 42.  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
  • 44.  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
  • 45. Crie seus próprios métodos para migrations
  • 46.  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
  • 50. Alterar dados com migrations
  • 52.  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
  • 54. 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
  • 56. 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  
  • 58. ok, vamos tirar as enumerações do banco...
  • 59. problema com FK’s (tabelas com enumerações ainda referenciadas)
  • 62.  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
  • 63. 1 class Donor < ActiveRecord::Base 2   has_enumeration_for :education_level, :with => EducationLevel 3 end
  • 64. 1 donor.education_level = EducationLevel::COMPLETE_3RD_GRADE 2 donor.education_level_humanize # 3º Grau Completo (sem join)
  • 66. schema.rb => banco de testes
  • 67. ... mas se a sua PK for varchar...
  • 68. 1 create_table "donors", :id => false, :force => true do |t| 3   t.string  "id", :limit => 6, :null => false 4   # ... 5 end
  • 69. 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
  • 70. 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 }
  • 71. Stored Procedures, functions, etc...
  • 73. • Dificeis de testar • Side effects • Código procedural • Lógica separada da app
  • 74. ?