DEV Community

Meu Código Ágil
Meu Código Ágil

Posted on • Updated on

JHipster 8 - Analisando o código da nossa primeira aplicação monolítica - Parte 1/3

No último post, nós passamos por um tutorial que nos levou a criar a nossa primeira aplicação, uma aplicação monolítica muito simples com apenas uma entidade cujo propósito era apenas apresentar a vocês o JHipster.

Percorremos passo a passo a construção desta aplicação e, por fim, mostrei para vocês como executá-la e também os prints das telas. Se você não está entendendo nada desses dois primeiros parágrafos, certamente você não conferiu ainda o post onde mostro passo a passo como utilizar o JHipster para gerar uma aplicação com mínimo esforço. Confere lá e depois volte aqui que eu prometo que tudo vai fazer mais sentido: JHipster 8 — Criando uma aplicação monolítica.

Pois bem … naquele post nós criamos a aplicação utilizando JHipster CLI, colocamos ela pra rodar, visualizamos as telas, interagimos, mas … ficou faltando algo bem importante para nós DEVs: abrir o capô dessa aplicação e verificar o que afinal o JHipster produziu. Sim, faltou olhar o código fonte que foi produzido pra nós. É isso que vamos fazer aqui!

Números

Vamos começar por uma análise quantitativa do código fonte gerado e vamos considerar dois momentos distintos: o primeiro quando geramos a base da nossa aplicação, sem entidades. E o segundo quando criamos a entidade Produto na aplicação.

Aplicação monolítica base

Na criação da aplicação base (sem entidades), foram gerados 532 arquivos distribuídos em 158 diretórios e subdiretórios.

Dividindo-se por extensão dos arquivos, as maiores quantidades de arquivos gerados foram:

.ts: 217
.java: 109
.html: 50
.json: 45

E os diretórios com maior número de arquivos gerados, somando-se os arquivos em seus subdiretórios, são:

<diretório da aplicação>/src/main/webapp: 308
<diretório da aplicação>/src/main/java: 70
<diretório da aplicação>/src/test/java: 39
<diretório da aplicação>/: 27

Aplicação monolítica final

Agora o quantitativo refere-se à versão final da aplicação monolítica que criamos, ou seja, após a inclusão da entidade Produto. Agora, a aplicação totaliza 571 arquivos distribuídos em 169 diretórios e subdiretórios.

A divisão por extensão dos arquivos foi ajustada para:

.ts: 236
.java: 118
.html: 54
.json: 50

E os diretórios com maior número de arquivos gerados, somando-se os arquivos em seus subdiretórios, são:

<diretório da aplicação>/src/main/webapp: 334
<diretório da aplicação>/src/main/java: 75
<diretório da aplicação>/src/test/java: 43
<diretório da aplicação>/: 27

A primeira conclusão a qual podemos chegar é que o volume de código e configuração gerado é bastante significativo. Fazer tudo isso na mão levaria bem mais tempo do que aquele que gastamos respondendo aos prompts do JHipster :)

Estrutura e Organização

A fim de melhor nos localizarmos quando formos analisar o código, é importante começarmos pela estrutura e organização dos arquivos na estrutura de diretórios da aplicação.

Começando pelo diretório raiz da aplicação, veremos que ele possui uma quantidade grande arquivos. Em sua grande maioria, arquivos de configuração das próprias ferramentas de desenvolvimento. Por exemplo: pom.xml, .gitignore, .prettierrc, jest.config.js, cypress.config.ts, tsconfig.json, .eslintrc.json e por aí vai.

Em relação aos subdiretórios da raiz, entendo que, ao menos nesta nossa primeira análise, vale a pena focarmos apenas no diretório onde estão os fontes da nossa aplicação: o diretório ./src/no qual damos um “zoom in” abaixo.

├─ src
   ├── main
   │   ├── docker
   │   ├── java
   │   ├── resources
   │   │   ├── config
   │   │   ├── i18n
   │   │   └── templates
   │   └── webapp
   │       ├── WEB-INF
   │       ├── app
   │       ├── content
   │       ├── i18n
   │       └── swagger-ui
   └── test
       ├── java
       ├── javascript
       │   └── cypress
       └── resources
           ├── META-INF
           ├── config
           ├── i18n
           └── templates
Enter fullscreen mode Exit fullscreen mode

Podemos ver que o diretório src se divide em main e test:

