Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Data Mappers em Ruby

Data Mappers em Ruby

Devido à popularidade da gem ActiveRecord, a comunidade Ruby tradicionalmente deu preferência a soluções de persistência baseadas no padrão Active Record. Entretanto este padrão nem sempre é a melhor escolha, especialmente em aplicações grandes. Uma alternativa para estes casos é o padrão Data Mapper, que vem ganhando mais atenção dos Rubistas recentemente. Esta palestra discute as opções atualmente disponíveis para usar o padrão Data Mapper em projetos Ruby e como cada biblioteca resolveu implementá-lo.

Lailson Bandeira

September 19, 2015
Tweet

More Decks by Lailson Bandeira

Other Decks in Technology

Transcript

  1. lailson bandeira + organizador do tropical ruby + fundador do

    frevo on rails + bacharel e mestre em ciência da computação
  2. INTRODUÇÃO complexidade God objects teste com setup difícil suite de

    teste que demora integrar com sistemas legados até que eu comecei a ter…
  3. INTRODUÇÃO problema fundamental orientação a objetos objetos, atributos, associações, herança,

    polimorfismo modelo relacional tabelas, linhas, colunas, relações
  4. INTRODUÇÃO “Vietnã da Ciência da Computação” http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx “Although it may

    seem trite to say it, Object/Relational Mapping is the Vietnam of Computer Science. It represents a quagmire which starts well, gets more complicated as time passes, and before long entraps its users in a commitment that has no clear demarcation point, no clear win conditions, and no clear exit strategy." — Ted Neward
  5. 1 / PADRÕES DOMAIN LOGIC Transaction Script Domain Model Table

    Module Service Layer OBJECT-RELATIONAL BEHAVIOR Unit of Work Identity Map Lazy Load OBJECT-RELATIONAL STRUCTURE Identity Field Foreign Key Mapping Association Table Mapping Dependent Mapping Embedded Value Serialized LOB Single Table Inheritance Class Table Inheritance Concrete Table Inheritance Inheritance Mappers WEB PRESENTATION Model View Controller Page Controller Front Controller Template View Transform View Two-Step View Application Controller OFFLINE CONCURRENCY Optimistic Offline Lock Pessimistic Offline Lock Coarse Grained Lock Implicit Lock SESSION STATE Client Session State Server Session State Database Session State DISTRIBUTION Remote Facade Data Transfer Object DATA SOURCE ARCHITECTURE Table Data Gateway Row Data Gateway Active Record Data Mapper OBJECT-RELATIONAL METADATA MAPPING Metadata Mapping Query Object Repository BASE Gateway Mapper Layer Supertype Separated Interface Registry Value Object Money Special Case Plugin Service Stub Record Set
  6. 1 / PADRÕES DOMAIN LOGIC Transaction Script Domain Model Table

    Module Service Layer OBJECT-RELATIONAL BEHAVIOR Unit of Work Identity Map Lazy Load OBJECT-RELATIONAL STRUCTURE Identity Field Foreign Key Mapping Association Table Mapping Dependent Mapping Embedded Value Serialized LOB Single Table Inheritance Class Table Inheritance Concrete Table Inheritance Inheritance Mappers WEB PRESENTATION Model View Controller Page Controller Front Controller Template View Transform View Two-Step View Application Controller OFFLINE CONCURRENCY Optimistic Offline Lock Pessimistic Offline Lock Coarse Grained Lock Implicit Lock SESSION STATE Client Session State Server Session State Database Session State DISTRIBUTION Remote Facade Data Transfer Object DATA SOURCE ARCHITECTURE Table Data Gateway Row Data Gateway Active Record Data Mapper OBJECT-RELATIONAL METADATA MAPPING Metadata Mapping Query Object Repository BASE Gateway Mapper Layer Supertype Separated Interface Registry Value Object Money Special Case Plugin Service Stub Record Set
  7. 1 / PADRÕES DOMAIN LOGIC Transaction Script Domain Model Table

    Module Service Layer OBJECT-RELATIONAL BEHAVIOR Unit of Work Identity Map Lazy Load OBJECT-RELATIONAL STRUCTURE Identity Field Foreign Key Mapping Association Table Mapping Dependent Mapping Embedded Value Serialized LOB Single Table Inheritance Class Table Inheritance Concrete Table Inheritance Inheritance Mappers WEB PRESENTATION Model View Controller Page Controller Front Controller Template View Transform View Two-Step View Application Controller OFFLINE CONCURRENCY Optimistic Offline Lock Pessimistic Offline Lock Coarse Grained Lock Implicit Lock SESSION STATE Client Session State Server Session State Database Session State DISTRIBUTION Remote Facade Data Transfer Object DATA SOURCE ARCHITECTURE Table Data Gateway Row Data Gateway Active Record Data Mapper OBJECT-RELATIONAL METADATA MAPPING Metadata Mapping Query Object Repository BASE Gateway Mapper Layer Supertype Separated Interface Registry Value Object Money Special Case Plugin Service Stub Record Set
  8. 1 / PADRÕES / DOMAIN MODEL domain model “Um modelo

    de objetos do domínio que incorpora tanto o comportamento quanto os dados." / domain logic pattern ALBUM + name + artist + released_at + tracks + add_track + send_to_store + fetch_statistics ATRIBUTOS MÉTODOS
  9. 1 / PADRÕES / DOMAIN MODEL Forma de organizar regras

    de negócio. dados regras de negócio estado comportamento + atuam sobre domain model / domain logic pattern
  10. 1 / PADRÕES / DOMAIN MODEL “oo clássico” Conceitos de

    domínio modelados como uma rede de objetos que colaboram através de mensagens. domain model / domain logic pattern
  11. 1 / PADRÕES / DOMAIN MODEL menos duplicação de código

    menos duplicação de conhecimento domain model / domain logic pattern
  12. 1 / PADRÕES / DOMAIN MODEL fluxo fragmentado É difícil

    acompanhar um processo que acontece em vários objetos. domain model / domain logic pattern
  13. 1 / PADRÕES / DOMAIN MODEL “skinny controllers, fat models”

    difíceis de manter e testar domain model / domain logic pattern
  14. 1 / PADRÕES / DOMAIN MODEL onde o código deve

    ficar? A B domain model / domain logic pattern
  15. 1 / PADRÕES / DOMAIN MODEL quais são as outras

    opções? DOMAIN LOGIC PATTERNS + transaction script + table module + domain model + service layer domain model / domain logic pattern
  16. 1 / PADRÕES / DOMAIN MODEL camada de serviço +

    comandos + casos de uso + interactors domain model / domain logic pattern
  17. 1 / PADRÕES / DOMAIN MODEL 1. fat models 3.

    híbrido 2. service layer Regras de negócio implementadas 
 nos objetos. Regras de negócio implementadas em uma camada de serviços. “modelo anêmico” Regras de negócio distribuídas entre objetos e camada de serviços.
  18. 1 / PADRÕES / ACTIVE RECORD active record “Um objeto

    que encapsula uma linha de uma tabela ou visão de um banco de dados, o acesso ao banco de dados e adiciona lógica de domínio a esses dados.” / data source architectural pattern ALBUM + name + artist + released_at + tracks + add_track + send_to_store + fetch_statistics ATRIBUTOS MÉTODOS BANCO DE DADOS
  19. 1 / PADRÕES / ACTIVE RECORD Implementar o acesso ao

    banco de dados no próprio modelo. ideia central dados regras de negócio estado comportamento + + persistência escrita/leitura active record / data source architectural pattern
  20. 1 / PADRÕES / ACTIVE RECORD mapeamento simples, direto apenas

    conversão de tipos + name : varchar + artist : varchar + released_at : date ALBUM + name : String + artist : String + released_at : Date ATRIBUTOS <classe> ALBUMS ATRIBUTOS <tabela> active record / data source architectural pattern
  21. 1 / PADRÕES / ACTIVE RECORD quando usar? lógica
 simples

    validações em um único objeto mapeamento direto active record / data source architectural pattern
  22. 1 / PADRÕES / ACTIVE RECORD vantagens simplicidade Simples e

    fácil de configurar, instalar, usar e entender. active record / data source architectural pattern
  23. 1 / PADRÕES / ACTIVE RECORD desvantagens acoplamento testes fat

    model Forte ligação entre a estrutura do banco e o modelo. Testes que dependem do banco de dados ou que requerem muito mocking ou setup. Incentiva concentrar muito código em um único objeto. active record / data source architectural pattern
  24. 1 / PADRÕES / ACTIVE RECORD implementações em ruby ActiveRecord

    Sequel::Model maioria dos ORMs mongoid, her, ohm, cequel, dynamoid… active record / data source architectural pattern
  25. 1 / PADRÕES / DATA MAPPER data mapper “Uma camada

    de mapeadores que move dados entre os objetos e um banco de dados mantendo-os independentes um do outro e do próprio mapeador.” / data source architectural pattern ALBUM + name + artist + released_at + tracks ATRIBUTOS BANCO DE DADOS ALBUM MAPPER name : varchar artist : varchar release_date : varchar name : String artist : String released_at : Date CAMADA DATA MAPPER …
  26. 1 / PADRÕES / DATA MAPPER data mapper / data

    source architectural pattern A transferência de dados de e para o banco de dados é feita por uma camada especializada, separada do modelo. ideia central MODELO DATA MAPPER
  27. 1 / PADRÕES / DATA MAPPER data mapper / data

    source architectural pattern Separação de responsabilidades: uma camada dedicada pode fazer mapeamentos complexos. motivação PERSON + name + position + date_of_birth + cpf ADDRESS + street + number + city + postal_code <class> <class> APLICAÇÃO PERSON + name + position + date_of_birth + cpf + street + number + city + postal_code <table> BANCO DE DADOS
  28. 1 / PADRÕES / DATA MAPPER data mapper / data

    source architectural pattern quando usar? sistema
 legado equipes
 independentes modelo
 rico Quando o mapeamento entre o modelo e a base de dados não é direto.
  29. 1 / PADRÕES / DATA MAPPER data mapper / data

    source architectural pattern quando usar? Quando a lógica de negócios é complexa e/ou envolve muitos objetos. Assim, os modelos podem se concentrar apenas nas regras de negócio. strategies, factories, visitors, commands…
  30. 1 / PADRÕES / DATA MAPPER data mapper / data

    source architectural pattern vantagens desacoplamento Modelos independentes do banco de dados. Mais fácil de manter e evoluir a aplicação. O banco de dados pode ser substituído com facilidade em testes, por exemplo. Para entender como a aplicação funciona, não é necessário saber como os modelos são persistidos. 1 2 3
  31. 1 / PADRÕES / DATA MAPPER data mapper / data

    source architectural pattern vantagens otimização Objetos diferentes armazenados na mesma tabela. Grafos de objetos em uma chamada com JOINs. modelo ryco Modelos que representam melhor o domínio com conceitos OO que não podem ser expressados no modelo relacional. sistemas legados Permite um modelo expressivo mesmo que a aplicação use um banco legado ou de terceiros.
  32. 1 / PADRÕES / DATA MAPPER data mapper / data

    source architectural pattern desvantagens camada extra Mais código para escrever, testar e manter. duplicação O código que mapeia os dados tende a ter duplicação e muda junto com o banco ou modelo. complexidade Bibliotecas data mapper são mais complexas e tendem a ter mais bugs.
  33. 1 / PADRÕES / DISCUSSÃO discussão / active record vs

    data mapper Active Record vs Data Mapper
  34. 1 / PADRÕES / DISCUSSÃO discussão / active record vs

    data mapper Princípio da Responsabilidade Única “Uma classe deve ter uma, e apenas uma, razão para mudar.” – Robert “Uncle Bob" Martin
  35. 1 / PADRÕES / DISCUSSÃO “Um objeto que encapsula uma

    linha de uma tabela ou visão de um banco de dados, o acesso ao banco de dados e adiciona lógica de domínio a esses dados.” discussão / active record vs data mapper definição de active record row data gateway gateway domain model
  36. 1 / PADRÕES / DISCUSSÃO discussão / active record vs

    data mapper Princípio da Responsabilidade Única Por definição, o padrão Active Record viola o Princípio da Responsabilidade Única.
  37. 1 / PADRÕES / DISCUSSÃO discussão / active record vs

    data mapper motivação conveniência
  38. 1 / PADRÕES / DISCUSSÃO discussão / active record vs

    data mapper Curmudgeon: An Opinionated Look at an Opinionated Framework Ernie Miller, RailsConf 2014
  39. 1 / PADRÕES / DISCUSSÃO discussão / active record vs

    data mapper Core Data .net Java Objective-C Python
  40. 1 / PADRÕES / DISCUSSÃO discussão / active record vs

    data mapper data mappers têm sido negligenciados em ruby
  41. 2 / IMPLEMENTAÇÕES exemplo ` ALBUM + name : String

    + artist : String + released_at : Date TRACK + position : Integer + name : String 1 *
  42. 2 / IMPLEMENTAÇÕES 1. criar esquema do banco de dados

    2. inserir albuns e faixas 3. buscar albuns por artista com faixas 4. buscar albuns lançados depois de 2002 tarefas
  43. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model 0.4.1 julho de

    2015 Luca Guidi e
 contribuidores A persistence framework for Lotus. 2014 lançado em
  44. 2 / IMPLEMENTAÇÃO / LOTUS MODEL REPOSITORY APP MAPPER ADAPTER

    modelos hash no formato do domínio hash no formato do adaptador dados binários BD ESCRITA LEITURA modelos hash no formato do domínio hash no formato do adaptador dados binários
  45. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model Lotus-model tem suporte

    a migrações. Lotus::Model.migration do change do create_table :albums do primary_key :id column :name, String, null: false column :artist, String, null: false column :release_date, Date end end end Lotus::Model.migration do change do create_table :tracks do primary_key :id column :position, Integer, null: false column :name, String, null: false foreign_key :album_id, :albums, null: false check { position > 0 } end end end / 1. criar esquema do banco de dados db/migrations/2_create_tracks.rb db/migrations/1_create_albums.rb
  46. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 1. criar

    esquema do banco de dados rubyconf_lotus=# \d albums Table "public.albums" Column | Type | Modifiers --------------+---------+----------------------------------------------------- id | integer | not null default nextval('albums_id_seq'::regclass) name | text | not null artist | text | not null release_date | date | Indexes: "albums_pkey" PRIMARY KEY, btree (id) Referenced by: TABLE "tracks" CONSTRAINT "tracks_album_id_fkey" FOREIGN KEY (album_id) REFERENCES albums(id) PSQL PSQL
  47. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 1. criar

    esquema do banco de dados rubyconf_lotus=# \d tracks Table "public.tracks" Column | Type | Modifiers ----------+---------+----------------------------------------------------- id | integer | not null default nextval('tracks_id_seq'::regclass) position | integer | not null name | text | not null album_id | integer | not null Indexes: "tracks_pkey" PRIMARY KEY, btree (id) Check constraints: "tracks_position_check" CHECK ("position" > 0) Foreign-key constraints: "tracks_album_id_fkey" FOREIGN KEY (album_id) REFERENCES albums(id) PSQL
  48. 2 / IMPLEMENTAÇÃO / LOTUS MODEL Lotus::Model.configure do adapter type:

    :sql, uri: 'postgres://localhost/rubyconf_lotus' mapping do collection :albums do entity Album repository AlbumRepository attribute :id, Integer attribute :name, String attribute :artist, String attribute :released_at, Date, as: :release_date end collection :tracks do entity Track repository TrackRepository lotus-model / 2. inserir albuns e faixas Configuração e Inicialização config/lotus.rb
  49. 2 / IMPLEMENTAÇÃO / LOTUS MODEL Lotus::Model.configure do adapter type:

    :sql, uri: 'postgres://localhost/rubyconf_lotus' mapping do collection :albums do entity Album repository AlbumRepository attribute :id, Integer attribute :name, String attribute :artist, String attribute :released_at, Date, as: :release_date end collection :tracks do entity Track repository TrackRepository attribute :id, Integer attribute :name, String attribute :position, Integer attribute :album_id, Integer end end migrations 'db/migrations' schema 'db/schema.sql' end Lotus::Model.load! lotus-model / 2. inserir albuns e faixas Configuração e Inicialização config/lotus.rb
  50. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 2. inserir

    albuns e faixas Criação dos modelos class Album include Lotus::Entity attributes :id, :name, :artist, :released_at end class Track include Lotus::Entity attributes :id, :position, :name, :album_id end models/track.rb models/album.rb
  51. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 2. inserir

    albuns e faixas Criação dos repositórios class AlbumRepository include Lotus::Repository end class TrackRepository include Lotus::Repository end repositories/track_repository.rb repositories/album_repository.rb
  52. 2 / IMPLEMENTAÇÃO / LOTUS MODEL isThisIt = Album.new(name: 'Is

    This It', artist: 'The Strokes', released_at: '2001-07-30') isThisIt = AlbumRepository.create(isThisIt) someday = Track.new(position: 5, name: 'Someday', album_id: isThisIt.id) lastNite = Track.new(position: 7, name: 'Last Nite', album_id: isThisIt.id) hardToExplain = Track.new(position: 8, name: 'Hard to Explain', album_id: isThisIt.id) TrackRepository.create(someday) TrackRepository.create(lastNite) TrackRepository.create(hardToExplain) lotus-model / 2. inserir albuns e faixas insert.rb The Strokes Is This It? 2001
  53. 2 / IMPLEMENTAÇÃO / LOTUS MODEL roomOnFire = Album.new(name: 'Room

    on Fire', artist: 'The Strokes', released_at: '2003-10-28') roomOnFire = AlbumRepository.create(roomOnFire) reptilia = Track.new(position: 2, name: 'Reptilia', album_id: roomOnFire.id) t1251 = Track.new(position: 4, name: '12:51', album_id: roomOnFire.id) theEnd = Track.new(position: 10, name: 'The End Has No End', album_id: roomOnFire.id) TrackRepository.create(reptilia) TrackRepository.create(t1251) TrackRepository.create(theEnd) lotus-model / 2. inserir albuns e faixas insert.rb The Strokes Room on Fire 2003
  54. 2 / IMPLEMENTAÇÃO / LOTUS MODEL hotFuss = Album.new(name: 'Hot

    Fuss', artist: 'The Killers', released_at: '2004-06-07') hotFuss = AlbumRepository.create(hotFuss) mrBrightsite = Track.new(position: 2, name: 'Mr. Brightside', album_id: hotFuss.id) somebodyToldMe = Track.new(position: 4, name: 'Somebody Told Me', album_id: hotFuss.id) TrackRepository.create(mrBrightsite) TrackRepository.create(somebodyToldMe) lotus-model / 2. inserir albuns e faixas insert.rb The Killers Hot Fuss 2004
  55. 2 / IMPLEMENTAÇÃO / LOTUS MODEL I, [2015-09-06T09:49:26.380195 #27075] INFO

    -- : (0.000289s) SET standard_conforming_strings = ON I, [2015-09-06T09:49:26.380446 #27075] INFO -- : (0.000164s) SET client_min_messages = 'WARNING' I, [2015-09-06T09:49:26.380633 #27075] INFO -- : (0.000148s) SET DateStyle = 'ISO' I, [2015-09-06T09:49:26.384690 #27075] INFO -- : (0.003857s) SELECT pg_attribute.attname AS pk FROM pg_class, pg_attribute, pg_index, pg_namespace WHERE pg_class.oid = pg_attribute.attrelid AND pg_class.relnamespace = pg_namespace.oid AND pg_class.oid = pg_index.indrelid AND pg_index.indkey[0] = pg_attribute.attnum AND pg_index.indisprimary = 't' AND pg_class.oid = CAST(CAST('"albums"' AS regclass) AS oid) I, [2015-09-06T09:49:26.386319 #27075] INFO -- : (0.001260s) INSERT INTO "albums" ("name", "artist", "release_date") VALUES ('Is This It', 'The Strokes', '2001-07-30') RETURNING "id" I, [2015-09-06T09:49:26.387831 #27075] INFO -- : (0.001189s) SELECT pg_attribute.attname AS pk FROM pg_class, pg_attribute, pg_index, pg_namespace WHERE pg_class.oid = pg_attribute.attrelid AND pg_class.relnamespace = pg_namespace.oid AND pg_class.oid = pg_index.indrelid AND pg_index.indkey[0] = pg_attribute.attnum AND pg_index.indisprimary = 't' AND pg_class.oid = CAST(CAST('"tracks"' AS regclass) AS oid) I, [2015-09-06T09:49:26.389433 #27075] INFO -- : (0.001345s) INSERT INTO "tracks" ("name", "position", "album_id") VALUES ('Someday', 5, 1) RETURNING "id" I, [2015-09-06T09:49:26.390354 #27075] INFO -- : (0.000629s) INSERT INTO "tracks" ("name", "position", "album_id") VALUES ('Last Nite', 7, 1) RETURNING "id" I, [2015-09-06T09:49:26.391296 #27075] INFO -- : (0.000662s) INSERT INTO "tracks" ("name", "position", "album_id") VALUES ('Hard to Explain', 8, 1) RETURNING "id" I, [2015-09-06T09:49:26.392204 #27075] INFO -- : (0.000572s) INSERT INTO "albums" ("name", "artist", "release_date") VALUES ('Room on Fire', 'The Strokes', '2003-10-28') RETURNING "id" I, [2015-09-06T09:49:26.393148 #27075] INFO -- : (0.000654s) INSERT INTO "tracks" ("name", "position", "album_id") VALUES ('Reptilia', 2, 2) RETURNING "id" I, [2015-09-06T09:49:26.394058 #27075] INFO -- : (0.000634s) INSERT INTO "tracks" ("name", "position", "album_id") VALUES ('12:51', 4, 2) RETURNING "id" I, [2015-09-06T09:49:26.395075 #27075] INFO -- : (0.000729s) INSERT INTO "tracks" ("name", "position", lotus-model / 2. inserir albuns e faixas STDOUT
  56. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 3. buscar

    albuns por artista com faixas Repositórios têm interface simples allAlbums = AlbumRepository.all # I, [2015-09-06T09:55:26.111841 #27175] INFO -- : (0.001068s) SELECT * FROM "albums" firstAlbum = AlbumRepository.first # I, [2015-09-06T09:55:26.113004 #27175] INFO -- : (0.000695s) SELECT * FROM "albums" ORDER BY "id" LIMIT 1 albums = AlbumRepository.where(artist: 'The Strokes') # => in `<main>': undefined method `where' for AlbumRepository:Class (NoMethodError) REPL
  57. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model Definindo consultas class

    AlbumRepository include Lotus::Repository def self.of_artist(artist) query.where(artist: artist) end end repositories/album_repository.rb class TrackRepository include Lotus::Repository def self.from_album(album) query.where(album_id: album.id) end end repositories/track_repository.rb / 3. buscar albuns por artista com faixas
  58. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 3. buscar

    albuns por artista com faixas Executando a consulta albums = AlbumRepository.of_artist('The Strokes') for album in albums puts "#{album.name} is an album from #{album.artist} (released on #{album.released_at})" tracks = TrackRepository.from_album(album) puts 'Some tracks:' for track in tracks puts "#{track.position} - #{track.name}" end puts end query.rb
  59. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 3. buscar

    albuns por artista com faixas Executando a consulta Is This It is an album from The Strokes (released on 2001-07-30) Some tracks: 5 - Someday 7 - Last Nite 8 - Hard to Explain Room on Fire is an album from The Strokes (released on 2003-10-28) Some tracks: 2 - Reptilia 4 - 12:51 10 - The End Has No End STDOUT
  60. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model / 4. buscar

    albuns lançados depois de 2002 Adicionando método no repositório class AlbumRepository include Lotus::Repository def self.of_artist(artist) query.where(artist: artist) end def self.released_after(date) query.where { release_date > date } end end repositories/album_repository.rb
  61. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model Executando a consulta

    albumsAfter2002 = AlbumRepository.released_after(Date.parse('2002-12-31')) for album in albumsAfter2002 puts "#{album.name} by #{album.artist} was released after 2002" end # => Room on Fire by The Strokes was released after 2002 # => Hot Fuss by The Killers was released after 2002 insert.rb / 4. buscar albuns lançados depois de 2002
  62. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model Funciona com qualquer

    objeto. class Track attr_accessor :id, :position, :name, :album_id def initialize(id: nil, position: nil, name: nil, album_id: nil) @id = id @position = position @name = name @album_id = album_id end end models/track.rb
  63. 2 / IMPLEMENTAÇÃO / LOTUS MODEL Lotus::Model.configure do adapter type:

    :memory, uri: 'rubyconf_lotus' mapping do collection :albums do entity Album repository AlbumRepository attribute :id, Integer attribute :name, String attribute :artist, String attribute :released_at, Date, as: :release_date end lotus-model Memory Adapter é especialmente útil para testes. config/lotus.rb
  64. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model vantagens repositórios A

    mesma entidade pode ser usada por vários repositórios. sequel O adaptador SQL usa a gem Sequel por baixo: lazy loading, expressões na consulta… validação Integra com a gem de validação do lotus.
  65. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model desvantagens imatura Pouco

    testada em produção. documentação Guias e documentação de API razoáveis, mas faltam outros materiais. funcionalidades Faltam funcionalidades importantes como associações e joins.
  66. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model desvantagens a falta

    de coleções é pior do que parece isThisIt = Album.new(name: 'Is This It', artist: 'The Strokes', released_at: '2001-07-30') isThisIt = AlbumRepository.create(isThisIt) someday = Track.new(position: 5, name: 'Someday', album_id: isThisIt.id) lastNite = Track.new(position: 7, name: 'Last Nite', album_id: isThisIt.id) hardToExplain = Track.new(position: 8, name: 'Hard to Explain', album_id: isThisIt.id) TrackRepository.create(someday) TrackRepository.create(lastNite) TrackRepository.create(hardToExplain) insert.rb
  67. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model desvantagens duplicação Lotus::Model.migration

    do change do create_table :albums do primary_key :id column :name, String, null: false column :artist, String, null: false column :release_date, Date end end end MIGRAÇÃO mapping do collection :albums do entity Album repository AlbumRepository attribute :id, Integer attribute :name, String attribute :artist, String attribute :released_at, Date, as: :release_date end ... MAPEAMENTO class Album include Lotus::Entity attributes :id, :name, :artist, :released_at end ENTIDADE
  68. 2 / IMPLEMENTAÇÃO / LOTUS MODEL lotus-model desvantagens não mapeia

    os atributos na consulta collection :albums do entity Album repository AlbumRepository attribute :id, Integer attribute :name, String attribute :artist, String attribute :released_at, Date, as: :release_date end class AlbumRepository include Lotus::Repository def self.of_artist(artist) query.where(artist: artist) end def self.released_after(date) query.where { release_date > date } end end repositories/album_repository.rb config/lotus.rb
  69. 2 / IMPLEMENTAÇÃO / ROM rom 0.9.1 agosto de 2015

    Piotr Solnica e
 contribuidores Persistence and mapping toolkit for Ruby. 2014 lançado em
  70. 2 / IMPLEMENTAÇÃO / ROM rom ECOSISTEMA rom-repository rom-model rom-mapper

    rom-support virtus transproc transflow INTEGRAÇÃO rom-rails rom-lotus rom-roda ADAPTADORES rom-sql rom-yesql rom-couchdb rom-cassandra rom-mongo rom-neo4j rom-event_store rom-influxdb rom-rethinkdb rom-yaml rom-csv rom-git
  71. 2 / IMPLEMENTAÇÃO / ROM entrada hash no formato do

    adaptador dados binários modelos hash no formato do adaptador hash no formato do adaptador dados binários REPOSITORY APP MAPPER ADAPTER BD ESCRITA LEITURA RELATION RELATION COMMAND hash no formato do domínio
  72. 2 / IMPLEMENTAÇÃO / ROM require 'logger' ROM.setup(:sql, 'postgres://localhost/rubyconf_rom', logger:

    Logger.new(STDOUT)) ROM.finalize gateway = ROM.env.gateways[:default] migration = gateway.migration do change do create_table :albums do primary_key :id column :name, String, null: false column :artist, String, null: false column :release_date, Date end create_table :tracks do primary_key :id column :position, Integer, null: false column :name, String, null: false rom / 1. criar esquema do banco de dados ROM tem suporte a migrações. migrate.rb
  73. 2 / IMPLEMENTAÇÃO / ROM require 'logger' ROM.setup(:sql, 'postgres://localhost/rubyconf_rom', logger:

    Logger.new(STDOUT)) ROM.finalize gateway = ROM.env.gateways[:default] migration = gateway.migration do change do create_table :albums do primary_key :id column :name, String, null: false column :artist, String, null: false column :release_date, Date end create_table :tracks do primary_key :id column :position, Integer, null: false column :name, String, null: false foreign_key :album_id, :albums, null: false check { position > 0 } end end end migration.apply(gateway.connection, :up) rom / 1. criar esquema do banco de dados ROM tem suporte a migrações. migrate.rb
  74. 2 / IMPLEMENTAÇÃO / ROM rom Configuração e Inicialização config/rom.rb

    / 2. inserir albuns e faixas require 'logger' ROM.use :auto_registration ROM.setup(:sql, 'postgres://localhost/rubyconf_rom', logger: Logger.new(STDOUT)) # Definição dos objetos require... ROM.finalize
  75. 2 / IMPLEMENTAÇÃO / ROM rom Criando comando e relation

    commands/create_album.rb / 2. inserir albuns e faixas class CreateAlbum < ROM::Commands::Create[:sql] relation :albums register_as :create result :one input Transproc(:rename_keys, released_at: :release_date) end class AlbumRelation < ROM::Relation[:sql] dataset :albums register_as :albums end relations/album_relation.rb
  76. 2 / IMPLEMENTAÇÃO / ROM rom Inserindo dados insert.rb /

    2. inserir albuns e faixas create_album = ROM.env.command(:albums).create is_this_it = create_album.call(name: 'Is This It', artist: 'The Strokes', released_at: '2001-07-30') I, [2015-09-08T15:32:22.385382 #46603] INFO -- : (0.000994s) INSERT INTO "albums" ("name", "artist", "release_date") VALUES ('Is This It', 'The Strokes', '2001-07-30') RETURNING "id", "name", "artist", "release_date" # => {:id=>1, :name=>"Is This It", :artist=>"The Strokes", :release_date=>#<Date: 2001-07-30 ((2452121j, 0s,0n),+0s,2299161j)>}
  77. 2 / IMPLEMENTAÇÃO / ROM rom Criando modelo / 2.

    inserir albuns e faixas models/album.rb class Album include ROM::Model::Attributes attribute :id attribute :name attribute :artist attribute :released_at, Date end mappers/album_mapper.rb class AlbumMapper < ROM::Mapper relation :albums register_as :entity model Album attribute :released_at, from: :release_date end
  78. 2 / IMPLEMENTAÇÃO / ROM rom Inserindo dados como modelo

    insert.rb / 2. inserir albuns e faixas is_this_it = Album.new(name: 'Is This It', artist: 'The Strokes', released_at: ‘2001-07-30') # => #<Album:0x007fb831195ac8 @name="Is This It", @artist="The Strokes", @released_at=#<Date: 2001-07-30 ((2452121j,0s,0n),+0s,2299161j)>, @id=nil> create_album = ROM.env.command(:albums).as(:entity).create is_this_it = create_album.call(is_this_it) # I, [2015-09-11T08:54:24.695788 #82180] INFO -- : (0.000835s) INSERT INTO "albums" ("name", "artist", "release_date") VALUES ('Is This It', 'The Strokes', '2001-07-30') RETURNING "id", "name", "artist", "release_date" # => #<Album:0x007fa7d31a5b30 @name="Is This It", @artist="The Strokes", @released_at=#<Date: 2001-07-30 ((2452121j,0s,0n),+0s,2299161j)>, @id=1>
  79. 2 / IMPLEMENTAÇÃO / ROM rom Criando comando, relation… /

    2. inserir albuns e faixas commands/create_track.rb class CreateTrack < ROM::Commands::Create[:sql] relation :tracks register_as :create input Transproc(:reject_keys, [:id]) end relations/track_relation.rb class TrackRelation < ROM::Relation[:sql] dataset :tracks register_as :tracks end
  80. 2 / IMPLEMENTAÇÃO / ROM rom …mapper e modelo! /

    2. inserir albuns e faixas mapper/track_mapper.rb class TrackMapper < ROM::Mapper relation :tracks register_as :entity model Track end models/track.rb class Track include ROM::Model::Attributes attribute :id attribute :position, Integer attribute :name attribute :album_id, Integer end
  81. 2 / IMPLEMENTAÇÃO / ROM rom Inserindo faixas insert.rb /

    2. inserir albuns e faixas someday = Track.new(position: 5, name: 'Someday', album_id: is_this_it.id) => #<Track:0x007f97539fb360 @position=5, @name="Someday", @album_id=1, @id=nil> create_tracks = ROM.env.command(:tracks).as(:entity).create someday = create_tracks.call(someday) I, [2015-09-11T09:14:33.958062 #82649] INFO -- : (0.001259s) INSERT INTO "tracks" ("position", "name", "album_id") VALUES (5, 'Someday', 1) RETURNING "id", "position", "name", "album_id" # => [#<Track:0x007f97542887d0 @position=5, @name="Someday", @album_id=1, @id=1>]
  82. 2 / IMPLEMENTAÇÃO / ROM rom Adicionando associação / 2.

    inserir albuns e faixas commands/create_track.rb class CreateTrack < ROM::Commands::Create[:sql] relation :tracks register_as :create input Transproc(:reject_keys, [:id]) associates :tracks, key: [:album_id, :id] end
  83. 2 / IMPLEMENTAÇÃO / ROM rom Inserindo grafos / 2.

    inserir albuns e faixas insert.rb create_album = ROM.env.command(:albums).as(:entity).create create_tracks = ROM.env.command(:tracks).as(:entity).create create_album_with_tracks = ROM.env.command([ { album: :albums }, [:create, [:tracks, [:create]]] ])
  84. 2 / IMPLEMENTAÇÃO / ROM is_this_it_attrs = { album: {

    name: 'Is This It', artist: 'The Strokes', released_at: '2001-07-30', tracks: [ {position: 5, name: 'Someday'}, {position: 7, name: 'Last Nite'}, {position: 8, name: 'Hard to Explain'} ] } } is_this_it = create_album_with_tracks.call(is_this_it_attrs) rom Inserindo grafos / 2. inserir albuns e faixas insert.rb
  85. 2 / IMPLEMENTAÇÃO / ROM rom Criando a query /

    3. buscar albuns por artista com faixas class AlbumRelation < ROM::Relation[:sql] dataset :albums register_as :albums def of_artist(artist) where(artist: artist) end end relations/album_relation.rb class AlbumRepository < ROM::Repository::Base relations :albums def of_artist(artist) albums.of_artist(artist) end end repositories/album_repository.rb
  86. 2 / IMPLEMENTAÇÃO / ROM rom Usando a query /

    3. buscar albuns por artista com faixas query.rb repository = AlbumRepository.new(ROM.env) repository.of_artist('The Strokes').as(:entity).to_a # => [#<Album:0x007fd19c206910 @id=1, @name="Is This It", @artist="The Strokes", @released_at=#<Date: 2001-07-30 ((2452121j,0s,0n),+0s,2299161j)>>, #<Album:0x007fd19c1d72a0 @id=2, @name="Room on Fire", @artist="The Strokes", @released_at=#<Date: 2003-10-28 ((2452941j,0s,0n),+0s,2299161j)>>]
  87. 2 / IMPLEMENTAÇÃO / ROM rom Adicionando associações / 3.

    buscar albuns por artista com faixas class AlbumRepository < ROM::Repository::Base relations :albums, :tracks def of_artist(artist) albums.of_artist(artist) end def with_tracks albums.combine_children(many: tracks) end end repositories/album_repository.rb class Album include ROM::Model::Attributes attribute :id attribute :name attribute :artist attribute :released_at, Date attribute :tracks, [Track] end model/album.rb
  88. 2 / IMPLEMENTAÇÃO / ROM rom Usando a query com

    associações / 3. buscar albuns por artista com faixas query.rb repository.with_tracks.of_artist('The Strokes').as(:entity).to_a # => [#<Album:0x007fe92c88ae38 @id=1, @name="Is This It", @artist="The Strokes", @released_at=#<Date: 2001-07-30 ((2452121j,0s,0n),+0s,2299161j)>, @tracks=[#<Track:0x007fe92c88a848 @id=1, @position=5, @name="Someday", @album_id=1>, #<Track:0x007fe92c88a488 @id=2, @position=7, @name="Last Nite", @album_id=1>, #<Track:0x007fe92c889ee8 @id=3, @position=8, @name="Hard to Explain", @album_id=1>]>, #<Album:0x007fe92c889858 @id=2, @name="Room on Fire", @artist="The Strokes", @released_at=#<Date: 2003-10-28 ((2452941j,0s,0n),+0s,2299161j)>, @tracks=[#<Track:0x007fe92c8891c8 @id=4, @position=2, @name="Reptilia", @album_id=2>, #<Track:0x007fe92c888bd8 @id=5, @position=4, @name="12:51", @album_id=2>, #<Track:0x007fe92c888408 @id=6, @position=10, @name="The End Has No End", @album_id=2>]>]
  89. 2 / IMPLEMENTAÇÃO / ROM rom Finalmente o exemplo /

    3. buscar albuns por artista com faixas query.rb albums = repository.with_tracks.of_artist('The Strokes').as(:entity) for album in albums puts "#{album.name} is an album from #{album.artist} (released on #{album.released_at})" puts 'Some tracks:' for track in album.tracks puts "#{track.position} - #{track.name}" end puts end
  90. 2 / IMPLEMENTAÇÃO / ROM rom Finalmente o exemplo /

    3. buscar albuns por artista com faixas Is This It is an album from The Strokes (released on 2001-07-30) Some tracks: 5 - Someday 7 - Last Nite 8 - Hard to Explain Room on Fire is an album from The Strokes (released on 2003-10-28) Some tracks: 2 - Reptilia 4 - 12:51 10 - The End Has No End STDOUT
  91. 2 / IMPLEMENTAÇÃO / ROM rom Criando a query /

    4. buscar albuns lançados depois de 2002 class AlbumRelation < ROM::Relation[:sql] dataset :albums # auto register_as :albums # auto ... def released_after(date) where { release_date > date } end end relations/album_relation.rb class AlbumRepository < ROM::Repository::Base relations :albums, :tracks ... def released_after(date) albums.released_after(date) end end repositories/album_repository.rb
  92. 2 / IMPLEMENTAÇÃO / ROM rom Usando a query /

    4. buscar albuns lançados depois de 2002 query.rb albumsAfter2002 = repository.released_after(Date.parse('2002-12-31')) for album in albumsAfter2002 puts "#{album.name} by #{album.artist} was released after 2002" end # => Room on Fire by The Strokes was released after 2002 # => Hot Fuss by The Killers was released after 2002
  93. 2 / IMPLEMENTAÇÃO / LOTUS MODEL vantagens poderoso Especialmente o

    Mapper, que tem muitos recursos. reusável Todos os componentes podem ser usados em conjunto ou separadamente. ativa Está andando rápido e deve chegar à versão 1.0 ainda neste ano. rom
  94. 2 / IMPLEMENTAÇÃO / LOTUS MODEL vantagens integração Repositórios podem

    integrar dados de diferentes fontes. validações Tem suporte a validações estilo Rails/ActiveModel. grafos Permite inserir vários objetos em uma única chamada. rom
  95. 2 / IMPLEMENTAÇÃO / LOTUS MODEL vantagens não necessariamente SQL

    SQL/Sequel, Memória, YeSQL, CouchDB, 
 Cassandra, MongoDB, Neo4J, Event Store, InfluxDB, RethinkDB, YAML, CSV, Git rom
  96. 2 / IMPLEMENTAÇÃO / LOTUS MODEL desvantagens complexo “Com grandes

    poderes, vêm grandes complexidades.” api low-level O usuário é obrigado a lidar com conceitos de baixo nível. funcional “Ruby não idiomático.” rom
  97. 2 / IMPLEMENTAÇÃO / LOTUS MODEL desvantagens verboso Muitos objetos

    e código boilerplate precisam ser escritos. muitas gems É difícil saber onde cada método está na documentação. rom
  98. 2 / IMPLEMENTAÇÃO / LOTUS MODEL desvantagens imaturo Projeto novo,

    não testado em produção, com base pequena de usuários. documentação A documentação é escassa, composta apenas por guias e API docs. rom
  99. 2 / IMPLEMENTAÇÕES outras gems curator 0.11.2 / set de

    2015 perpetuity 1.0.1 / jan de 2015 elasticsearch 1.0.13 / set de 2015 guacamole 0.4.0 / dez de 2014
  100. data mappers em ruby Apresentação realizada em 19/09/2015 como parte

    da RubyConf Brasil 2015, disponível em https://speakerdeck.com/lailsonbm/data-mappers-em-ruby. Slides criados no Keynote com as fontes Galano Grotesque, Bernino Sans e Daniel. 
 Creative Commons Attribution 4.0 International License. RUBYCONF BRASIL 2015 LAILSON BANDEIRA https://github.com/lailsonbm https://twitter.com/lailsonbm https://speakerdeck.com/lailsonbm