Crie um aplicativo da Web MVC com Grails

Crie um aplicativo da Web MVC com Grails

 

1. Visão geral

Neste tutorial, aprenderemos como criar um aplicativo da web simples usandoGrails.

Grails (mais precisamente, a versão principal mais recente) é uma estrutura construída em cima do projeto Spring Boot e usa a linguagem Apache Groovy para desenvolver aplicativos da web.

É inspirado no Rails Framework para Ruby eis built around the convention-over-configuration philosophy which allows reducing boilerplate code.

2. Configuração

Em primeiro lugar, vamos passar à página oficial para preparar o ambiente. No momento deste tutorial, a versão mais recente é 3.3.3.

Simplificando, existem duas maneiras de instalar o Grails: via SDKMAN ou baixando a distribuição e adicionando binários à variável de ambiente PATH.

Não cobriremos a configuração passo a passo porque ela está bem documentada noGrails Docs.

3. Anatomia de um aplicativo Grails

Nesta seção, entenderemos melhor a estrutura da aplicação Grails. Como mencionamos anteriormente, o Grails prefere a convenção do que a configuração, portanto, a localização dos arquivos define sua finalidade. Vamos ver o que temos no diretóriograils-app:

  • assets - um lugar onde armazenamos arquivos de ativos estáticos, como estilos, arquivos javascript ou imagens

  • conf - contém arquivos de configuração do projeto:

    • application.yml contém configurações de aplicativo da web padrão, como fonte de dados, tipos MIME e outras configurações relacionadas a Grails ou Spring

    • resources.groovy contém definições de feijão de primavera

    • logback.groovy contém configuração de registro

  • controllers - responsável por lidar com solicitações e gerar respostas ou delegá-las às visualizações. Por convenção, quando um nome de arquivo termina com*Controller, o framework cria um mapeamento de URL padrão para cada ação definida na classe do controlador

  • domain - contém o modelo de negócios do aplicativo Grails. Cada classe que mora aqui será mapeada para as tabelas do banco de dados pelo GORM

  • i18n - usado para suporte de internacionalização

  • init - um ponto de entrada do aplicativo

  • services - a lógica de negócios do aplicativo ficará aqui. Por convenção, o Grails criará um bean singleton Spring para cada serviço

  • taglib - o local para bibliotecas de tags personalizadas

  • views - contém visualizações e modelos

4. Um aplicativo da web simples

Neste capítulo, criaremos um aplicativo Web simples para gerenciar alunos. Vamos começar invocando o comando CLI para criar um esqueleto de aplicativo:

grails create-app

Quando a estrutura básica do projeto for gerada, vamos prosseguir para a implementação de componentes reais do aplicativo da web.

4.1. Camada de domínio

Como estamos implementando um aplicativo da web para lidar com os alunos, vamos começar gerando uma classe de domínio chamadaStudent:

grails create-domain-class com.example.grails.Student

E, finalmente, vamos adicionar as propriedadesfirstNameelastName a ele:

class Student {
    String firstName
    String lastName
}

Grails aplica suas convenções e irá configurar um mapeamento objeto-relacional para todas as classes localizadas no diretóriograils-app/domain.

Além disso, graças ao traçoGormEntity,all domain classes will have access to all CRUD operations, que usaremos na próxima seção para implementar serviços.

4.2. Camada de serviço

Nosso aplicativo tratará dos seguintes casos de uso:

  • Visualizando uma lista de alunos

  • Criando novos alunos

  • Remoção de alunos existentes

Vamos implementar esses casos de uso. Começaremos gerando uma classe de serviço:

grails create-service com.example.grails.Student

Vamos ao diretóriograils-app/services, encontre nosso serviço recém-criado no pacote apropriado e adicione todos os métodos necessários:

@Transactional
class StudentService {

    def get(id){
        Student.get(id)
    }

    def list() {
        Student.list()
    }

    def save(student){
        student.save()
    }

    def delete(id){
        Student.get(id).delete()
    }
}

Note that services don’t support transactions by default. Podemos habilitar esse recurso adicionando a anotação@Transactional à classe.

4.3. Camada de controlador

Para disponibilizar a lógica de negócios para a IU, vamos criar umStudentController invocando o seguinte comando:

grails create-controller com.example.grails.Student

Por padrão,Grails injects beans by names. Isso significa que podemos injetar facilmente a instância singletonStudentService em nosso controlador, declarando uma variável de instância chamadastudentsService.

Agora podemos definir ações para ler, criar e excluir alunos.

class StudentController {

    def studentService

    def index() {
        respond studentService.list()
    }

    def show(Long id) {
        respond studentService.get(id)
    }

    def create() {
        respond new Student(params)
    }

    def save(Student student) {
        studentService.save(student)
        redirect action:"index", method:"GET"
    }

    def delete(Long id) {
        studentService.delete(id)
        redirect action:"index", method:"GET"
    }
}

Por convenção,the index() action from this controller will be mapped to the URI*/student/index*,a açãoshow() para/student/showe assim por diante.