main: abaixo deste diretório estão os arquivos fontes que se relacionam com a aplicação em si. Podemos perceber que ele se subdivide em:

  • docker: onde encontramos alguns arquivos YML a serem utilizados pelo Docker Compose para subir diferentes serviços relacionados à aplicação, por exemplo, uma instância do banco PostgreSQL.
  • java: onde está o código fonte Java da nossa aplicação em uma estrutura que inicia com o nome do pacote Java que demos na criação da aplicação: br/com/meucodigoagil/minhapp.
  • resources: que contém os arquivos de recursos tais como arquivos YAML de configuração da aplicação, os arquivos de mensagem de internacionalização (um pra cada idioma selecionado durante os prompts), configuração da biblioteca de logging Logback etc.
  • webapp: aqui está o código fonte do nosso frontend Angular com todos os seus arquivos Typescript, HTML, CSS, imagens, entre outros.

test: abaixo deste diretório está o código que não faz parte da aplicação em si mas que apoiará o processo de garantia e controle da qualidade. Assim como o diretório “main”, ele também está subdivido para melhor organização:

  • java: aqui se encontram os testes automatizados, unitários e de integração, do nosso backend.
  • javacript: aqui temos os nossos testes de frontend e2e utilizando o Cypress.
  • resources: assim como no diretório “main”, aqui temos os arquivos de YAML de configuração da aplicação, os arquivos de mensagem de internacionalização etc … mas que são aplicáveis apenas quando a aplicação está em execução durante os testes automatizados.

Arquivos de Configuração

Vamos começar a análise da nossa aplicação pelos seus principais arquivos de configuração:

pom.xml

Durante os prompts, escolhemos o Maven como a ferramenta de build do backend Java da nossa aplicação. Dentre outras características, uma aplicação Maven é organizada em estrutura padronizada de diretórios e conta com a presença de um ou mais arquivos de configuração pom.xml.

Entre outras coisas, este arquivo vai conter a lista de dependências da aplicação, plugins utilizados e suas configurações etc. Percebemos logo no início do arquivo a identificação da nossa aplicação.

<groupId>br.com.meucodigoagil.minhapp</groupId>
<artifactId>minha-aplicacao-monolitica</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Minha Aplicacao Monolitica</name>
<description>Description for Minha Aplicacao Monolitica</description>
Enter fullscreen mode Exit fullscreen mode

Na sequência, temos uma lista de propriedades com seus respectivos valores. Essa seção sozinha não alterada nada na aplicação, mas entenda essas propriedades como variáveis que serão utilizadas ao longo do pom.xml para configurar a aplicação como, por exemplo, na definição da versão de plugins, de dependências, de ferramentas de desenvolvimento e do próprio Java.

<properties>
    <maven.version>3.2.5</maven.version>
    <java.version>17</java.version>
    <node.version>v20.11.1</node.version>
    <npm.version>10.5.0</npm.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <start-class>br.com.meucodigoagil.minhapp.MinhaAplicacaoMonoliticaApp</start-class>
    <argLine>-Djava.security.egd=file:/dev/./urandom -Xmx1G</argLine>
Enter fullscreen mode Exit fullscreen mode

A seguir, temos a seção <dependencyManagement> que serve para gerenciar as dependências transitivas do projeto. Ela não adiciona diretamente as dependências ao projeto, mas define as versões que devem ser usadas por todos os módulos ou subprojetos e, assim, evita repetição de versões de dependências em vários lugares e ajuda a manter a consistência.

Na <dependencyManagement>da nossa aplicação, temos declarada a dependência tech.jhipster:jhipster-dependencies versão 8.2.0. Essa dependência é chave em uma aplicação gerada com o JHipster. É nela que o JHipster estabelece, por exemplo, que o pacote Spring Boot utilizado na aplicação será a versão 3.2.3.

