Всего в цикле публикаций о миграции из Grails в Micronaut будет 10 частей:

  1. Многомодульный проект

  2. Конфигурация

  3. Статическая компиляция

  4. Датасеты

  5. Маршалинг

  6. Классы предметной области

  7. Сервисы

  8. Контроллеры

  9. Приложение Micronaut

  10. Micronaut Data

Обратите внимание: ваше приложение должно быть создано в Grails 4.x или более поздней версии.

Часть 1. Многомодульный проект

Мы начинаем цикл статей с инструкциями, которые помогут вам шаг за шагом мигрировать из Grails. Итак, для начала нужно извлечь компоненты вашего приложения в отдельные библиотеки. Это лучше всего сделать путем создания многомодульного проекта (в документации Gradle — multi-project). Чтобы создать структуру, которую будет легко масштабировать, мы воспользуемся набором плагинов Kordamp Gradle.

Сперва перенесем все, что имеет отношение к вашему приложению, в отдельную папку apps/<имя-вашего-приложения>. Далее во всех инструкциях этого цикла мы будем писать hello там, где должно быть имя вашего приложения. Таким образом, у нас получится новый путь apps/hello. Файлы Gradle перемещать не нужно, за исключением файла build.gradle (ему следует присвоить имя, совпадающее с именем папки, в которую он перемещен; в нашем примере — hello.gradle). Файлы Grails Wrapper можно удалить.

├── .gitignore
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── grails-app
├── grails-wrapper.jar
├── grailsw
├── grailsw.bat
└── src

Вот так будет выглядеть структура папок в проекте после перемещения всех файлов:

├── .gitignore
├── apps
│   └── hello
│       ├── grails-app
│       ├── hello.gradle
│       └── src
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
└── gradlew.bat

Далее создаем файл setting.gradle, с помощью которого будут установлены плагины Kordamp Gradle:

buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'org.kordamp.gradle:settings-gradle-plugin:0.46.0'
    }
}
apply plugin: 'org.kordamp.gradle.settings'

rootProject.name = 'hello-root'

projects {
    directories = ['apps', 'libs']
}

В переменной rootProject.name следует объявить название проекта, добавив -root, чтобы избежать конфликта имен. Благодаря плагинам Kordamp Gradle все подпапки внутри папок app и libs автоматически определятся как подпроекты.

Теперь создаем новый файл build.gradle в корневой папке. Меняем значения на свои:

plugins {
    id 'org.kordamp.gradle.groovy-project' version '0.46.0'
}

config {
    release = (rootProject.findProperty('release') ?: false).toBoolean()

    info {
        name        = 'Sample'
        vendor      = 'Acme'
        description = 'Sample project'

        links {
            website      = 'https://github.com/me/repo'
            issueTracker = 'https://github.com/me/repo/issues'
            scm          = 'https://github.com/me/repo.git'
        }

        people {
            person {
                id    = 'joecool'
                name  = 'Joe Cool'
                roles = ['developer']
            }
        }
    }

    licensing {
        enabled = false
    }
}

allprojects {
    repositories {
        mavenCentral()
    }
}

Наконец необходимо явно объявить класс приложения (в нашем примере — hello.Application) в подпроекте apps/hello/hello.gradle, иначе произойдет конфликт конфигурации плагинов Gradle и приложение не запустится:

springBoot {
    mainClassName = 'hello.Application'
}

Теперь пробуем запустить приложение с помощью команды ниже, чтобы проверить, все ли в порядке:

./gradlew bootRun

Следующий шаг — перенос конфигурации из Grails в объекты конфигурации Micronaut.


Часть 2. Конфигурация

Продолжаем плавно мигрировать из Grails: теперь разберемся с доступом к конфигурации.

В Grails можно получить доступ к файлам конфигурации из объекта GrailsApplication или с помощью статических методов класса Holders.

@CompileDynamic 
class MyService {

    GrailsApplication grailsApplication
    
    String returnFoo() {
        // normal access
        return grailsApplication.config.agorapulse.foo
    }
    
    String returnBar() {
        // static access
        return Holders.config.agorapulse.bar
    }

}

Grails 4.x и старше уже поддерживает Micronaut, так что можно пользоваться возможностями последнего, в том числе взаимодействовать с объектами свойств конфигурации. Можно, например, создать простой объект для привязки значений конфигурации:

@CompileStatic 
@ConfigurationProperties('agorapulse')
class AgorapulseConfiguration {
    
    String foo
    String bar

}

Этот объект можно интегрировать в любой сервис Grail. К полю нужно добавить аннотацию @Inject, так как автоматическая привязка для бинов Micronaut уже не работает.