4.4. Ver Camada

Depois de configurar nossas ações do controlador, podemos agora criar as visualizações da interface do usuário. Criaremos três Groovy Server Pages para listar, criar e remover Alunos.

Por convenção, o Grails renderiza uma visão com base no nome e na ação do controlador. Por exemplo,the index()action from StudentController will resolve to /grails-app/views/student/index.gsp

Vamos começar implementando a visualização/grails-app/views/student/index.gsp, que exibirá uma lista de alunos. Usaremos a tag<f:table/> para criar uma tabela HTML exibindo todos os alunos retornados da açãoindex() em nosso controlador.

Por convenção, quando respondemos com uma lista de objetos,Grails will add the “List” suffix to the model name para que possamos acessar a lista de objetos de alunos com a variávelstudentList:



    
        
    
    
        
        

Vamos agora prosseguir para a visualização/grails-app/views/student/create.gsp,, que permite ao usuário criar novos alunos. Usaremos a tag<f:all/> integrada, que exibe um formulário para todas as propriedades de um determinado bean:



    
        
    
    
        

Por fim, vamos criar a visualização/grails-app/views/student/show.gsp para visualizar e, eventualmente, excluir alunos.

Entre outras tags, tiraremos vantagem de<f:display/>, que pega um bean como argumento e exibe todos os seus campos:



    
        
    
    
        
        

4.5. Testes unitários

Grails aproveita principalmenteSpock para fins de teste. Se você não está familiarizado com Spock, é altamente recomendável lerthis tutorial primeiro.

Vamos começar com o teste de unidade da açãoindex() do nossoStudentController.

Vamos simular o métodolist() deStudentServicee testar seindex() retorna o modelo esperado:

void "Test the index action returns the correct model"() {
    given:
    controller.studentService = Mock(StudentService) {
        list() >> [new Student(firstName: 'John',lastName: 'Doe')]
    }

    when:"The index action is executed"
    controller.index()

    then:"The model is correct"
    model.studentList.size() == 1
    model.studentList[0].firstName == 'John'
    model.studentList[0].lastName == 'Doe'
}

Agora, vamos testar a açãodelete(). Verificaremos sedelete() foi invocado deStudentService e verificaremos o redirecionamento para a página de índice:

void "Test the delete action with an instance"() {
    given:
    controller.studentService = Mock(StudentService) {
      1 * delete(2)
    }

    when:"The domain instance is passed to the delete action"
    request.contentType = FORM_CONTENT_TYPE
    request.method = 'DELETE'
    controller.delete(2)

    then:"The user is redirected to index"
    response.redirectedUrl == '/student/index'
}

4.6. Testes de integração

A seguir, vamos dar uma olhada em como criar testes de integração para a camada de serviço. Principalmente, vamos testar a integração com um banco de dados configurado emgrails-app/conf/application.yml.

By default, Grails uses the in-memory H2 database para este propósito.

Em primeiro lugar, vamos começar definindo um método auxiliar para criar dados para preencher o banco de dados:

private Long setupData() {
    new Student(firstName: 'John',lastName: 'Doe')
      .save(flush: true, failOnError: true)
    new Student(firstName: 'Max',lastName: 'Foo')
      .save(flush: true, failOnError: true)
    Student student = new Student(firstName: 'Alex',lastName: 'Bar')
      .save(flush: true, failOnError: true)
    student.id
}

Graças à anotação@Rollback em nossa classe de teste de integração,each method will run in a separate transaction, which will be rolled back at the end of the test.

Dê uma olhada em como implementamos o teste de integração para nosso métodolist():

void "test list"() {
    setupData()

    when:
    List studentList = studentService.list()

    then:
    studentList.size() == 3
    studentList[0].lastName == 'Doe'
    studentList[1].lastName == 'Foo'
    studentList[2].lastName == 'Bar'
}

Além disso, vamos testar o métododelete() e validar se a contagem total de alunos diminui em um:

void "test delete"() {
    Long id = setupData()

    expect:
    studentService.list().size() == 3

    when:
    studentService.delete(id)
    sessionFactory.currentSession.flush()

    then:
    studentService.list().size() == 2
}

5. Executando e implantando

A execução e a implantação de aplicativos podem ser feitas invocando um comando único via Grails CLI.

Para executar o aplicativo, use:

grails run-app

Por padrão, o Grails configurará o Tomcat na porta 8080.

Vamos navegar atéhttp://localhost:8080/student/index para ver a aparência de nosso aplicativo da web:

image

Se você deseja implantar seu aplicativo em um contêiner de servlet, use:

grails war

para criar um artefato de guerra pronto para implantar.

6. Conclusão

Neste artigo, focamos em como criar um aplicativo Web Grails usando a filosofia de convenção sobre configuração. Também vimos como executar testes de unidade e integração com a estrutura Spock.

Como sempre, todo o código usado aqui pode ser encontradoover on GitHub.