<properties>
    <jhipster-dependencies.version>8.2.0</jhipster-dependencies.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>tech.jhipster</groupId>
            <artifactId>jhipster-dependencies</artifactId>
            <version>${jhipster-dependencies.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
Enter fullscreen mode Exit fullscreen mode

Como podemos ver na seção seguinte, <dependencies>, temos mais uma grande quantidade de dependências onde boa parte não tem especificada a versão pois isso já está estabelecido pela jhipster-dependencies.

<dependencies>
    <dependency>
        <groupId>tech.jhipster</groupId>
        <artifactId>jhipster-framework</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    ...
Enter fullscreen mode Exit fullscreen mode

Também temos a seção <build>, onde temos a definição do defaultGoaldo Maven como spring-boot:run, a declaração e configuração de diversos plugins do Maven, tais como, plugin de compilação, testes unitários e de integração, Sonar, Liquibase e muitos outros.

 <build>
    <defaultGoal>spring-boot:run</defaultGoal>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>${maven-surefire-plugin.version}</version>
          <configuration>
              <!-- Force alphabetical order to have a reproducible build -->
              <runOrder>alphabetical</runOrder>
              <excludes>
                  <exclude>**/*IT*</exclude>
                  <exclude>**/*IntTest*</exclude>
              </excludes>
              <systemPropertyVariables>
                  <java.util.logging.config.file>src/test/resources/logback.xml</java.util.logging.config.file>
              </systemPropertyVariables>
          </configuration>
        </plugin>
        ...
Enter fullscreen mode Exit fullscreen mode

package.json

Quem já desenvolveu um simple “Hello World” em Node.js já conhece este arquivo. Ele contém a lista de dependências do frontend da nossa aplicação, tanto em tempo de produção quando de desenvolvimento.

Logo no início do arquivo encontramos a identificação da nossa aplicação.

{
  "name": "minha-aplicacao-monolitica",
  "version": "0.0.1-SNAPSHOT",
  "private": true,
  "description": "Description for Minha Aplicacao Monolitica",
  "license": "UNLICENSED",
  ...
Enter fullscreen mode Exit fullscreen mode

Também encontraremos um conjunto de scripts que facilitam a vida do desenvolvedor. Para disparar os scripts, basta que o desenvolvedor execute o comando npm run <nome do script>.

"scripts": {
    "app:start": "./mvnw",
    "app:up": "docker compose -f src/main/docker/app.yml up --wait",
    "backend:build-cache": "./mvnw dependency:go-offline -ntp",
    "backend:debug": "./mvnw -Dspring-boot.run.jvmArguments=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000\"",
    "backend:doc:test": "./mvnw -ntp javadoc:javadoc --batch-mode",
    "backend:info": "./mvnw -ntp enforcer:display-info --batch-mode",
    "backend:nohttp:test": "./mvnw -ntp checkstyle:check --batch-mode",
    "backend:start": "./mvnw -Dskip.installnodenpm -Dskip.npm",
    "backend:unit:test": "./mvnw -ntp -Dskip.installnodenpm -Dskip.npm verify --batch-mode -Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.br.com.meucodigoagil.minhapp=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF",
    "build": "npm run webapp:prod --",
    ...
    "webapp:build": "npm run clean-www && npm run webapp:build:dev",
    "webapp:build:dev": "ng build --configuration development",
    "webapp:build:prod": "ng build --configuration production",
    "webapp:dev": "ng serve",
    "webapp:dev-ssl": "ng serve --ssl",
    "webapp:dev-verbose": "ng serve --verbose",
    "prewebapp:instrumenter": "npm run clean-www && npm run clean-coverage",
    "webapp:instrumenter": "ng build --configuration instrumenter",
    "webapp:prod": "npm run clean-www && npm run webapp:build:prod",
    "webapp:test": "npm run test --"
},
...
Enter fullscreen mode Exit fullscreen mode

Por fim, temos as dependências onde podemos notar claramente que nosso frontend se baseia no Angular 17.3.0.

"dependencies": {
    "@angular/common": "17.3.0",
    "@angular/compiler": "17.3.0",
    "@angular/core": "17.3.0",
    "@angular/forms": "17.3.0",
    "@angular/localize": "17.3.0",
    "@angular/platform-browser": "17.3.0",
    "@angular/platform-browser-dynamic": "17.3.0",
    "@angular/router": "17.3.0",
    "@fortawesome/angular-fontawesome": "0.14.1",
    "@fortawesome/fontawesome-svg-core": "6.5.1",
    "@fortawesome/free-solid-svg-icons": "6.5.1",
    "@ng-bootstrap/ng-bootstrap": "16.0.0",
    ...
Enter fullscreen mode Exit fullscreen mode

angular.json

Neste arquivo encontramos configurações do framework Angular, como a especificação do diretório raiz dos arquivos fonte Angular, o diretório onde estão armazenados assets (imagens, arquivos de estilo etc), configurações de build etc.

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "minha-aplicacao-monolitica": {
      "projectType": "application",
      "schematics": {...},
      "root": "",
      "sourceRoot": "src/main/webapp",
      "prefix": "jhi",
      "architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {...},
            "outputPath": "target/classes/static/",
            "index": "src/main/webapp/index.html",
            "main": "src/main/webapp/main.ts",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/main/webapp/content",
              "src/main/webapp/favicon.ico",
              "src/main/webapp/manifest.webapp",
              "src/main/webapp/robots.txt"
            ],
            ...
Enter fullscreen mode Exit fullscreen mode

Conclusão

Vamos encerrando aqui a primeira parte da nossa análise do código da aplicação monolítica gerada pelo JHipster. Fizemos uma análise quantitativa dos arquivos gerados, exploramos a estrutura de diretórios e organização dos arquivos da aplicação e, por fim, mergulhamos nos principais arquivos de configuração da aplicação localizados no diretório raiz.

Mas não acabou por aqui! Na próxima postagem, vamos nos aprofundar no código-fonte Java do backend da nossa aplicação. Fique ligado!

Enquanto isso, faça você mesmo um mergulho solo nos elementos que exploramos neste post e mande suas dúvidas nos comentários ;)

Top comments (0)