¡Esta es una revisión vieja del documento!
Esta sección expone recomendaciones para organizar un proyecto de software. Como referencia se utilizará la tecnología java con el framework springboot.
Como práctica recomendada en la industria del desarrollo de software, se debe utilizar una estructura de carpetas bien organizada para mejorar la capacidad de mantenimiento del código, la colaboración entre los miembros del equipo y el proceso de desarrollo general.
A continuación se detallan algunas razones clave para organizar la estructura de carpetas de un protecto Spring Boot:
Una estructura de carpetas bien definida proporciona un diseño claro de dónde encontrar los diferentes componentes de su aplicación. Facilita a los desarrolladores la localización de archivos, paquetes y recursos específicos, lo que reduce el tiempo dedicado a buscar código.
Spring Boot sigue un enfoque modular en el que la aplicación se divide en diferentes componentes lógicos como controladores, servicios, repositorios, configuraciones, etc. Tener una estructura de carpetas adecuada le permite organizar estos componentes de manera efectiva, lo que hace que la aplicación sea más fácil de entender y mantener.
La estructura de carpetas impone una separación de responsabilidades, lo que permite a los desarrolladores centrarse en partes específicas de la aplicación sin preocuparse por código no relacionado. Por ejemplo, la lógica empresarial reside en la capa de servicio, el código de acceso a datos en la capa de repositorio y el manejo de solicitudes web en la capa de controlador.
A medida que la aplicación crece, una estructura de carpetas bien organizada facilita la incorporación de nuevas características y funcionalidades sin convertir el proyecto en un desastre inmanejable. Facilita la adición de nuevos módulos o componentes manteniendo una arquitectura general coherente.
Una organización adecuada fomenta la creación de componentes reutilizables. Cuando los desarrolladores pueden encontrar y comprender fácilmente el código existente, es más probable que lo reutilicen en lugar de duplicar esfuerzos.
Cuando varios desarrolladores trabajan en un proyecto, seguir una estructura de carpetas estandarizada garantiza la coherencia y facilita la colaboración. Todos los miembros del equipo saben dónde colocar el código nuevo y pueden localizar rápidamente el código agregado por otros. Compilación e implementación: herramientas como sistemas de compilación, integración continua y scripts de implementación pueden aprovechar una estructura de carpetas predecible para automatizar los procesos de compilación e implementación de manera eficiente.
Una estructura de carpetas organizada simplifica la escritura de pruebas y la depuración de la aplicación. Las clases y recursos de prueba se pueden colocar junto a las clases correspondientes, lo que facilita la comprensión de la cobertura del conjunto de pruebas.
Cuando nuevos desarrolladores se unen al equipo, una estructura de carpetas bien definida les ayuda a ponerse al día rápidamente. Pueden comprender rápidamente la arquitectura y localizar secciones de código relevantes. Base de código mantenible: una base de código limpia y bien estructurada es más fácil de mantener a largo plazo. Reduce la deuda técnica y hace que sea menos probable que se introduzcan errores o regresiones al realizar cambios.
A continuación se comparte el siguiente gráfico de referencia que proponen una estructura de organización de un proyecto springboot.
Contiene clases de configuración, donde configura los ajustes de la aplicación o otras configuraciones a nivel de aplicación.
/** * Copyright © 2023 ADA Corporation. All rights reserved. * * This component is protected by copyright. * * Use of this component is subject to the terms of the license agreement. */ package com.ada.viatico.proceso.config; import java.util.List; import java.util.TimeZone; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.servers.Server; @Configuration public class ViaticosConfig { /** The log. */ private static Logger log = LoggerFactory.getLogger(ViaticosConfig.class); /** The url. */ @Value("${openapi.url}") private String url; /** The application title. */ @Value("${application.title}") private String applicationTitle; /** The application summary. */ @Value("${application.version}") private String applicationVersion; @PostConstruct public void init() { log.info("Establicendo Zona Horaria..."); TimeZone.setDefault(TimeZone.getTimeZone("America/Bogota")); log.info("Zona Horaria establecida a {}", TimeZone.getDefault().getID()); } /** * Property sources placeholder configurer. * * @return the property sources placeholder configurer */ @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } /** * My open API. * * @return the open API */ @Bean public OpenAPI myOpenAPI() { Server server = new Server(); server.setUrl(url); server.setDescription("Server URL environment"); Contact contact = new Contact(); contact.setEmail("carlos.torres@ada.co; daniel.lopez@ada.co"); contact.setName("Carlos Torres | Daniel López"); contact.setUrl("www.ada.co"); License mitLicense = new License().name("ADA License").url("www.ada.co/licenses/"); Info info = new Info() .title(applicationTitle) .version("v" + applicationVersion) .summary("Microservicio para gestionar Viáticos.") .description("Servicio que realiza las operaciones de creación/aprobación de Solicitudes y Legalización de Viáticos.") .contact(contact) .termsOfService("https://www.ada.co/terms") .license(mitLicense); return new OpenAPI().info(info).servers(List.of(server)); } }
Contiene sus clases de controlador RESTful. Estas clases manejan solicitudes HTTP entrantes y definen los puntos finales de la API.
/** * Copyright © 2023 ADA Corporation. All rights reserved. * * This component is protected by copyright. * * Use of this component is subject to the terms of the license agreement. */ package com.ada.viatico.proceso.controller.v2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.ada.viatico.proceso.dto.DatosGenerarAprobacion; import com.ada.viatico.proceso.dto.GenericResponseDto; import com.ada.viatico.proceso.repository.IMestroCompromisoRepo; import com.ada.viatico.proceso.service.Impl.TicketsImp; import com.ada.viatico.proceso.service.Impl.ViaticoSolicitudImpl; import com.ada.viatico.proceso.service.v2.ViaticosServiceV2; import com.ada.viatico.proceso.sfcomercial.dto.AprobacionLegalizacionDto; import com.ada.viatico.proceso.utiliti.AdelantoSolicitud; import com.ada.viatico.proceso.utiliti.GuardarLegalizacion; import com.ada.viatico.proceso.utiliti.ProcesoGuardar; import com.ada.viatico.proceso.utiliti.ProcesoViaticoAprobacion; import com.ada.viatico.proceso.utiliti.ViaticosException; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; /** * The Class ViaticosController. * @version 2.0 * @author carlos.torres@ada.co */ @CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE}) @RestController @RequestMapping("v2") public class ViaticosControllerV2 { /** The log. */ private Logger log = LoggerFactory.getLogger(ViaticosControllerV2.class); /** The ticketsimpl. */ @Autowired TicketsImp ticketsimpl; /** The guardar. */ @Autowired ProcesoGuardar guardar; /** The aprobacion. */ @Autowired ProcesoViaticoAprobacion aprobacion; /** The solicitud repo. */ @Autowired ViaticoSolicitudImpl solicitudRepo; /** The savelega. */ @Autowired GuardarLegalizacion savelega; /** The repo compromiso. */ @Autowired IMestroCompromisoRepo repoCompromiso; /** The solicitud adelanto. */ @Autowired AdelantoSolicitud solicitudAdelanto; /** The viaticos service. */ @Autowired ViaticosServiceV2 viaticosServiceV2; /** * Aprobar legalizacion. * * @param aprobacionLegalizacionDto the aprobacion legalizacion dto * @return the response entity * @throws ViaticosException the viaticos exception */ @Operation(summary = "Aprobar Legalización.", description = "Aprueba la Legalización de un Viático.") @ApiResponse(responseCode = "201", content = { @Content(schema = @Schema(implementation = GenericResponseDto.class)) }) @ApiResponse(responseCode = "500", content = { @Content(schema = @Schema(implementation = ViaticosException.class)) }) @Parameter(name = "aprobacionLegalizacionDto", description = "Componente que contiene los parametros necesarios para el proceso de legalización.", in = ParameterIn.QUERY, required = true) @PostMapping("/aprobarlegalizacion") public ResponseEntity<GenericResponseDto> aprobarLegalizacion(@RequestBody AprobacionLegalizacionDto aprobacionLegalizacionDto) throws ViaticosException { var result = viaticosServiceV2.aprobarLegalizacion(aprobacionLegalizacionDto); if(result.getCode() == -1L) { log.info("approve legalization executed with errors."); return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR); } log.info("approve legalization executed correctly."); return new ResponseEntity<>(result, HttpStatus.CREATED); } /** * Guardar viatico. * * @param datos the datos * @return the response entity * @throws ViaticosException the viaticos exception */ @Operation(summary = "Aprobar Solicitud.", description = "Aprueba la solicitud de un Viático.") @ApiResponse(responseCode = "201", content = { @Content(schema = @Schema(implementation = GenericResponseDto.class)) }) @ApiResponse(responseCode = "500", content = { @Content(schema = @Schema(implementation = ViaticosException.class)) }) @Parameter(name = "datos", description = "Componente que contiene los parametros necesarios para el proceso de solicitud.", in = ParameterIn.QUERY, required = true) @PostMapping("/generaraprobacion") public ResponseEntity<GenericResponseDto> guardarViatico(@RequestBody DatosGenerarAprobacion datos) throws ViaticosException { var result = viaticosServiceV2.aprobarSolicitud(datos); if(result.getCode() == -1L) return new ResponseEntity<>(result, HttpStatus.SERVICE_UNAVAILABLE); return new ResponseEntity<>(result, HttpStatus.CREATED); } }
Contiene sus clases para permitir conexiones multicliente y modelo multiempresa.
Un DTO es un patrón de diseño utilizado para transferir datos entre diferentes capas o componentes de una aplicación. El objetivo principal de un DTO es encapsular datos y proporcionar una estructura de datos simple que pueda transmitirse fácilmente por la aplicación. Los DTO se utilizan a menudo para transferir datos entre el front-end y el back-end de una aplicación web, entre microservicios o entre diferentes capas de una aplicación, como la capa de servicio y la capa de presentación.
Características de una DTO:
/** * Copyright © 2023 ADA Corporation. All rights reserved. * * This component is protected by copyright. * * Use of this component is subject to the terms of the license agreement. */ package com.ada.viatico.proceso.sfcomercial.dto; import java.io.Serializable; import lombok.Data; /** * Instantiates a new afectacion presupuestal dto. */ @Data public class AfectacionPresupuestalDto implements Serializable { /** The Constant serialVersionUID. */ private static final long serialVersionUID = -7167570415608375973L; /** The cod disponibilidad. */ Long codDisponibilidad; /** The codigo rubro. */ Long codigoRubro; /** The valor. */ Long valor; /** The cod C costos. */ Long codCCostos; }