Bancos de dados relacionais dominaram o armazenamento de dados por
mais de 30 anos, mas a crescente popularidade de bancos de dados sem
esquemas (ou NoSQL) sugere que há uma mudança a caminho. Enquanto o
RDDMS fornece uma base sólida para armazenar dados em arquiteturas
cliente-servidor tradicionais, ele não escala de maneira fácil (ou
barata) para nós múltiplos. Esse é um ponto fraco infeliz na era de
aplicativos da Web altamente escaláveis, como o Facebook e o Twitter.
Considerando que as alternativas mais antigas ao banco de dados
relacional (lembra-se dos bancos de dados orientados a objetos?)
falhavam ao resolver problemas realmente urgentes, os bancos de dados
NoSQL, como o Bigtable do Google o SimpleDB da Amazon, surgiram como uma
resposta direta à demanda por alta escalabilidade da Web.
Em essência, o
NoSQL poderia ser um grande aplicativo para um grande problema um
problema que desenvolvedores de aplicativos da Web podem encontrar cada
vez mais, não menos, com a evolução da Web 2.0.
Nessa parte de Desenvolvimento Java 2.0,
introduzirei a modelagem de dados sem esquema, que é o principal
obstáculo do NoSQL para muitos desenvolvedores treinados na mentalidade
relacional.
Como será aprendido, começar com um modelo de domínio (ao
invés de um modelo relacional) é a chave para facilitar sua entrada.
Caso esteja usando o Bigtable, como usa meu exemplo, é possível
solicitar a ajuda do Gaelyk: uma extensão de estrutura leve para o
Google App Engine.
NoSQL: Uma nova mentalidade?
Quando desenvolvedores falam sobre bancos de dados não relacionais ou
NoSQL, a primeira coisa que é frequentemente dita é que eles necessitam
mudar sua mentalidade. Em minha opinião, isso depende, na verdade, de
sua abordagem inicial sobre modelagem de dados.
Caso esteja acostumado a
criar aplicativos modelando a estrutura do banco de dados primeiro (ou
seja, imaginar tabelas e seus relacionamentos primeiro), então a
modelagem de dados com um armazenamento de dados sem esquemas, como o
Bigtable, necessitará que você repense na maneira como faz as coisas. No
entanto, caso você crie seus aplicativos começando com um modelo de
domínio, então, você sentirá que a estrutura sem esquemas do Bigtable é
mais natural.
Armazenamentos de dados não relacionais não possuem tabelas de junção ou
chaves primárias, nem mesmo a noção de chaves estrangeiras (apesar de
que chaves de ambos os tipos estejam presentes em uma forma mais livre).
Então, você provavelmente terminará frustrado caso tente usar modelagem
relacional como uma base para a modelagem de dados em um banco de dados
NoSQL. Começar por um modelo de domínio simplifica as coisas; na
verdade, descobri que a flexibilidade da estrutura sem esquemas por
debaixo do modelo de domínio é animadora.
A complexidade relativa de mudar de um modelo de dados relacional para
um modelo de dados sem esquemas depende de sua abordagem: ou seja, se
você começa com uma estrutura relacional ou baseada no domínio.
Ao
migrar para um armazenamento de dados como o CouchDB ou Bigtable, você realmente
perde a engenhosidade de uma plataforma de persistência estabelecida,
como a Hibernate (até agora, pelo menos). Por outro lado, há o efeito
green-pasture de ser capaz de construir-se por si próprio. E, no
processo, você aprenderá, de maneira aprofundada, sobre os
armazenamentos de dados sem esquema.
Construído para escalar
Novas soluções surgem com os novos problemas de aplicativos da Web
altamente escaláveis. O Facebook não conta com um banco de dados
relacional para suas necessidades de armazenamento; ao invés disso, ele
usa um armazenamento de chave/valor essencialmente, um HashMap de alto desempenho. A solução interna, chamada de Cassandra, também é
usada pelo Twitter e Digg e foi recentemente doada à Apache Software
Foundation. A Google é uma outra entidade da Web cujo crescimento
explosivo levou à procura de armazenamento de dados não relacional
Bigtable é o resultado.
Entidades e relacionamentos
Um armazenamento de dados sem esquema oferece a flexibilidade de
criar um modelo de domínio com objetos primeiro (algo que estruturas
mais novas como o Grails facilitam automaticamente). O avanço de seu
trabalho torna-se, então, mapear seu domínio para o armazenamento de
dados subjacente, que no caso do Google App Engine, não poderia ser mais
fácil.
No artigo “Java development 2.0: Gaelyk for Google App Engine,”
introduzi o Gaelyk, uma estrutura baseada em Groovy que facilita
trabalhar com o armazenamento de dados subjacente do Google. Uma grande
parte desse artigo está focada na alavancagem do objeto Entity do Google. O exemplo a seguir (do artigo) mostra como entidades de objetos funcionam no Gaelyk.
Listagem 1. Persistência do objeto com Entidade
def ticket = new Entity("ticket")
ticket.officer = params.officer
ticket.license = params.plate
ticket.issuseDate = offensedate
ticket.location = params.location
ticket.notes = params.notes
ticket.offense = params.offense
Essa abordagem quanto à persistência do objeto funciona, mas é fácil
notar como ela pode se tornar fatigante caso usasse muito entidades de
ticket por exemplo, caso as estivesse criando (ou procurando) em
vários servlets. Ter um servlet comum (ou Groovlet) para cuidar das
tarefas para você aliviaria um pouco dessa responsabilidade. Uma opção
mais natural como demonstrarei seria modelar um objeto Ticket.
Estrutura por objeto
O padrão de favorecer mais o modelo do objeto que a estrutura do banco
de dados aparece em estruturas de aplicativos da Web modernas, como
Grails e Ruby on Rails, que enfatizam a estrutura de um modelo do objeto
e tratam da criação do esquema do banco de dados subjacente para você.
De volta às corridas
Ao invés de refazer o exemplo dos tickets da introdução ao Gaelyk,
farei algo novo e usarei um tema em execução neste artigo e construirei
um aplicativo para demonstrar as técnicas discutidas.
Como mostra o diagrama many-to-many na Figura 1, uma Race possui muitos Runners e um Runner pode pertencer a muitas Races.
Figura 1. Corrida e corredores
Se eu fosse usar uma estrutura de tabela relacional para criar esse
relacionamento, seriam necessárias, pelo menos, três tabelas: sendo a
terceira uma tabela de junção ligando um relacionamento many-to-many.
Estou feliz por não estar preso ao modelo de dados relacional.
Ao invés
disso, usarei o Gaelyk (e o código Groovy) para mapear esse
relacionamento many-to-many para a abstração do Bigtable do Google para o
Google App Engine. O fato de o Gaelyk permitir que uma Entity seja tratada como um Map torna o processo bem simples.
Um dos encantos do armazenamento de dados sem esquemas é que eu não
tenho que saber tudo logo de início, ou seja, é possível acomodar
mudanças mais facilmente do que seria possível com um esquema de banco
de dados relacional. (Observe que não estou sugerindo que não é possível
modificar um esquema; estou apenas dizendo que é possível fazer
mudanças de maneira mais fácil sem um esquema.)
Não irei definir
propriedades nos meus objetos de domínio, deixarei isso para a natureza
dinâmica do Groovy (que permite, em essência, tornar meus objetos do
domínio em intermediários na comunicação com os objetos Entity
do Google). Ao invés disso, usarei meu tempo para descobrir como eu
quero encontrar objetos e manipular relacionamentos. Isso é algo que o
NoSQL e as várias estruturas alavancando armazenamentos de dados sem
esquemas ainda não possuem integrado.
Escalando com Shards
Sharding é uma forma de particionamento que replica uma
estrutura de tabela nos nós, mas divide dados entre elas de maneira
lógica. Por exemplo, um nó poderia ter todos os dados relacionados a
contas que residem nos EUA e outro poderia ter todas as contas que
residem na Europa.
O desafio dos shards ocorre quando os nós possuem
relacionamentos ou seja, junções de shards cruzados. É um problema
difícil de ser resolvido e, em muitos casos, acaba não sendo suportado. Ao final do artigo, consulte a seção de Recursos
para um link para a minha discussão com Max Ross, do Google, sobre
sharding e o desafio da escalabilidade com bancos de dados relacionais.
A classe base Model
Começarei criando uma classe base que mantém uma instância de um objeto Entity. Então, permitirei que subclasses possuam propriedades dinâmicas que serão adicionadas à instância Entity correspondente através do método setProperty conveniente do Groovy. setProperty
é invocado para qualquer método setter que realmente não exista em um
objeto. (Se isso soar estranho, não se preocupe, pois lhe fará sentido
ao vê-lo em ação.)
A Listagem 2 mostra minha primeira tentativa com uma instância Model para meu aplicativo de exemplo:
Listagem 2. Uma classe base Model simples
package com.b50.nosql
import com.google.appengine.api.datastore.DatastoreServiceFactory
import com.google.appengine.api.datastore.Entity
abstract class Model {
def entity
static def datastore = DatastoreServiceFactory.datastoreService
public Model(){
super()
}
public Model(params){
this.@entity = new Entity(this.getClass().simpleName)
params.each{ key, val ->
this.setProperty key, val
}
}
def getProperty(String name) {
if(name.equals("id")){
return entity.key.id
}else{
return entity."${name}"
}
}
void setProperty(String name, value) {
entity."${name}" = value
}
def save(){
this.entity.save()
}
}
Observe como a classe abstrata define um construtor que toma um Map
das propriedades é sempre possível adicionar mais construtores mais
tarde e é o que farei em breve. Essa configuração é bem conveniente para
estruturas da Web, que frequentemente agem fora dos parâmetros que
estão sendo enviados de um formulário. Gaelyk e Grails englobam tais
parâmetros em um objeto chamado params. O construtor repete esse Map e invoca o método setProperty para cada par de chaves/valores.
Olhando para o método setProperty é possível ver que a chave está definida para o nome da propriedade daentity subjacente, enquanto que o valor correspondente é o valor da entity.
Truques do Groovy
Como mencionado anteriormente, a natureza dinâmica do Groovy permite
capturar chamadas de método para propriedades que não existam através de
métodos get e set Property. Assim, subclasses do Model
na Listagem 2 não têm que definir propriedades próprias elas
simplesmente delegam qualquer chamada para uma propriedade para um
objeto entity subjacente.
O código na Listagem 2 faz algumas outras coisas que são exclusivas
do Groovy e que valem a pena ser destacadas. Primeiramente, posso
ignorar o método acessador de uma propriedade pré-anexando @ à propriedade. Tenho que fazer isso para a referência do objeto entity no construtor, caso contrário, invocaria o método setProperty. Invocar setProperty nessa junção interromperia, obviamente, o padrão, já que a variável entity no método setProperty seria null.
Em segundo lugar, a chamada this.getClass().simpleName, no construtor, define o “tipo” de entity a propriedade simpleName irá produzir um nome de subclasse sem um prefixo do pacote (observe que simpleName é, realmente, uma chamada para getSimpleName, mas que o Groovy permite que eu tente acessar uma propriedade sem a chamada de método JavaBeans-esque correspondente.)
Finalmente, se uma chamada é realizada para a propriedade id (ou seja, a chave do objeto), o método getProperty é inteligente o bastante para perguntar à key subjacente por seu id. No Google App Engine, as propriedades da key das entities são automaticamente geradas.
A subclasse Race
Definindo a subclasse Race é tão fácil quanto parece, como mostra a Listagem 3:
Listagem 3. Uma subclasse Race
package com.b50.nosql
class Race extends Model {
public Race(params){
super(params)
}
}
Quando uma subclasse é instanciada com uma lista de parâmetros (ou seja, um Map contendo pares de chave/valores) uma entity correspondente é criada na memória). Para persisti-la, necessito somente invocar o método save.
Listagem 4. Criando uma instância Race e salvando-a em um armazenamento de dados do GAE
import com.b50.nosql.Runner
def iparams = [:]
def formatter = new SimpleDateFormat("MM/dd/yyyy")
def rdate = formatter.parse("04/17/2010")
iparams["name"] = "Charlottesville Marathon"
iparams["date"] = rdate
iparams["distance"] = 26.2 as double
def race = new Race(iparams)
race.save()
Na Listagem 4, que é um Groovelet, um Map (chamado iparams) é criado com três propriedades um nome, uma data e uma distância para a corrida. (Observe que no Groovy, um Map é criado através de [:].) Uma nova instância de Race é criada e, consequentemente, salva no armazenamento de dados subjacente através do método save.
É possível verificar o armazenamento de dados subjacente através do
console do Google App Engine para ver que os dados estão realmente lá,
como mostra a Figura 2:
Figura 2. Visualizando a Race recém-criada
Métodos localizadores produzem Entidades persistidas
Agora que possuo uma Entity salva, seria útil ter a
capacidade de recuperá-la; subsequentemente, é possível adicionar um
método “localizador”. Neste caso, o transformarei em um método de
classes (static) e permitirei que as Races sejam encontradas pelo nome (ou seja, farei uma busca baseada na propriedade name). Mais tarde, é sempre possível adicionar outros localizadores para buscar por outras propriedades.
Também adotarei uma convenção para meus localizadores, especificando que qualquer localizador sem a palavra all em seu nome tem o objetivo de encontrar one instance, ou seja, uma instância. Localizadores com a palavra all (como em findAllByName) podem retornar uma Collection ou uma List de instâncias. A Listagem 5 mostra o localizador findByName:
Listagem 5. Um localizador simples efetuando uma busca baseada em um nome de Entidade
static def findByName(name){
def query = new Query(Race.class.simpleName)
query.addFilter("name", Query.FilterOperator.EQUAL, name)
def preparedQuery = this.datastore.prepare(query)
if(preparedQuery.countEntities() > 1){
return new Race(preparedQuery.asList(withLimit(1))[0])
}else{
return new Race(preparedQuery.asSingleEntity())
}
}
Esse localizador simples usa tipos Query e PreparedQuery
do Google App Engine para encontrar uma entidade do tipo “Race”, cujo
nome corresponda (exatamente) ao que estiver definido. Caso mais de uma Race corresponda a esse critério, o localizador retornará o primeiro de uma lista, como instruído pelo limite de paginação de 1 (withLimit(1)).
O findAllByName correspondente seria similar, mas com um parâmetro adicionado de quantos você deseja?, como mostra a Listagem 6:
Listagem 6. Encontrar todos por nome
static def findAllByName(name, pagination=10){
def query = new Query(Race.class.getSimpleName())
query.addFilter("name", Query.FilterOperator.EQUAL, name)
def preparedQuery = this.datastore.prepare(query)
def entities = preparedQuery.asList(withLimit(pagination as int))
return entities.collect { new Race(it as Entity) }
}
Como o localizador previamente definido, findAllByName encontra instâncias Race por nome, mas ele retorna todas as Races. A propósito, método collect Groovy é mais engenhoso: ele permite que eu inclua um loop correspondente que cria instâncias Race. Observe como o Groovy também permite valores padrões para parâmetros de método; assim, caso eu não defina um segundo valor pagination terá o valor 10.
Listagem 7. Localizadores em ação
def nrace = Race.findByName("Charlottesville Marathon")
assert nrace.distance == 26.2
def races = Race.findAllByName("Charlottesville Marathon")
assert races.class == ArrayList.class
Os localizadores na Listagem 7 funcionam da maneira esperada: findByName retorna uma instância, enquanto
findAllByName uma Collection (considerando que haja mais de uma “Maratona de Charlottesville”).
Os objetos Runner não são muito diferentes
Agora que me sinto confortável para criar e encontrar instâncias de Race, estou pronto para criar um objeto Runner rápido. O processo é tão fácil como foi a criação da instância Race inicial; simplesmente estendo o Model, como mostra a Listagem 8.
Listagem 8. Um Runner é muito fácil
package com.b50.nosql
class Runner extends Model{
public Runner(params){
super(params)
}
}
Olhando para a Listagem 8,
tenho a sensação de que estou quase na linha de chegada. Ainda tenho
que criar os links entre os corredores e as corridas. E, é claro, farei a
modelagem como relacionamentos many-to-many, pois espero que meus
corredores realizem mais de uma corrida.
Modelagem do domínio sem um esquema
A abstração do Google App Engine sobre o Bigtable não é orientada ao
objeto; ou seja, não é possível salvar relacionamentos dessa maneira,
mas é possível compartilhar chaves. Consequentemente, a fim de modelar o
relacionamento entre Races e Runners, armazenarei uma lista de chaves Runner dentro de cada instância de Race e vice-versa.
No entanto, terei que adicionar um pouco de lógica em meu mecanismo
de compartilhamento de chaves, pois quero que minha API resultante seja
natural não quero pedir a uma Race por uma lista de chaves Runner, eu quero uma lista de Runners. Felizmente, isso não é difícil.
Na Listagem 9, adicionei dois métodos à instância Race. Quando uma instância Runner é passada para o método addRunner seu id correspondente é adicionado à Collection de ids na propriedade runners da entity subjacente. Caso haja uma collection existente de runners, a nova chave instância Runner é adicionada a ela; caso contrário, uma nova Collection é criada e chave do Runner (a propriedade id na entidade) é adicionada a ela.
Listagem 9. Adicionando e recuperando corredores
def addRunner(runner){
if(this.@entity.runners){
this.@entity.runners << runner.id
}else{
this.@entity.runners = [runner.id]
}
}
def getRunners(){
return this.@entity.runners.collect {
new Runner( this.getEntity(Runner.class.simpleName, it) )
}
}
Quando o método getRunners na Listagem 9 é invocado, uma coleta de instâncias Runner é criada a partir da coleção subjacente de ids. Assim, um novo método (getEntity) é definido na classe Model como mostra a Listagem 10:
Listagem 10. Criando uma entidade a partir de um ID
def getEntity(entityType, id){
def key = KeyFactory.createKey(entityType, id)
return this.@datastore.get(key)
}
O método getEntity usa a classe KeyFactory do Google para criar a chave subjacente que pode ser usada para encontrar uma entidade individual dentro do armazenamento de dados.
Finalmente, um novo construtor que aceita um tipo de entidade é definido, como mostra a Listagem 11:
Listagem 11. Um construtor recém-adicionado
public Model(Entity entity){
this.@entity = entity
}
Como é possível ver nas Listagens 9, 10, e 11 e no modelo de objeto da Figura 1, é possível adicionar um Runner a qualquer Race e também é possível obter uma lista de instâncias Runner de qualquer Race. Na Listagem 12, crio uma ligação similar do lado da equação do Runner. A Listagem 12 mostra os novos métodos da classe do Runner.
Listagem 12. Corredores e suas corridas
def addRace(race){
if(this.@entity.races){
this.@entity.races << race.id
}else{
this.@entity.races = [race.id]
}
}
def getRaces(){
return this.@entity.races.collect {
new Race( this.getEntity(Race.class.simpleName, it) )
}
}
Dessa maneira, consegui modelar dois objetos de domínio com um armazenamento de dados sem esquemas.
Terminando a corrida com alguns corredores
Agora, tudo que necessito criar é uma instância Runner e adicioná-la a uma Race. Caso queira que o relacionamento seja bidirecional, como meu modelo de objeto na Figura 1 mostra, também posso adicionar instâncias Race aos Runners, como mostra a Listagem 13:
Listagem 13. Corredores com suas corridas
def runner = new Runner([fname:"Chris", lname:"Smith", date:34])
runner.save()
race.addRunner(runner)
race.save()
runner.addRace(race)
runner.save()
Após adicionar um novo Runner à race e a chamada para save da Race, o armazenamento de dados foi atualizado com uma lista de IDs, como mostra a captura de tela na Figura 3:
Figura 3. Visualizando a nova propriedade de corredores em uma corrida
Examinando de perto os dados no Google App Engine, é possível ver que uma entidade Race possui agora uma list de Runners, como mostra a Figura 4.
Figura 4. Visualizando a nova lista de corredores
Do mesmo modo, antes de adicionar uma Race a uma instância Runner recém-criada, a propriedade não existe, como mostra a Figura 5.
Figura 5. Um corredor sem uma corrida
Porém, depois de associar uma Race a um Runner, o armazenamento de dados adiciona a nova list de ids de races.
Figura 6. Um corredor fora das corridas
A flexibilidade do armazenamento de dados sem esquema é animadora
propriedades são adicionadas automaticamente ao armazenamento subjacente
sob demanda. Como desenvolvedor, não preciso atualizar ou alterar um
esquema, muito menos implementar!
Prós e contras do NoSQL
Há, é claro, prós e contras da modelagem de dados sem esquemas. Uma
vantagem do aplicativo Back to the Races é que ele é bastante flexível.
Caso eu decida adicionar uma nova propriedade ao Runner (como um SSN), não há muito o que ser feito na verdade, se eu incluí-la
nos parâmetros do construtor, ela estará lá. O que acontece com as
instâncias mais antigas que ainda não foram criadas com um SSN? Nada.
Elas possuem um campo que é null
Por outro lado, está claro que troquei consistência e integridade por
eficiência. A arquitetura de dados atual do aplicativo não me deixa com
nenhuma restrição seria possível, teoricamente, criar um número
infinito de instâncias do mesmo objeto.
Sob a manipulação de chaves do
Google App Engine, todas elas teriam chaves únicas, mas todo o restante
seria idêntico. O pior é que exclusões em cascata não existem, então,
se eu usasse a mesma técnica para modelar relacionamentos one-to-many e o
pai fosse removido, eu terminaria com um filho ultrapassado. É claro
que seria possível implementar minha própria verificação de integridade
mas isso é importante: Eu mesmo teria que fazer isso (assim como fiz
todo o restante).
Usar armazenamentos de dados sem esquemas requer disciplina. Caso crie vários tipos de Races algumas com nomes, outras sem nome, algumas com uma propriedade date e outras com uma propriedade race_date estaria dando um tiro em meu próprio pé (e no pé de todas as outras pessoas que alavancarem meu código).
É claro que ainda é possível usar JDO e JPA com o Google App Engine.
Tendo usando tanto os modelos relacionais quanto o sem esquemas em
projetos múltiplos, posso dizer que a API de baixo nível do Gaelyk é a
mais flexível e divertida para se trabalhar. Uma outra vantagem de usar o
Gaelyk é obter um entendimento melhor de armazenamentos de dados
Bigtable e sem esquemas, no geral.
Leitores rápidos
A velocidade é um fator importante no argumento NoSQL versus relacional.
Para um Web site moderno passando dados para, potencialmente, milhões
de usuários (pense no crescente número de 400 milhões de usuários do
Facebook), o modelo relacional é simplesmente muito lento, sem mencionar
caro. Os armazenamentos de dados NoSQL, em comparação, são extremamente
rápidos no que diz respeito a leituras.
Concluindo
Tendências vem e vão e, algumas vezes, é seguro ignorá-las (conselho
sábio vindo de um rapaz com um guarda-roupa cheio de ternos antiquados).
Mas o NoSQL se parece menos com uma tendência e mais como uma base
emergente para o desenvolvimento de aplicativos da Web altamente
escaláveis.
No entanto, os bancos de dados NoSQL não substituirão o
RDBMS; eles irão suplementá-lo. Uma grande quantidade de ferramentas e
estruturas bem sucedidas residem sobre bancos de dados relacionais e os
próprios RDBMSs parecem não correr risco de perder popularidade.
O que os bancos de dados NoSQL fazem é, finalmente, apresentar uma
alternativa adequada ao modelo de dados objeto-relacional. Eles mostram
que algo diferente é possível e para casos de uso específico e
altamente atrativo melhor Bancos de dados sem esquemas são mais
adequados para aplicativos da Web multinós que necessitam de recuperação
de dados rápida e escalabilidade.
Como um efeito colateral, eles estão
ensinando desenvolvedores a abordarem a modelagem de dados a partir de
uma perspectiva orientada ao domínio, ao invés de uma perspectiva
relacional.
Recursos
Aprender
- Java development 2.0:
Esta série do developerWorks explora tecnologias e ferramentas que
estão redefinindo o panorama de desenvolvimento Java, incluindo Gaelyk (dezembro de 2009), Google App Engine (agosto de 2009), e CouchDB (novembro 2009). - “NoSQL Patterns”
(Ricky Ho, Pragmatic Programming Techniques, novembro de 2009): Uma
visão geral e listagens de bancos de dados NoSQL, seguida por um olhar
mais aprofundado sobre a arquitetura comum de bancos de dados NoSQL. - “Saying Yes to NoSQL; Going Steady with Cassandra” (John Quinn, Digg Blogs, março de 2010): O VP de engenharia do Digg explica a decisão de mudar do MySQL para o Cassandra.
- “Sharding with Max Ross”
(podcast do JavaWorld, julho de 2008): Andrew Glover conversa com Max
Ross, do Google, sobre a técnica de sharding e o desenvolvimento de
Hibernate Shards. - “Is the Relational Database Doomed?”
(Tony Bain, ReadWriteEnterprise, fevereiro de 2009): Com bancos de
dados não relacionais crescendo tanto dentro como fora da nuvem, uma
mensagem clara está surgindo: “Caso queira escalabilidade ampla e sob
demanda, é necessário um banco de dados não relacional.” - Google App Engine for Java: Part 3: Persistence and relationships”
(Richard Hightower, developerWorks, agosto de 2009): Rick Hightower
explica as fraquezas da estrutura de persistência atual baseada em Java
do Google App Engine e discute algumas das soluções alternativas. - “Cloud computing with Amazon Web Services, Part 5: Dataset processing in the cloud with SimpleDB”
(Prabhakar Chaganti, developerWorks, fevereiro de 2009): Aprenda os
conceitos do SimpleDB da Amazon e explore algumas das funções fornecidas
pela boto, uma biblioteca Python de software livro para interagir com
ele. - “Bigtable: A Distributed Storage System for Structured Data”
(Fay Chang et al., Google, novembro de 2006): Bigtable é um sistema de
armazenamento distribuído para gerenciar dados estruturados que foi
criado para ser escalado para um tamanho muito grande: petabytes de
dados em milhares de servidores de bens. - “The Vietnam of Computer Science” (Ted Neward, junho de 2006): Trata dos desafios associados ao mapeamento de objetos para modelos relacionais.
- Procure na livraria tecnológica livros sobre esses e outros assuntos técnicos.
- Zona de tecnologia Java do developerWorks: Encontre centenas de artigos sobre cada aspecto da programação Java.
Obter produtos e tecnologias
- Gaelyk: Comece a usar a estrutura leve de desenvolvimento de aplicativos Groovy para o Google App Engine.
artigo publicado originalmente no developerWorks Brasil, por Andrew Glover
Andrew Glover é um desenvolvedor, autor, palestrante e empreendedor com
grande interesse em behavior-driven development (BDD – Desenvolvimento
Guiado por Comportamento), Continuous Integration (CI – Integração
contínua) e no desenvolvimento ágil de software. É possível acompanhá-lo
em seu blog.