Aplicativo Maven de múltiplos módulos com módulos Java

Aplicativo Maven de múltiplos módulos com módulos Java

1. Visão geral

OJava Platform Module System (JPMS) adiciona mais confiabilidade, melhor separação de interesses e encapsulamento mais forte para aplicativos Java. No entanto, não é uma ferramenta de construção, portanto,it lacks the ability for automatically managing project dependencies.

Claro, podemos nos perguntar se podemosuse well-established build tools, like Maven or Gradle, em aplicações modularizadas.

Na verdade, nós podemos! Neste tutorial,we’ll learn how to create a multi-module Maven application using Java modules.

2. Encapsulando módulos Maven em módulos Java

Comomodularity and dependency management are not mutually exclusive concepts in Java,, podemos integrar perfeitamente oJPMS, por exemplo, com o Maven, aproveitando assim o melhor dos dois mundos.

Em um projeto Maven multimódulo padrão, adicionamos um ou mais módulos Maven filho, colocando-os na pasta raiz do projeto e declarando-os no POM pai, na seção<modules>.

Por sua vez, editamos o POM de cada módulo filho e especificamos suas dependências por meio das coordenadas <groupId>, <artifactId>e <version> padrão.

O mecanismoreactor no Maven - responsável por lidar com projetos multimódulos - cuida de construir todo o projeto na ordem certa.

Nesse caso, usaremos basicamente a mesma metodologia de design, mas com uma variante sutil, porém fundamental:we’ll wrap each Maven module into a Java module by adding to it the module descriptor file,module-info.java.

3. O Módulo Maven Pai

Para demonstrar como a modularidade e o gerenciamento de dependência funcionam bem juntos, construiremos um projeto Maven multimódulo de demonstração básico,whose functionality will be narrowed to just fetching some domain objects from a persistence layer.

Para manter o código simples, usaremos umMap simples como a estrutura de dados subjacente para armazenar os objetos de domínio. Obviamente, podemos facilmente mudar ainda mais adiante para um banco de dados relacional completo.

Vamos começar definindo o módulo pai Maven. Para fazer isso, vamos criar um diretório raiz do projeto chamado, por exemplo,multimodulemavenproject (mas poderia ser qualquer outra coisa), e adicionar a ele o arquivopom.xml pai:

com.example.multimodulemavenproject
multimodulemavenproject
1.0
pom
multimodulemavenproject


    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.0
                
                    11
                    11
                
            
        
    



    UTF-8

Há alguns detalhes que devem ser observados na definição do POM pai.

Em primeiro lugar, como estamos usando Java 11,we’ll need at least Maven 3.5.0 on our system, as Maven supports Java 9 and higher from that version onward.

E, também precisaremos de pelo menos a versão 3.8.0 doMaven compiler plugin. Portanto, vamos verificar olatest version of the plugin on Maven Central.

4. Os Módulos Filhos Maven

Observe que até este ponto,the parent POM doesn’t declare any child modules.

Como nosso projeto de demonstração buscará alguns objetos de domínio da camada de persistência, criaremos quatro módulos filhos do Maven:

  1. entitymodule: conterá uma classe de domínio simples

  2. daomodule: manterá a interface necessária para acessar a camada de persistência (um contratoDAO básico)

  3. userdaomodule: incluirá uma implementação da interface dedaomodule

  4. mainappmodule: o ponto de entrada do projeto

4.1. Módulo Maven entitymodule

Agora, vamos adicionar o primeiro módulo filho do Maven, que inclui apenas uma classe de domínio básica.

No diretório raiz do projeto, vamos criar a estrutura de diretórioentitymodule/src/main/java/com/example/entity e adicionar uma classeUser:

public class User {

    private final String name;

    // standard constructor / getter / toString

}

A seguir, vamos incluir o arquivopom.xml do módulo:


    com.example.multimodulemavenproject
    multimodulemavenproject
    1.0


com.example.entitymodule
entitymodule
1.0
jar
entitymodule

Como podemos ver, o móduloEntity não tem nenhuma dependência com outros módulos, nem requer artefatos Maven adicionais, pois inclui apenas a classeUser.

Agora, precisamosencapsulate the Maven module into a Java module. Para conseguir isso, vamos simplesmente colocar o seguinte arquivo descritor de módulo (module-info.java) no diretórioentitymodule/src/main/java:

module com.example.entitymodule {
    exports com.example.entitymodule;
}

Por fim, vamos adicionar o módulo filho do Maven ao POM pai:


    entitymodule

4.2. Módulo Mavendaomodule

Vamos criar um novo módulo Maven que conterá uma interface simples. Isso é conveniente para definir um contrato abstrato para buscar tipos genéricos da camada de persistência.

Na verdade, há um motivo muito convincente para colocar essa interface em um módulo Java separado. By doing so, we have an abstract, highly-decoupled contract, which is easy to reuse in different contexts. No núcleo, esta é uma implementação alternativa deDependency Inversion Principle, que produz um design mais flexível.

Portanto, vamos criar a estrutura de diretóriodaomodule/src/main/java/com/example/dao no diretório raiz do projeto e adicionar a interfaceDao<T>:

public interface Dao {

    Optional findById(int id);

    List findAll();

}

Agora, vamos definir o arquivopom.xml do módulo:


    // parent coordinates


com.example.daomodule
daomodule
1.0
jar
daomodule

O novo módulo não requer outros módulos ou artefatos, então vamos apenas embrulhá-lo em um módulo Java. Vamos criar o descritor do módulo no diretóriodaomodule/src/main/java:

module com.example.daomodule {
    exports com.example.daomodule;
}

Finalmente, vamos adicionar o módulo ao POM pai:


    entitymodule
    daomodule

