JHipster 8 - Analisando o código da nossa primeira aplicação monolítica - Parte 2/3
Tags: jhipster, java, spring, agile
Published: 2024-05-07 00:23:13

Dando continuidade à nossa análise do código-fonte da aplicação monolítica que criamos utilizando o JHipster, vamos agora explorar o código Java do nosso backend. É importante lembrar que o código do backend é baseado no framework [Spring Boot](https://spring.io/projects/spring-boot). No caso do JHipster 8, no Spring Boot 3.2.3 mais especificamente.

Conforme mencionado no [primeiro post](https://dev.to/meucodigoagil/jhipster-8-analisando-o-codigo-da-nossa-primeira-aplicacao-monolitica-parte-13-2380) desta sequência voltada para a análise do código do nosso monolito, o volume de código gerado é bastante extenso, o que torna inviável abordar todos os detalhes sem se tornar cansativo. Portanto, selecionei criteriosamente os pacotes e classes que julgo mais importantes neste estágio em que nos encontramos nesta jornada de apresentação do JHipster 8.

# Java

## MinhaAplicacaoMonoliticaApp.java

Conforme definido durante os prompts do JHipster CLI ao construirmos nossa aplicação, o pacote base das classes da aplicação é `br.com.meucodigoagil.minhapp`, assim, nosso código fonte Java estará localizado no diretório `src/main/java/br/com/meucodigoagil/minhapp` e é exatamente aí que estará a classe principal da nossa aplicação: `MinhaAplicacaoMonoliticaApp`. 

Como uma aplicação Spring Boot, é pela sua classe principal que a aplicação é inicializada. Esta classe deve ser anotada com `@SpringBootApplication`. 

```java
@SpringBootApplication
@EnableConfigurationProperties({ LiquibaseProperties.class, ApplicationProperties.class })
public class MinhaAplicacaoMonoliticaApp {
  ...
}
```

Como uma classe principal, podemos notar que ela também conta com um método estático `main` que cria uma instância de `SpringApplication` passando a própria classe como parâmetro, executa a instância criada e imprime no console aquela mensagem que vimos ao inicializar a nossa aplicação informando, entre outros, host e porta.

```java
public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MinhaAplicacaoMonoliticaApp.class);
    DefaultProfileUtil.addDefaultProfile(app);
    Environment env = app.run(args).getEnvironment();
    logApplicationStartup(env);
}
```

## br.com.meucodigoagil.minhapp.config.*

Dentro do pacote `br.com.meucodigoagil.minhapp.config`, você poderá conferir um grande número de classes destinadas a configurar a nossa aplicação.

A classe `CacheConfiguration` habilita cache na nossa aplicação e configura o Ehcache conforme selecionamos durante os prompts do JHipster CLI.

```java
@Configuration
@EnableCaching
public class CacheConfiguration {

    private GitProperties gitProperties;
    private BuildProperties buildProperties;
    private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;

    public CacheConfiguration(JHipsterProperties jHipsterProperties) {
        JHipsterProperties.Cache.Ehcache ehcache = jHipsterProperties.getCache().getEhcache();

        jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
            CacheConfigurationBuilder.newCacheConfigurationBuilder(
                Object.class,
                Object.class,
                ResourcePoolsBuilder.heap(ehcache.getMaxEntries())
            )
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds())))
                .build()
        );
    }
```

A classe `DatabaseConfiguration` possui um bean que configura servidor do banco de dados H2 (usado em tempo de desenvolvimento) para escutar em uma porta TCP.

Perceba no código a seguir que a anotação `@Profile` restringe a aplicação desta configuração apenas quando a aplicação está sendo executada sob o perfil de desenvolvimento.

```java
@Bean(initMethod = "start", destroyMethod = "stop")
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
public Object h2TCPServer() throws SQLException {
    String port = getValidPortForH2();
    log.debug("H2 database is available on port {}", port);
    return H2ConfigurationHelper.createServer(port);
}
```

A classe `SecurityConfiguration` faz uma série de configurações relacionadas à segurança da aplicação, como a liberação ou a restrição de certos endpoints. Enquanto a `SecurityJwtConfiguration` cria beans relacionados à autenticação por JWT, incluindo codificador e decodificador do token.

```java
@Configuration
public class SecurityJwtConfiguration {

    private final Logger log = LoggerFactory.getLogger(SecurityJwtConfiguration.class);

    @Value("${jhipster.security.authentication.jwt.base64-secret}")
    private String jwtKey;

    @Bean
    public JwtDecoder jwtDecoder(SecurityMetersService metersService) {
        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(getSecretKey()).macAlgorithm(JWT_ALGORITHM).build();
        return token -> {
            try {
                return jwtDecoder.decode(token);
            } catch (Exception e) {
                if (e.getMessage().contains("Invalid signature")) {
                    metersService.trackTokenInvalidSignature();
                } else if (e.getMessage().contains("Jwt expired at")) {
                    metersService.trackTokenExpired();
                } else if (
                    e.getMessage().contains("Invalid JWT serialization") ||
                    e.getMessage().contains("Malformed token") ||
                    e.getMessage().contains("Invalid unsecured/JWS/JWE")
                ) {
                    metersService.trackTokenMalformed();
                } else {
                    log.error("Unknown JWT error {}", e.getMessage());
                }
                throw e;
            }
        };
    }

    @Bean
    public JwtEncoder jwtEncoder() {
        return new NimbusJwtEncoder(new ImmutableSecret<>(getSecretKey()));
    }
    ...
```

## br.com.meucodigoagil.minhapp.domain.*

No pacote `br.com.meucodigoagil.minhapp.domain` encontraremos nossas classes de domínio, ou seja, as entidades da nossa aplicação anotadas com `@Entity` . Além das entidades que fazem parte do domínio da nossa aplicação, como a nossa `Produto`, encontraremos entidades relacionadas a funcionalidades de segurança criadas automaticamente pelo JHipster, como `User` e `Authority`.

Ao analisarmos a classe `Produto`, podemos notar que o atributo `nome` possui a anotação com as regras de tamanho que solicitamos ao JHipster CLI.


```java
@Entity
@Table(name = "produto")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@SuppressWarnings("common-java:DuplicatedBlocks")
public class Produto implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    @Column(name = "id")
    private Long id;

    @NotNull
    @Size(min = 3, max = 100)
    @Column(name = "nome", length = 100, nullable = false, unique = true)
    private String nome;

    ...
}
```

## br.com.meucodigoagil.minhapp.repository.*

Neste pacote temos as classes que farão o papel de repositório de dados, ou seja, que terão a responsabilidade de buscar os dados no banco de dados e que são anotadas com `@Repository`. Temos repositório para cada uma das classes de domínio. Perceba que se trata apenas de uma interface que, além de anotada, estende a interface `JpaRepository` do Spring, que proverá por baixo dos panos um conjunto de métodos básicos de acesso e manutenção de dados.

```java
@Repository
public interface ProdutoRepository extends JpaRepository<Produto, Long> {}
```

## br.com.meucodigoagil.minhapp.service.*

Neste pacote ficam as classes `@Service` que, no Spring, são responsáveis pela implementação de regras de negócio, bem como as classes DTO. Ao visualizar as classes deste pacote na nossa aplicação, você notará que se trata apenas do `UserService`, relacionada à entidade `User` padrão do JHipster e `MailService`, além de algumas classes Exception. Isso se deve ao fato de que durante o prompt do JHipster CLI nós escolhemos que as nossas classes `@RestController` chamariam as `@Repository` de forma direta. Lembra?

## br.com.meucodigoagil.minhapp.web.*

Neste pacote, teremos a implementação de classes do tipo Filter e Controllers do nosso MVC. Falando em controllers, eles estão mais especificamente no pacote `br.com.meucodigoagil.minhapp.web.rest`.

Encontramos o controller que expõe endpoints da nossa entidade `Produto` e também outros controllers com endpoints voltados para mecanismos de autenticação e autorização da aplicação.

Conforme citado anteriormente, podemos ver no código que a `ProdutoResource` tem injetada uma instância de `ProdutoRepository` em seu construtor e que este será invocado, por exemplo, no método `getAllProdutos()`.

```java
@RestController
@RequestMapping("/api/produtos")
@Transactional
public class ProdutoResource {

    private final Logger log = LoggerFactory.getLogger(ProdutoResource.class);

    private static final String ENTITY_NAME = "produto";

    @Value("${jhipster.clientApp.name}")
    private String applicationName;

    private final ProdutoRepository produtoRepository;

    public ProdutoResource(ProdutoRepository produtoRepository) {
        this.produtoRepository = produtoRepository;
    }

    /**
     * {@code GET  /produtos} : get all the produtos.
     *
     * @return the {@link ResponseEntity} with status {@code 200 (OK)}
     * and the list of produtos in body.
     */
    @GetMapping("")
    public List<Produto> getAllProdutos() {
        log.debug("REST request to get all Produtos");
        return produtoRepository.findAll();
    }
}
```

# Resources

Além dos arquivos Java, também fazem parte do backend os arquivos de configuração, que ficam sob o diretório `src/main/resources`. Passemos pelos principais pontos.

## config/application*.yml

Na raiz do diretório `src/main/resources/config`, temos um conjunto de arquivos YML para configuração da nossa aplicação. O arquivo `application.yml`, detêm as configurações aplicáveis a qualquer que seja o profile sob o qual a aplicação está em execução, seja desenvolvimento ou produção.

Já os arquivos com algum sufixo, são aplicados de acordo com o profile sob o qual a aplicação está em execução. Assim, o arquivo `application-dev.yml` armazenará valores de configuração especificamente para quando a aplicação está executando sob o perfil `dev`. Note que sob a chave `spring.datasource` encontramos uma configuração do banco de dados H2:


```yaml
spring:
  ...
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:h2:file:./target/h2db/db/minhaAplicacaoMonolitica;DB_CLOSE_DELAY=-1
    username: minhaAplicacaoMonolitica
    password:
    hikari:
      poolName: Hikari
      auto-commit: false
  h2:
    console:
      # disable spring boot built-in h2-console since we start it manually with correct configuration
      enabled: false
```

Por outro lado, o `application-prod.yml` será utilizado quando a aplicação estiver sob o profile `prod`. Se verificarmos a mesma chave `spring.datasource`, encontraremos a configuração para uma base PostgreSQL conforme nós escolhemos em um dos prompts do JHipster CLI:

```yaml
spring:
  ...
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:postgresql://localhost:5432/minhaAplicacaoMonolitica
    username: minhaAplicacaoMonolitica
    password:
    hikari:
      poolName: Hikari
      auto-commit: false
```

## config/liquibase/

Sob o diretório `src/resources/config/liquibase`, temos os changelogs do [Liquibase](https://www.liquibase.com), a ferramenta utilizada pelas aplicações geradas pelo JHipster para o gerenciamento de mudanças no esquema do banco de dados.

No subdiretório `changelog`, temos os arquivos com os conjunto de instruções para criação e modificação do esquema de banco de dados conforme podemos ver a seguir a criação da tabela `Produto`:

```xml
<changeSet id="20240324155937-1" author="jhipster">
    <createTable tableName="produto">
        <column name="id" type="bigint">
            <constraints primaryKey="true" nullable="false"/>
        </column>
        <column name="nome" type="varchar(100)">
            <constraints nullable="false" unique="true" uniqueConstraintName="ux_produto__nome" />
        </column>
        <column name="tipo" type="varchar(255)">
            <constraints nullable="false" />
        </column>
        <!-- jhipster-needle-liquibase-add-column - JHipster will add columns here -->
    </createTable>
</changeSet>
```

Já na raiz, temos no arquivo `master.xml`, além de declarar algumas propriedades que podem ser utilizadas dentro dos changelogs, também faz referência aos próprios changelogs que serão executados:

```xml
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
    <property name="now" value="now()" dbms="h2"/>
    ...

    <include file="config/liquibase/changelog/00000000000000_initial_schema.xml" relativeToChangelogFile="false"/>
    <include file="config/liquibase/changelog/20240324155937_added_entity_Produto.xml" relativeToChangelogFile="false"/>
</databaseChangeLog>
```

## config/i18n/

Sob este diretório encontramos arquivos de propriedades com mensagens nos idiomas que selecionamos para a aplicação. A seguir, mostramos parcialmente os arquivos com mensagens em português e em inglês:


```
 # Reset email
email.reset.title=A Senha da aplicação minhaAplicacaoMonolitica foi redifinida
email.reset.greeting=Caro {0}
email.reset.text1=Foi solicitado a redefinição de senha da sua conta minhaAplicacaoMonolitica. Por favor clique na url abaixo para alterá-la:
email.reset.text2=Atenciosamente,
```

```
 # Reset email
# Reset email
email.reset.title=minhaAplicacaoMonolitica password reset
email.reset.greeting=Dear {0}
email.reset.text1=For your minhaAplicacaoMonolitica account a password reset was requested, please click on the URL below to reset it:
email.reset.text2=Regards,
```

Os textos contidos nas chaves desses arquivos serão lidos com auxílio do Spring Boot para retornar textos de acordo com o idioma do usuário final. A seguir um exemplo de como isso poderia ser realizado considerando que os arquivos de mensagens tivessem uma mensagem sob a chave `error.message`:

```java
public class ApenasExemplo{

  private final MessageSource messageSource;

  public ApenasExemplo(MessageSource messageSource){
    this.messageSource = messageSource;
  }

  public void facaAlgo(User user){
    Locale locale = Locale.forLanguageTag(user.getLangKey());
    throw new RuntimeException(
      messageSource.getMessage("error.message", null, locale)
    );
  }
}
```

# Conclusão

Finalizamos aqui a análise do código backend gerado pelo JHipster. Passamos pelas principais classes Java da aplicação, classes do domínio, classes de configuração, classes de acesso a dados, controllers, e também pelos arquivos de configuração do nosso backend.

Na próxima publicação, vamos nos aprofundar no código-fonte do nosso frontend Angular. Até lá, explore mais o código do nosso backend e mande suas dúvidas nos comentários ;)