JHipster 8 - Analisando o código da nossa primeira aplicação monolítica - Parte 3/3
Meu Código Ágil
Posted on May 22, 2024
Chegamos finalmente à parte final da análise do código da nossa aplicação, vamos encarar agora o código frontend gerado pelo JHipster que, como escolhido durante os prompts apresentados, está baseado no framework Angular.
Vimos ao longo desta análise que o código do frontend está sob o diretório src/main/webapp
. Assim como no backend, o JHipster também gera um volume grande de código. Assim, vou manter o foco nos pontos mais importantes.
Vale relembrar que os arquivos de configuração do nosso frontend, tais como package.json
, tsconfig.json
, angular.json
, jest.conf.js
etc, são armazenados na raiz da nossa aplicação e já foram abordados no primeiro post desta série de análise de código.
webapp/i18n/
Sob este diretório encontramos um subdiretório para cada um dos idiomas suportados pela nossa aplicação, os quais selecionamos durante os prompts apresentados pelo JHipster:
└── webapp
├── i18n
│ ├── en
│ └── pt-br
Dentro de cada um dos subdiretórios, encontramos 18 arquivos JSON contendo textos no respectivo idioma a serem exibidos nas diversas páginas da UI da aplicação de acordo com o idioma selecionado pelo usuário. Abaixo um exemplo do arquivo pt-br/error.json
:
{
"error": {
"title": "Página de erro!",
"http": {
"400": "Requisição inválida.",
"403": "Você não tem autorização para acessar esta página.",
"404": "A página não existe.",
"405": "O verbo HTTP utilizado não é suportado para essa URL.",
"500": "Erro interno do servidor."
},
"concurrencyFailure": "Outro usuário modificou esses dados ao mesmo tempo que você. Suas modificações foram rejeitadas.",
"validation": "Erro de validação no servidor."
}
}
Portanto, quando precisar fazer um ajuste nos textos exibidos na interface com o usuário, você já sabe onde alterar.
webapp/content/
Aqui o JHipster incluiu os assets do frontend: arquivos de imagem e arquivos de estilo CSS ou SCSS. Quando for trocar a logo da aplicação, por exemplo, é aqui que você vai guardar o arquivo de imagem. Quando for mudar cores, tamanho, fontes etc ... vai ser aqui também.
└── webapp
├── content
│ ├── css
│ ├── images
│ └── scss
webapp/app/
No subdiretório app
encontraremos a grande maioria dos arquivos fonte do frontend. Aqui está o código de todos os componentes da interface com o usuário, das telas compostas por esses componentes, rotas, serviços, interceptadores etc.
☢️ EITA! PARA TUDO!!!⚠️
Caso você seja um desenvolvedor backend raiz, tenho certeza que quase infartou no último parágrafo. Na verdade, já está sentido calafrios desde que resolveu parar pra ler este post, correto?Já que você me deu essa moral de chegar até aqui, eu escrevi um outro post Off Topic com uma nano introdução sobre alguns conceitos básicos de Angular.
Acredito que vai ajudar pra que esse post não fique muito abstrato e uma leitura excessivamente chata pra você. Acessa aí, veja se ajuda, dê seu feedback e depois volte aqui pra continuar nossa análise:
[Off Topic] Nano introdução do framework Angular para Devs do back
Mas se você já está familiarizado com o Angular e quiser dar umas dicas de como a gente pode deixar esse post mais didático ou mais preciso, dá um confere lá também e mande seus comentários ;)
Começando pela raiz do subdiretório app
, alguns elementos aqui merecem atenção:
app-routes.ts
: como o nome já sugere, aqui estão declaradas as rotas principais da nossa aplicação.
const routes: Routes = [
{
path: '',
component: HomeComponent,
title: 'home.title',
},
...
{
path: 'login',
component: LoginComponent,
title: 'login.title',
},
...
{
path: 'admin',
data: {
authorities: [Authority.ADMIN],
},
canActivate: [UserRouteAccessService],
loadChildren: () => import('./admin/admin.routes'),
},
{
path: 'account',
loadChildren: () => import('./account/account.route'),
},
...
app.config.ts
: entre outras coisas, neste arquivo está o provimento das rotas declaradas acima:
import routes from './app.routes';
...
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, ...routerFeatures),
...
webapp/app/account/
Adentrando na estrutura em ordem alfabética, temos este diretório que reúne todos os componentes e serviços relacionados às contas de usuários da nossa aplicação. Como podemos observar, o JHipster ainda subdividiu os componentes e serviços em grupos relacionados ao registro, a ativação de usuário, reinicialização de senha etc.
├── account
│ ├── activate
│ ├── password
│ │ └── password-strength-bar
│ ├── password-reset
│ │ ├── finish
│ │ └── init
│ ├── register
│ └── settings
webapp/app/admin/
Neste diretório, encontramos os componentes e serviços relacionados à administração da aplicação. Uma das principais e sempre é gerada pelo JHipster é a de administração de usuários da aplicação, user-management
. Através do serviço e do conjunto de componentes ali implementados, um administrador poderá visualizar os dados dos usuários registrados e dar manutenção. Um outro grupo de funcionalidades, relacionados a métricas, logs e informações sobre o estado da aplicação, só está disponível porque, durante os prompts do JHipster, nós optamos pela geração da UI de administração.
├── admin
│ ├── configuration
│ ├── docs
│ ├── health
│ ├── logs
│ ├── metrics
│ └── user-management
webapp/app/config/
Aqui fica uma série de configurações default da aplicação. Por exemplo:
authority.constants.ts
: declara um enumerador Typescript com as roles ADMIN
e USER
utilizadas ao longo da aplicação para condicionar seu funcionamento. Suponhamos que e a lógica da nossa aplicação demandasse um outro papel com acessos intermediários entre o ADMIN
e o USER
, poderíamos criar aqui um novo elemento MANAGER
no enumerador e referenciá-lo ao longo da aplicação onde fosse aplicável.
font-awesome-icons.ts
: temos o import de alguns ícones específicos da dependência @fortaawesome/free-solid-svg-icons
que são utilizados ao longo da nossa UI.
pagination.constants.ts
: define, entre outros, o número de registros padrão em cada página para as entidades que possuem busca paginada. Caso queiramos modificar essa quantidade de registros para todas as entidades paginadas, simplesmente podemos editar aqui essa constante.
webapp/app/core/
Aqui encontramos uma série de serviços e classes com funcionalidades auxiliares que serão utilizadas de forma transversal ao longo da aplicação segmentados em subdiretórios:
auth
: implementa serviços relacionados a autenticação com JWT, manutenção de estado na sessionStorage
e lógica de autorização baseda em papéis utilizada na declaração das rotas (CanActivate).
config
: possui um serviço de configuração da aplicação.
interceptors
: contém um conjunto de interceptadores de requisições HTTP utilizados, por exemplo, para adicionar o HTTP HEADER Authorization
com o token JWT às requisições aos endpoints do backend, identificar requisições com JWT expirado e redirecionar o usuário para a tela de login, tratar outros erros HTTP não relacionados a autenticação etc.
webapp/app/entities/
Neste diretório encontramos a lógica associada às entidades suportadas pela aplicação. Tanto as entidades relacionadas ao contexto de segurança, user
e authority
quanto as entidades do domínio da nossa aplicação que, até agora, temos tão somente a produto.
├── entities
│ ├── produto
│ │ ├── delete
│ │ ├── detail
│ │ ├── list
│ │ ├── route
│ │ ├── service
│ │ └── update
Podemos ver já pela estrutura que a entidade tem suas funcionalidades segmentadas em subdiretórios:
list
: provê o componente que é a porta de entrada da entidade Produto, responsável por carregar os dados e apresentar em forma tabular.
@if (produtos && produtos.length > 0) {
<div class="table-responsive table-entities" id="entities">
<table class="table table-striped" aria-describedby="page-heading">
<thead>
...
details
: implementa o componente que exibirá os dados detalhados de um produto.
delete
: dispõe do componente que solicita a confirmação do usuário quando solicitada a exclusão de um produto e, confirmando-se, faz chamada ao método delete
do ProdutoService
.
update
: provê o componente de formulário tanto para criação quanto para manutenção dos dados de um produto.
<form name="editForm" role="form" novalidate (ngSubmit)="save()" [formGroup]="editForm">
<h2
id="jhi-produto-heading"
data-cy="ProdutoCreateUpdateHeading"
jhiTranslate="minhaAplicacaoMonoliticaApp.produto.home.createOrEditLabel"
>
Criar ou editar Produto
</h2>
...
service
: implementa o serviço da entidade produto ProdutoService
que provê as funcionalidades de interação com os endpoints do backend para realizar CRUD na entidade:
export class ProdutoService {
protected http = inject(HttpClient);
protected applicationConfigService = inject(ApplicationConfigService);
protected resourceUrl = this.applicationConfigService.getEndpointFor('api/produtos');
create(produto: NewProduto): Observable<EntityResponseType> {
return this.http.post<IProduto>(this.resourceUrl, produto, { observe: 'response' });
}
update(produto: IProduto): Observable<EntityResponseType> {
return this.http.put<IProduto>(`${this.resourceUrl}/${this.getProdutoIdentifier(produto)}`, produto, { observe: 'response' });
}
...
find(id: number): Observable<EntityResponseType> {
return this.http.get<IProduto>(`${this.resourceUrl}/${id}`, { observe: 'response' });
}
...
delete(id: number): Observable<HttpResponse<{}>> {
return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' });
}
Vale chamar atenção também para o fato de que, na raiz do webapp/app/entities/produto
, encontraremos um arquivo de definição de rotas específico para a entidade Produto, produto-routes.ts
. É possível observar que a exibição de cada um dos componentes contidos nos subdiretórios que acabamos de analisar está atrelada ao path da URL:
const produtoRoute: Routes = [
{
path: '',
component: ProdutoComponent,
data: { defaultSort: 'id,' + ASC },
canActivate: [UserRouteAccessService],
},
{
path: ':id/view',
component: ProdutoDetailComponent,
resolve: { produto: ProdutoResolve },
canActivate: [UserRouteAccessService],
},
{
path: 'new',
component: ProdutoUpdateComponent,
resolve: { produto: ProdutoResolve },
canActivate: [UserRouteAccessService],
},
{
path: ':id/edit',
component: ProdutoUpdateComponent,
resolve: { produto: ProdutoResolve },
canActivate: [UserRouteAccessService],
},
];
webapp/app/layouts/
Neste diretório são implementados uma série de componentes relacionados ao layout visual da aplicação: rodapé, barra de navegação, componente de erro etc.
├── layouts
│ ├── error
│ ├── footer
│ ├── main
│ ├── navbar
│ └── profiles
webapp/app/shared/
Aqui temos componentes compartilhados entre as diversas telas, tais como: paginação, filtros, exibição de mensagem, formatação de data etc.
└── shared
├── alert
├── auth
├── date
├── filter
├── language
├── pagination
└── sort
Conclusão
Ufa! Concluímos por aqui a série de análises do código da nossa aplicação monolítica gerado pelo JHipster. Neste último post da série, percorremos a estrutura de diretórios do frontend Angular. Detalhei um pouco aqueles pontos que me pareceram mais relevantes. Acredito que após esses três posts já temos uma boa noção do código que temos em mãos.
Nossa próxima missão é evoluir essa aplicação de forma que possamos conhecer outras funcionalidades do JHipster e, assim, aprofundarmos nosso conhecimento sobre essa poderosa ferramenta que tem um potencial enorme de acelerar os projetos de desenvolvimento das nossas organizações.
Fique à vontade para enviar suas dúvidas, comentários, sugestões e continue me acompanhando pois tem muito mais conteúdo vindo por aí! ;)
Posted on May 22, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
May 22, 2024