Académique Documents
Professionnel Documents
Culture Documents
Grails
Um Caso de Sucesso
Agilidade em uma empresa de desenvolvimento para Gestão Hospitalar
C ada vez mais aplicações corporativas são desenvolvidas ou migradas para a plataforma web.
Isso acontece porque a web traz diversos avanços e facilidades no que diz respeito à inte-
gração de múltiplos usuários. O conceito de servidores de aplicações permite que usemos alguns padrões
para solucionar problemas comuns do modelo client server, como gerenciamento de estado conversacional
e sincronismo de informações, de forma praticamente transparente para os desenvolvedores. A única coisa
exigida dos desenvolvedores é que conheçam os padrões.
Junto com o uso dos objetos 'session', 'request' e 'response', esses padrões formam uma convenção (ver o
quadro Convenção sobre Configuração), muito conhecida pelos desenvolvedores web ao redor do mundo.
42 www.mundoj.com.br
"SUJHPt(SBJMTo6N$BTPEF4VDFTTP
O Grails faz uso extensivo de convenções, com o objetivo de simplificar a aos requisitos de uma aplicação web completa. Para isso, utiliza o Spring
vida do desenvolvedor. Há, porém, casos em que as conveções não se apli- para injeção de dependências, o Log4J para o sistema de logs, o Sitemesh
cam. Para esses casos, o Grails (e outros frameworks) oferecem a alternativa para o sistema de layouts e templates, o Hibernate para persistência e o
de configurar. A diferença é que, neste caso, a configuração é a exceção à JUnit para testes, além de ser totalmente baseado no modelo de Servlets e
regra, o que na prática significa bem menos configurações. JSPs. Como podemos notar, o Grails é mais uma camada de abstração que
ao facilitar o uso de frameworks complexos, nos permite mais tranquilida-
Descobrimos então que a plataforma web é a primeira opção quando se de para desenvolver nossas aplicações.
fala em desenvolver uma aplicação hoje em dia, porque é mais fácil de se
desenvolver com essas convenções todas disponíveis. A sensação é de que Apesar de usar os frameworks mais utilizados, a grande diferença consiste
a maioria dos problemas foram resolvidos e só nos resta trabalhar com foco exatamente na interface que o Grails disponibiliza para que o desenvol-
nas regras de negócio. Os frameworks mais tradicionais da plataforma Java vedor desfrute destes mecanismos. Isso quer dizer que para utilizar o site-
utilizam-se dessas convenções de forma muito eficaz e já estão consolida- mesh, há uma convenção bem mais simples do que se você fosse utilizá-lo
dos no mercado. diretamente em sua aplicação. O mesmo acontece com o Hibernate, Log4J,
Spring e os outros.
Há, porém, alguns problemas quando se fala de frameworks mais tra-
dicionais, principalmente no uso de configurações em excesso. Estes
frameworks são muito dependentes de configurações, o que dificulta em Plugins
muito a vida dos desenvolvedores. É comum que as configurações tenham
que ser repetidas para cada ação da aplicação. Isso acontece frequente- Além de sua arquitetura padrão, já presente no download
mente com JSF e Struts, por exemplo. original, há ainda a possibilidade de instalar plugins, que
complementam o framework. Dentre os diversos exemplos
Uma nova geração de frameworks tem surgido para enfrentar este proble- de plugins, temos o plugin para integração com o JPA, Acegi,
ma. Estes são frameworks que permitem o aproveitamento de convenções JSecurity, o Adobe Flex através do BlazeDS, o Quartz para
ao invés de configurações, mas que quando necessário ainda permite que scheduling etc.
sejam configurados a um nível bem satisfatório. Para a plataforma Java, um
dos principais players deste mercado é o framework Grails. Tudo isso pode ser conferido direto no site do framework,
www.grails.org. Já dá para notar que o Grails é um framework
O que é Grails que veio para ficar.
A grande vantagem do Grails é sua filosofia voltada para o uso de convenções java.util.ArrayList myList = new java.util.ArrayList();
ao invés de configurações (Convention over Configuration), elevando ainda
mais as vantagens da plataforma web, no que diz respeito à produtividade e
facilidade de desenvolvimento. Podemos ainda utilizar uma sintaxe um pouco mais groovy:
A composição do Grails
O uso de [] nesse caso não cria um array, mas sim uma instância da classe Ar-
O Grails é um framework MVC que encapsula vários outros frameworks que rayList. Note que o ; é opcional, assim como o uso de return.
estão presentes na grande maioria das aplicações web atuais para atender
Se quiser testar mais a fundo a integração, pegue qualquer programa Java
43
"SUJHPt(SBJMTo6N$BTPEF4VDFTTP
A maioria dos programas irá funcionar normalmente, como se fosse compilado Closures
em Java, com algumas raríssimas exceções. Raras mesmo.
Retirando um exemplo do livro Programming Groovy escrito por Venkat Subram- Há ainda no Groovy uma funcionalidade chamada closure, que nada
manian para os famosos Pragmatics Programmers, veremos como a sintaxe do mais é do que uma instância da classe groovy.lang.Closure.
Groovy é um alívio para os programadores java, ao invés de ser um peso. Análise
este programa em Java: Closure cl = { println “Eu sou uma closure” }
System.out.println(“Year: “ + car.getYear());
System.out.println(“Miles: “ + car.getMiles());
System.out.println(“Setting miles”); Podemos ainda ter closures que recebem parâmetros. No exemplo a se-
car.setMiles(25); guir, passaremos um parâmetro indicando o nome de alguém, para que
System.out.println(“Miles: “ + car.getMiles()); a closure imprima uma saudação.
}
}
def saudar = { nome -> println “Olá, ” + nome }
Agora veja o mesmo programa, escrito em groovy:
class Car { Repare que indicamos os parâmetro dentro das chaves e, em caso de ha-
def miles = 0 ver mais de um parâmetro, podemos separá-los por vírgula. Podemos até
final year mesmo indicar o tipo desse parâmetro como na declaração de métodos
comuns. Veja como fica a execução dessa closure.
Car(theYear) { year = theYear }
44 www.mundoj.com.br
"SUJHPt(SBJMTo6N$BTPEF4VDFTTP
Herança?
Neste caso, o uso da herança é interessante devido à forma como o Grails trata
esses registros no banco de dados. Não teremos complicações nenhuma com
o Hibernate, visto que o mapeamento será feito pelo Grails de forma automá-
tica. No banco de dados, o Grails irá criar uma única tabela para essas classes,
acrescentando uma coluna com o nome “class” na tabela, que permitirá a clas-
Figura 1. Mapeamento de urls do Grails. sificação dos registros. Essa coluna reflete o valor do atributo class do objeto
tratado, que irá variar entre Pessoa, Paciente e Médico.
de negócio. Podem se relacionar e são automaticamente mapeados para per-
sistência através do wrapper do hibernate que já vem com o Grails, chamado Optei por este caminho no artigo por motivos didáticos. Há outras opções,
GORM. Elas podem se relacionar e também serem utilizadas normalmente, como composição através do embedded, por exemplo.
pois são classes simples e normais. Os domain devem ser colocados dentro Vejamos como desenvolver uma pequena tela de atendimento para este
do diretório grails-app/domains, podendo colocar em pacotes ou não. modelo.
As tags podem ser usadas tanto nas views, como também nos controllers, na
forma de métodos comuns, permitindo o reuso de lógicas como obtenção
de códigos i18n. Além disso, você também pode criar suas próprias taglibs
de forma simples e descomplicada.
'JHVSB&TUSVUVSBEFVNBBQMJDBÎÍP(SBJMT
Para finalizar, temos os Services. Services são classes que devem encapsular
operações de negócio muito complexas, que precisem de lógicas mais ex- Criando a aplicação
tensas. Dessa forma, podemos separar as regras de negócio das regras de
apresentação e, assim, deixar nossas actions e contrllers muito mais limpos. Para criar uma aplicação utilizando o Grails, devemos apenas emitir o
Os services são classes comuns que possuem métodos. Por padrão, são sta- comando grails create-app atendimento. Com esse comando, o Grails
teless, porém há formas de mudar isso. A exemplo dos outros artefatos, os emitirá uma série de ações, executando alguns scripts para a estrutura-
services são organizados dentro do diretório grails-app/services. ção da sua aplicação. Ao final, deve haver um diretório com o nome aten-
dimento no diretório onde você emitiu o comando acima. A estrutura de
Integração pronta diretórios da aplicação pode ser vista na figura 3.
45
"SUJHPt(SBJMTo6N$BTPEF4VDFTTP
O case
Para exemplificar como o framework pode ser utilizado, nada mais interessante O desafio consiste em fazer com que o conhecimento e a produtividade que es-
do que apresentarmos um case de sucesso. tes desenvolvedores, vindos do ambiente VB, sejam mantidos para o novo sis-
tema web. Isso exige que a curva de aprendizado e a transição sejam mantidas.
O case se passa na Wareline, uma empresa de Campinas focada no desenvolvi-
mento de softwares de gestão hospitalar. O sistema atual deles é desenvolvido Os paradigmas Desktop e Web são bem diferentes e, apesar de passarem por
em visual basic, com módulos web, escritos em Java e PHP. A empresa já manti- treinamento antes do projeto, as dificuldades que podem surgir são inúmeras.
nha uma relação estreita com o mundo open source, pois há um bom tempo já
utiliza o banco de dados PostgreSQL em todos os seus sistemas. Nesse aspecto, o Grails é favorecido, pois uma vez que o desenvolvedor se
acostume com as convenções, basta segui-las. Com o tempo, conforme os de-
Para esse projeto, o objetivo é migrar todo o sistema para a plataforma Web. senvolvedores vão ganhando mais experiência, podem começar a customizar
Participei da definição da arquitetura, do processo de gestão, e agora acompa-
as partes necessárias sem perder produtividade ou prejudicar o funcionamento
nho a evolução do projeto.
geral do framework.
Metade da equipe formada possui experiência no desenvolvimento para a
web, basicamente com background em servlets e JSPs. A outra metade é for- Contar com um ambiente pronto, estável e baseado nas melhores práticas
mada por desenvolvedores trazidos do Visual Basic para o novo projeto, pois diminui muito os riscos da migração da plataforma desktop para a web, sem
detém grande parte do conhecimento do negócio. trocar todos os desenvolvedores.
O Domain
s Paciente
tend
Em nosso exemplo, vamos tratar de uma pequena parte do domain desta ex 1
aplicação: o atendimento de pacientes na recepção do hospital. Obviamente,
neste artigo, vou apresentar somente uma pequena e incompleta parte do Pessoa N
modelo real da aplicação, devido às limitações de espaço. 1
ex
ten Atendimento
Na figura 2, vemos uma parte do domínio. Um dos trechos mais simples da ds
N
aplicação e que já reflete a grande quantidade de relacionamentos entre 1
entidades que há neste projeto. Temos uma entidade Atendimento, que repre- 1
senta cada atendimento feito. Um atendimento sempre deve referenciar um Endereço
Paciente e um Médico. Além disso, Médico e Paciente são subclasses de Pessoa, Médico
que por sua vez contém um Endereço.
Figura 2. Modelo da aplicação de exemplo.
Repare como os relacionamentos são declarados: utiliza-se belongsTo para static constraints = {
endereco(nullable:true)
determinar à qual classe aquela pertence. O hasMany para indicar quando }
uma classe possui um conjunto de objetos de uma outra classe. Isso faz parte
}
de uma série de convenções e configurações possíveis com o mecanismo de
persistência do Grails, o GORM (Grails Object Relational Mapper). O GORM
possibilita realizar praticamente qualquer coisa que se faz com o Hibernate, já Listagem 3. Classe Paciente.
que ele é uma abstração sobre este framework.
class Paciente extends Pessoa {
Agora, seguindo o conceito do MVC, uma vez que já temos as classes de
}
domínio bem definidas, precisamos escrever os Controllers e as Views. Um
controller no Grails não passa de uma classe com algumas closures públicas.
Essas closures são similares a métodos comuns e serão tratadas como actions
46 www.mundoj.com.br
"SUJHPt(SBJMTo6N$BTPEF4VDFTTP
Este arquivo deve seguir uma convenção de diretórios para que o Grails o en-
contre. Ele deve ser colocado em grails-app/views/atendimento/list.gsp. Note
que dentro do diretório views teremos um subdiretório para cada controller.
A partir daqui, já podemos executar a aplicação e visualizar o que está feito. A def create = {
figura 4 mostra a listagem com alguns atendimentos já cadastrados e pode ser return [‘atendimento’: new Atendimento()]
acessada através da url http://localhost:8080/atendimento/atendimento/list. }
def save = {
Agora só precisamos de uma action para inserir um novo atendimento. def atendimento = new Atendimento(params)
Para isso, vamos criar uma action em nosso controller de atendimento. if(!atendimento.hasErrors() && atendimento.save()) {
flash.message = “Atendimento ${atendimento.id} created”
Como visto na Listagem 8, adicionamos duas actions ao Atendimento- redirect(action:list)
Controller. A action create irá criar uma nova instância de atendimento e } else {
então renderizar, por convenção, a view create, mostrada na Listagem 9. render(view:’create’,model:[atendimento:atendimento])
}
A action save obtém os dados através do mapa params e os grava no banco. }
47
"SUJHPt(SBJMTo6N$BTPEF4VDFTTP
Vale a pena notar que nós verificamos a existência de erros de casting através
da chamada a atendimento.hasErrors(). Caso esta chamada retorne false e a
chamada ao método atendimento.save() retorne true, então iremos redirecio-
nar para a nossa action list, para listarmos o novo registro. Além disso, adiciona-
mos uma mensagem para ser exibida na página de destino.
Caso não consigamos salvar por algum motivo, seja erro de casting ou alguma
outra validação, como a ausência do médico ou do paciente, voltaremos à view
create, em que a tag <g:hasErrors bean="${atendimento}"> irá cuidar para que
os problemas sejam exibidos para o usuário. A figura 5 mostra como deve ficar
o formulário de criação do atendimento que pode ser acessado através da url
http://localhost:8080/atendimento/atendimento/create.
<div class=”body”>
<h1>Create Atendimento</h1>
<g:if test=”${flash.message}”>
<div class=”message”>${flash.message}</div>
</g:if>
<g:hasErrors bean=”${atendimento}”>
<div class=”errors”> 'JHVSB'PSNVMÈSJPQBSBDSJBÎÍPEFVNBUFOEJNFOUP
<g:renderErrors bean=”${atendimento}” as=”list” />
</div>
</g:hasErrors> utilizar o controle transacional default do Grails. Os services podem ser
<g:form action=”save” method=”post” > facilmente utilizados nos controllers através da injeção de dependências
<div class=”dialog”> por convenção.
<div class=
”value ${hasErrors(bean:atendimento,field:’data’,’errors’)}”>
<label for=”data”>Data:</label><br/> Após refatorar o código da action save, a action fica como na Listagem 10.
<g:datePicker name=”data” value= ”${atendimento?.data}”
precision=”minute” ></g:datePicker>
</div><br/> Listagem 10. Action save refatorada para utilizar um service.
<div class=
”value ${hasErrors(bean:atendimento,field:’medico’,’errors’)}”>
<label for=”medico”>Medico:</label><br/> def save = {
<g:select optionKey=”id” optionValue=”nome”
from=”${Medico.list()}” name=”medico.id”
value=”${atendimento?.medico?.id}” ></g:select> def atendimento = new Atendimento(params)
</div><br/>
<div class= if(!atendimento.hasErrors() && atenderService.
”value ${hasErrors(bean:atendimento,field:’paciente’,’errors’)}”>
<label for=”paciente”>Paciente:</label><br/>
<g:select optionKey=”id” optionValue=”nome” salvarAtendimento(atendimento)) {
from=”${Paciente.list()}” name=”paciente.id”
value=”${atendimento?.paciente?.id}” ></g:select> flash.message = “Atendimento ${atendimento.id} created”
</div><br/>
<div class=
”value ${hasErrors(bean:atendimento,field:’queixa’,’errors’)}”> redirect(action:list)
<label for=”queixa”>Queixa:</label><br/>
<input type=”text” id=”queixa” name=”queixa” } else {
value=”${fieldValue(bean:atendimento,field:’queixa’)}”/>
</div><br/>
</div> render(view:’create’,model:[atendimento:atendimento])
<div class=”buttons”>
<span class=”button”><input class=”save” type=”submit” }
value=”Create” /></span>
</div>
</g:form> }
</div>
Agora precisamos criar o service, conforme a Listagem 11. Os services
devem ficar no diretório grails-app/services e por convenção seus nomes
Encapsulando as operações em Services devem terminar com a palavra Service.
Apesar de esse ser um exemplo simples, o case real não é tão simples as-
sim. A complexidade das operações de negócio pode tornar essas actions Listagem 11. AtenderService.groovy.
muito grandes e difíceis de manter. Os controllers devem ser magros, visto
public class AtenderService {
que sua função é apenas chamar as operações de negócio adequadas e re-
boolean salvarAtendimento(atendimento){
direcionar a requisição para a view adequada. Para resolver isso, podemos return atendimento.save()
utilizar os services do Grails. Com o uso do services, podemos centralizar as }
operações de negócio em uma classe separada ao controller e, além disso, }
48 www.mundoj.com.br
"SUJHPt(SBJMTo6N$BTPEF4VDFTTP
49