Backend Integration: Implementing Services And Models
Integrating the frontend with the backend is a crucial step in developing a robust and efficient application. This article delves into the comprehensive implementation of service layers and models, focusing on backend integration based on Swagger documentation (v3) for the GEA (Gestão Espaço Acadêmico) project. We'll explore the necessary steps, technical context, and acceptance criteria to ensure a seamless integration process.
Understanding the Importance of Service Layer and Models
In any modern web application, the service layer acts as an intermediary between the frontend and the backend. It encapsulates the business logic and provides a clean interface for the frontend to interact with the backend API. Models, on the other hand, define the structure of the data being exchanged between the frontend and the backend. Implementing these components correctly is vital for maintaining a scalable, maintainable, and efficient application. This article will guide you through the steps to implement service layers and models effectively.
Why are Service Layers Important?
Service layers are crucial for several reasons:
- Abstraction: They abstract the backend implementation details from the frontend, allowing developers to work on the frontend without needing to know the intricacies of the backend.
- Reusability: Services can be reused across different parts of the application, reducing code duplication and improving maintainability.
- Testability: Services are easier to test in isolation, leading to more robust and reliable applications.
- Maintainability: Changes to the backend API can be accommodated in the service layer without affecting the frontend code.
Why are Models Important?
Models, also known as data transfer objects (DTOs), play a crucial role in defining the structure of the data exchanged between the frontend and the backend. They help ensure consistency and avoid type-related errors. Here's why models are essential:
- Data Consistency: Models define a clear contract for the data being exchanged, ensuring that both the frontend and backend agree on the data structure.
- Type Safety: Using models with strong typing (e.g., TypeScript interfaces) helps catch type-related errors at compile time, reducing runtime errors.
- Code Readability: Models make the code easier to read and understand by providing a clear definition of the data being used.
- Maintainability: Changes to the data structure can be made in the model definitions, making it easier to maintain the application.
Technical Context and Setup
Before diving into the implementation details, it's essential to understand the technical context of the GEA project. This includes the base URL, folder architecture, and coding standards that need to be followed.
Base URL
The base URL for the GEA backend API is environment.apiUrl, which points to http://localhost:8080. This URL will be used as the base for all API requests made from the frontend.
Folder Architecture
To maintain a clean and organized codebase, the following folder structure should be adhered to:
- Services:
src/app/services/[resource-name]/[resource-name].service.ts - Models/Types:
src/app/models/[resource-name].model.ts
This structure ensures that services and models related to a specific resource (e.g., Professor, Disciplina) are grouped together, making it easier to locate and maintain the code.
Coding Standards
To ensure consistency across the codebase, the following coding standards should be followed:
- Return Types: All service methods should return
Observable<T>, leveraging the power of RxJS for asynchronous operations. - Method Naming: Methods should be named in Portuguese (e.g.,
buscarPorId,criar,atualizar,deletar) for consistency and clarity.
Detailed Implementation Steps
Now, let's dive into the detailed implementation steps, covering the creation of models and the implementation/refactoring of services.
1. Defining Models (Interfaces)
The first step is to create TypeScript interfaces based on the DTOs (Data Transfer Objects) defined in the Swagger documentation. This involves defining the structure of the data that will be exchanged between the frontend and the backend.
Best Practices for Model Definition
- Base Interfaces: Create base interfaces for read operations (e.g.,
Sala,Curso). These interfaces should represent the core properties of the resource. - Specific Types for Create/Edit: For create and edit operations, if the payload structure differs significantly from the base interface, create specific types (e.g.,
SalaRequestorCriarSala) in thetypes/[model-name].tsfolder. This helps keep the code clean and avoids overly complex interfaces. - Correctly Map Enums and Types: Pay close attention to mapping enums and types correctly. For example, ensure that boolean values (e.g.,
isEvento) are mapped tobooleanand dates are handled as either string ISO format orDateobjects.
Example: Defining a Sala Model
Consider a Sala (Room) model. A base interface might look like this:
// src/app/models/sala.model.ts
export interface Sala {
id: number;
nome: string;
capacidade: number;
tipo: string; // e.g., 'Laboratório', 'Sala de Aula'
recursos: string[]; // List of resource names
}
For creating a new Sala, a specific type might be defined:
// src/app/models/types/sala.types.ts
export interface CriarSala {
nome: string;
capacidade: number;
tipo: string;
recursos?: string[];
}
2. Implementing/Refactoring Services
The next step is to implement or refactor the services. This involves creating the necessary methods to interact with the backend API endpoints. The goal is to cover 100% of the endpoints exposed by the API.
Service-Specific Tasks
We will now detail the tasks for each service, covering the methods that need to be implemented or refactored.
A. AgendamentoService (/agendamentos):
buscarAulaPorId(id):GET /agendamentos/aulas/{id}atualizarAula(id, dados):PUT /agendamentos/aulas/{id}deletarAula(id):DELETE /agendamentos/aulas/{id}listarAulas():GET /agendamentos/aulascriarAula(dados):POST /agendamentos/aulascriarAulaAD(dados):POST /agendamentos/aulas/auxiliar-docentelistarTodos():GET /agendamentosbuscarPorData(data):GET /agendamentos/{data}listarAulasPorProfessor(id):GET /agendamentos/aulas/professor/{id}listarAulasPorDisciplina(id):GET /agendamentos/aulas/disciplina/{id}listarEventos():GET /agendamentos/eventoscriarEvento(dados):POST /agendamentos/eventos
B. AuthService (/auth):
registrar(dados):POST /auth/registerlogin(credenciais):POST /auth/login
C. AuxiliarDocenteService (/auxiliar-docentes):
listar(page, size):GET /auxiliar-docentes(paginated)criar(dados):POST /auxiliar-docentes
D. CargoService (/cargos):
listarTodos():GET /cargoscriar(dados):POST /cargosobterCargoUser():GET /cargos/userobterCargoAdmin():GET /cargos/admin
E. CoordenadorService (/coordenadores):
listarTodos():GET /coordenadorescriar(dados):POST /coordenadoresbuscarPorId(id):GET /coordenadores/{id}deletar(id):DELETE /coordenadores/{id}buscarPorRegistro(registro):GET /coordenadores/registro/{registro}
F. CursoService (/cursos):
buscarPorId(id):GET /cursos/{id}atualizar(id, dados):PUT /cursos/{id}deletar(id):DELETE /cursos/{id}listarTodos():GET /cursoscriar(dados):POST /cursos
G. DisciplinaService (/disciplinas):
Refactor existing service
buscarPorId(id):GET /disciplinas/{id}atualizar(id, dados):PUT /disciplinas/{id}deletar(id):DELETE /disciplinas/{id}listarTodas():GET /disciplinascriar(dados):POST /disciplinas
H. JanelaHorarioService (/janelas-horario):
buscarPorId(id):GET /janelas-horario/{id}atualizar(id, dados):PUT /janelas-horario/{id}listarTodas():GET /janelas-horariocriar(dados):POST /janelas-horariolistarDisponiveisPorData(data):GET /janelas-horario/disponiveis/{data}
I. NotificacaoService (/notificacoes):
listarTodas():GET /notificacoescriar(dados):POST /notificacoes
J. ProfessorService (/professores):
Refactor existing service
buscarPorId(id):GET /professores/{id}atualizar(id, dados):PUT /professores/{id}listarTodos():GET /professorescriar(dados):POST /professoreslistarDisciplinas(id):GET /professores/{id}/disciplinaslistarCursos(id):GET /professores/{id}/cursosdeletarPorRegistro(registro):DELETE /professores/{registro}
K. RecursoService (/recursos):
buscarPorId(id):GET /recursos/{id}atualizar(id, dados):PUT /recursos/{id}deletar(id):DELETE /recursos/{id}listarTodos():GET /recursoscriar(dados):POST /recursoslistarPorTipo(tipoId):GET /recursos/tipo/{tipoId}
L. SalaService (/salas):
buscarPorId(id):GET /salas/{id}atualizar(id, dados):PUT /salas/{id}deletar(id):DELETE /salas/{id}listarTodas():GET /salascriar(dados):POST /salaslistarRecursosDaSala(id):GET /salas/{id}/recursosadicionarRecursoNaSala(id, dados):POST /salas/{id}/recursosatualizarRecursoNaSala(salaId, recursoId, dados):PUT /salas/{salaId}/recursos/{recursoId}removerRecursoDaSala(salaId, recursoId):DELETE /salas/{salaId}/recursos/{recursoId}obterRecomendacoes(dados):POST /salas/recomendacoeslistarDisponiveis():GET /salas/disponiveis
M. TipoRecursoService (/tipo-recurso):
- Implement full CRUD (GET, POST, PUT, DELETE) according to endpoints.
N. TipoSalaService (/tipos-salas):
- Implement full CRUD (GET, POST, PUT, DELETE) according to endpoints.
O. UsuarioService (/usuarios):
buscarPorId(id):GET /usuarios/{id}atualizar(id, dados):PATCH /usuarios/{id}listarTodos():GET /usuarios
Example: Implementing DisciplinaService
Here’s an example of how to implement the DisciplinaService:
// src/app/services/disciplina/disciplina.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Disciplina } from '../../models/disciplina.model';
import { environment } from '../../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class DisciplinaService {
private apiUrl = `${environment.apiUrl}/disciplinas`;
constructor(private http: HttpClient) {}
buscarPorId(id: number): Observable<Disciplina> {
return this.http.get<Disciplina>(`${this.apiUrl}/${id}`);
}
atualizar(id: number, dados: Disciplina): Observable<Disciplina> {
return this.http.put<Disciplina>(`${this.apiUrl}/${id}`, dados);
}
deletar(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
listarTodas(): Observable<Disciplina[]> {
return this.http.get<Disciplina[]>(this.apiUrl);
}
criar(dados: Disciplina): Observable<Disciplina> {
return this.http.post<Disciplina>(this.apiUrl, dados);
}
}
3. Code Cleanup
After implementing the services and models, it’s essential to clean up the codebase. This involves removing dead code, duplicate services, and ensuring that all methods use dependency injection correctly.
Cleanup Tasks
- Remove Dead Code: Eliminate any unused code or services that are no longer needed.
- Remove Duplicate Services: Ensure that there are no duplicate services that don’t follow the defined standard.
- Ensure Correct Dependency Injection: Verify that all methods use the
HttpClientdependency injection correctly.
Acceptance Criteria and Tasks Checklist
To ensure that the implementation meets the required standards, the following acceptance criteria and tasks checklist should be used:
Acceptance Criteria
- All service methods return
Observable<T>. ✅ - Methods are named in Portuguese. ✅
- Models are defined based on Swagger DTOs. ✅
- Correctly mapped Enums and Types. ✅
- Code follows the defined folder architecture. ✅
- No dead code or duplicate services. ✅
HttpClientdependency injection is used correctly. ✅
Tasks Checklist
- [ ] Define models (interfaces) for all resources.
- [ ] Implement/refactor
AgendamentoService. - [ ] Implement
AuthService. - [ ] Implement
AuxiliarDocenteService. - [ ] Implement
CargoService. - [ ] Implement
CoordenadorService. - [ ] Implement
CursoService. - [ ] Refactor
DisciplinaService. - [ ] Implement
JanelaHorarioService. - [ ] Implement
NotificacaoService. - [ ] Refactor
ProfessorService. - [ ] Implement
RecursoService. - [ ] Implement
SalaService. - [ ] Implement CRUD for
TipoRecursoService. - [ ] Implement CRUD for
TipoSalaService. - [ ] Implement
UsuarioService. - [ ] Perform code cleanup.
Conclusion
Implementing the service layer and models is a critical step in integrating the frontend with the backend for the GEA project. By following the steps outlined in this article, you can ensure that the integration is done correctly, resulting in a scalable, maintainable, and efficient application. Remember to adhere to the coding standards, folder architecture, and acceptance criteria to maintain a high-quality codebase. By meticulously implementing these services and models, the GEA project will be well-equipped to provide a seamless and efficient academic management experience.
For more information on best practices for Angular services and RxJS Observables, you can visit the official Angular documentation or explore resources like https://www.learnrxjs.io/.