4.3. Módulo Mavenuserdaomodule

A seguir, vamos definir o módulo Maven que contém uma implementação da interfaceDao.

No diretório raiz do projeto, vamos criar a estrutura de diretóriouserdaomodule/src/main/java/com/example/userdao e adicionar a seguinte classeUserDao:

public class UserDao implements Dao {

    private final Map users;

    // standard constructor

    @Override
    public Optional findById(int id) {
        return Optional.ofNullable(users.get(id));
    }

    @Override
    public List findAll() {
        return new ArrayList<>(users.values());
    }
}

Simplificando, a classeUserDao fornece uma API básica que nos permite buscar objetosUser da camada de persistência.

Para manter as coisas simples, usamos aMap como a estrutura de dados de apoio para persistir os objetos de domínio. Claro, é possível fornecer uma implementação mais completa que usa, por exemplo,Hibernate’s entity manager.

Agora, vamos definir o POM do módulo Maven:


    // parent coordinates


com.example.userdaomodule
userdaomodule
1.0
jar
userdaomodule


    
        com.example.entitymodule
        entitymodule
        1.0
    
    
        com.example.daomodule
        daomodule
        1.0
    

Nesse caso, as coisas são um pouco diferentes, pois o módulouserdaomodule requer os módulos entitymoduleedaomodule. É por isso que os adicionamos como dependências no arquivopom.xml.

Ainda precisamos encapsular esse módulo Maven em um módulo Java. Então, vamos adicionar o seguinte descritor de módulo no diretóriouserdaomodule/src/main/java:

module com.example.userdaomodule {
    requires com.example.entitymodule;
    requires com.example.daomodule;
    provides com.example.daomodule.Dao with com.example.userdaomodule.UserDao;
    exports com.example.userdaomodule;
}

Por fim, precisamos adicionar este novo módulo ao POM pai:


    entitymodule
    daomodule
    userdaomodule

Em uma visualização de alto nível, é fácil ver quethe pom.xml file and the module descriptor play different roles. Mesmo assim, eles se complementam muito bem.

Digamos que precisamos atualizar as versões dos artefatos entitymoduleedaomodule Maven. Podemos fazer isso facilmente sem precisar alterar as dependências no descritor do módulo. Maven cuidará de incluir os artefatos certos para nós.

Da mesma forma, podemos alterar a implementação do serviço que o módulo fornece modificando a diretiva“provides..with” no descritor do módulo.

Ganhamos muito quando usamos os módulos Maven e Java juntos. The former brings the functionality of automatic, centralized dependency management, while the latter provides the intrinsic benefits of modularity.

4.4. Módulo Mavenmainappmodule

Além disso, precisamos definir o módulo Maven que contém a classe principal do projeto.

Como fizemos antes, vamos criar a estrutura de diretóriomainappmodule/src/main/java/mainapp no diretório raiz e adicionar a ela a seguinte classeApplication:

public class Application {

    public static void main(String[] args) {
        Map users = new HashMap<>();
        users.put(1, new User("Julie"));
        users.put(2, new User("David"));
        Dao userDao = new UserDao(users);
        userDao.findAll().forEach(System.out::println);
    }
}

O métodomain() da classeApplication é bastante simples. Primeiro, ele preenche umHashMap com alguns objetosUser. Em seguida, ele usa uma instânciaUserDao para buscá-los emMap,e os exibe no console.

Além disso, também precisamos definir o arquivopom.xml do módulo:


    // parent coordinates


com.example.mainappmodule
mainappmodule
1.0
jar
mainappmodule


    
        com.example.entitymodule
         entitymodule
         1.0
    
    
        com.example.daomodule
        daomodule
        1.0
    
    
        com.example.userdaomodule
        userdaomodule
        1.0
    

As dependências do módulo são autoexplicativas. Então, só precisamos colocar o módulo dentro de um módulo Java. Portanto, na estrutura de diretóriomainappmodule/src/main/java, vamos incluir o descritor do módulo:

module com.example.mainappmodule {
    requires com.example.entitypmodule;
    requires com.example.userdaopmodule;
    requires com.example.daopmodule;
    uses com.example.daopmodule.Dao;
}

Finalmente, vamos adicionar este módulo ao POM pai:


    entitymodule
    daomodule
    userdaomodule
    mainappmodule

Com todos os módulos filhos Maven já instalados e perfeitamente encapsulados em módulos Java, é assim que a estrutura do projeto se parece:

multimodulemavenproject (the root directory)
pom.xml
|-- entitymodule
    |-- src
        |-- main
            | -- java
            module-info.java
            |-- com
                |-- example
                    |-- entity
                    User.class
    pom.xml
|-- daomodule
    |-- src
        |-- main
            | -- java
            module-info.java
            |-- com
                |-- example
                    |-- dao
                    Dao.class
    pom.xml
|-- userdaomodule
    |-- src
        |-- main
            | -- java
            module-info.java
            |-- com
                |-- example
                    |-- userdao
                    UserDao.class
    pom.xml
|-- mainappmodule
    |-- src
        |-- main
            | -- java
            module-info.java
            |-- com
                |-- example
                    |-- mainapp
                    Application.class
    pom.xml

5. Executando o aplicativo

Por fim, vamos executar o aplicativo, de dentro de nosso IDE ou de um console.

Como podemos esperar, devemos ver alguns objetosUser impressos no console quando o aplicativo é inicializado:

User{name=Julie}
User{name=David}

6. Conclusão

Neste tutorial, aprendemos de forma pragmáticahow to put Maven and the JPMS to work side-by-side, in the development of a basic multi-module Maven project that uses Java modules.

Como de costume, todos os exemplos de código mostrados neste tutorial estão disponíveisover on GitHub.