@CompileStatic
class MyService {

    @Inject AgorapulseConfiguration configuration

    String returnFoo() {
        return configuration.foo
    }

    String returnBar() {
        return configuration.bar
    }

}

Не лишним также будет добавить дополнительную проверку класса конфигурации. Теперь ваш сервис полностью готов к внедрению статической компиляции.

В следующей части мы расскажем, как реализовать статическую компиляцию для всего проекта Grails.


Часть 3. Статическая компиляция

Переход в другую экосистему — всегда непростая задача, даже если это родственный фреймворк. Чтобы миграция прошла успешно, стоит внедрить статическую компиляцию в качестве дополнительного уровня безопасности в масштабе приложения. Это также поможет на следующих этапах миграции при рефакторинге моделей предметной области. Процедура внедрения статической компиляции подробно описана в статье по ссылке ниже: рекомендуем сначала прочитать ее.

Мы хотим внедрить статическую компиляцию во все приложения и библиотеки проекта, поэтому обновляем файл settings.gradle следующим образом:

projects {
    directories = ['apps', 'libs']

    plugins {
        dirs(['apps',  'libs']) {
            id 'groovy'
            id 'java-library'
        }
    }
}

Эти строки гарантируют, что во всех подпроектах apps и libs будут работать плагины groovy и java-library.

Теперь для внедрения статической компиляции в масштабе приложения нужно настроить зависимость Enterprise Groovy. Дописываем этот код в файл build.gradle:

projects {
    subprojects {
        dirs(['apps', 'libs']) {
            // add Enterprise Groovy library
            dependencies {
                compileOnly 'com.virtualdogbert:enterprise-groovy:1.0.3'
            }
            
            // configure Enterprise Groovy library
            compileGroovy.groovyOptions.configurationScript = 
                rootProject.file('config/groovy/conventions.groovy')
        }  
    }
}

Создаем файл конфигурации, упомянутый выше, в папке config/groovy/conventions.groovy:

Map conventions = [
    disable                     : false,
    whiteListScripts            : true,
    disableDynamicCompile       : false,  
    dynamicCompileWhiteList     : [
                'UrlMappings',
                'Application',
                'BootStrap',
                'resources', 
                'org.grails.cli'
    ],
    limitCompileStaticExtensions: false,
    defAllowed                  : false,    // For controllers you can use Object in place of def, and in Domains add Closure to constraints/mappings closure fields.
    skipDefaultPackage          : true,     // For GSP files
    compileStaticExtensions     : [
      'org.grails.compiler.ValidateableTypeCheckingExtension',
      'org.grails.compiler.NamedQueryTypeCheckingExtension',
      'org.grails.compiler.HttpServletRequestTypeCheckingExtension',
      'org.grails.compiler.WhereQueryTypeCheckingExtension',
      'org.grails.compiler.DynamicFinderTypeCheckingExtension',
      'org.grails.compiler.DomainMappingTypeCheckingExtension',
      'org.grails.compiler.RelationshipManagementMethodTypeCheckingExtension'
    ],
]
System.setProperty(
    'enterprise.groovy.conventions', 
    "conventions=${conventions.inspect()}"
)

Простые действия закончились, переходим к более сложным манипуляциям.

Даже если вы все делали верно на предыдущих шагах, вы можете столкнуться с ошибками компиляции при запуске команды ./gradlew classes в терминале. У Enterprise Groovy есть один недостаток: результаты статической компиляции не отображаются в явном виде в IDE. Поэтому рекомендуем добавлять аннотации @CompileStatic или @GrailsCompileStatic к классам, которые не прошли компиляцию: так вы увидите все ошибки прямо в вашей среде разработки. Если какие-либо части кода не компилируются, добавьте к ним аннотацию @CompileDynamic. Учтите, что динамическую компиляцию следует использовать в минимальном масштабе. Бонус: в результате код вашего приложения будет более устойчивым к ошибкам и будет меньше риска при его рефакторинге.

В следующей части мы будем готовить датасеты для классов предметной области. Они послужат основой для тестирования, которое мы будем проводить далее, чтобы гарантировать успешную миграцию.

Источники и обсуждение:

Оригинальные публикации: часть 1, часть 2, часть 3.

Материал подготовлен в рамках курса «Groovy Developer».

Всех желающих приглашаем на бесплатное demo-занятие «Знакомство с Micronaut». Micronaut представляет собой легковесный фреймворк на основе JVM для создания приложений на основе микросервисов. На занятии мы рассмотрим возможности фреймворка, такие как compile time DI, создание native image, serverless функции, и другие.
>> РЕГИСТРАЦИЯ