
Всего в цикле публикаций о миграции из Grails в Micronaut будет 10 частей:
- Многомодульный проект 
- Конфигурация 
- Статическая компиляция 
- Датасеты 
- Маршалинг 
- Классы предметной области 
- Сервисы 
- Контроллеры 
- Приложение Micronaut 
- 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 функции, и другие.
>> РЕГИСТРАЦИЯ
 
          