From 1a86f842d2d2a2441462efe98ef380e61e2a4b21 Mon Sep 17 00:00:00 2001 From: Pablo de la Torre Jamardo Date: Mon, 2 Mar 2026 16:38:11 +0100 Subject: [PATCH] refactor: replace Spring Boot with Jooby framework - Remove Spring Boot dependencies and annotations. - Implement Jooby MVC controllers and Guice dependency injection. - Migrate persistence layer to Ebean ORM. - Configure Flyway migrations and ApiErrorController. - Update application configuration to HOCON format. --- .env.example | 2 - .gitignore | 3 +- Dockerfile | 9 +- HELP.md | 31 ---- application/pom.xml | 9 + .../experience/CreateExperienceUseCase.java | 14 +- .../project/GetProjectUseCase.java | 1 - bootstrap/pom.xml | 162 ++++++++++++++--- .../bootstrap/PortfolioApplication.java | 73 ++++++-- .../CertificationApplicationConfig.java | 23 +-- .../education/EducationApplicationConfig.java | 23 +-- .../ExperienceApplicationConfig.java | 23 +-- .../profile/ProfileApplicationConfig.java | 23 +-- .../project/ProjectApplicationConfig.java | 23 +-- .../skill/SkillApplicationConfig.java | 23 +-- bootstrap/src/main/resources/application.conf | 60 +++++++ bootstrap/src/main/resources/application.yml | 54 ------ domain/pom.xml | 5 +- .../domain/certification/CertificationId.java | 3 +- .../domain/experience/Achievement.java | 1 - .../domain/experience/ExperienceId.java | 3 +- .../project/port/ProjectRepositoryPort.java | 3 +- infrastructure/pom.xml | 80 +++++++-- .../infrastructure/config/CorsConfig.java | 40 ++--- .../infrastructure/config/SecurityConfig.java | 32 +--- .../CertificationRepositoryAdapter.java | 41 +++-- ...paEntity.java => CertificationEntity.java} | 7 +- ...er.java => CertificationEntityMapper.java} | 11 +- .../SpringDataCertificationRepository.java | 14 -- .../adapter/EducationRepositoryAdapter.java | 41 +++-- ...ionJpaEntity.java => EducationEntity.java} | 9 +- ...Mapper.java => EducationEntityMapper.java} | 11 +- .../repo/SpringDataEducationRepository.java | 13 -- .../adapter/ExperienceRepositoryAdapter.java | 45 +++-- ....java => ExperienceAchievementEntity.java} | 3 +- ...ceJpaEntity.java => ExperienceEntity.java} | 12 +- ...Entity.java => ExperienceSkillEntity.java} | 3 +- ...apper.java => ExperienceEntityMapper.java} | 22 +-- .../repo/SpringDataExperienceRepository.java | 13 -- .../adapter/ProfileRepositoryAdapter.java | 55 +++--- ...ofileJpaEntity.java => ProfileEntity.java} | 7 +- ...tity.java => ProfileSocialLinkEntity.java} | 6 +- ...paMapper.java => ProfileEntityMapper.java} | 16 +- .../repo/SpringDataProfileRepository.java | 10 -- .../adapter/ProjectRepositoryAdapter.java | 45 +++-- ...ojectJpaEntity.java => ProjectEntity.java} | 16 +- ...aEntity.java => ProjectFeatureEntity.java} | 3 +- ...tity.java => ProjectTechnologyEntity.java} | 3 +- ...paMapper.java => ProjectEntityMapper.java} | 22 +-- .../repo/SpringDataProjectRepository.java | 13 -- .../skill/adapter/SkillRepositoryAdapter.java | 43 +++-- .../{SkillJpaEntity.java => SkillEntity.java} | 3 +- ...upJpaEntity.java => SkillGroupEntity.java} | 9 +- .../{SkillJpaMapper.java => SkillMapper.java} | 16 +- .../skill/repo/SpringDataSkillRepository.java | 13 -- .../rest/api/ApiErrorController.java | 40 ++--- .../rest/api/ApiRootController.java | 44 ++--- .../CertificationController.java | 49 +++-- .../mapper/CertificationRestMapper.java | 2 +- .../rest/education/EducationController.java | 49 +++-- .../education/mapper/EducationRestMapper.java | 2 +- .../rest/experience/ExperienceController.java | 49 +++-- .../dto/CreateExperienceRequest.java | 3 +- .../rest/experience/dto/ExperienceDto.java | 3 +- .../mapper/ExperienceRestMapper.java | 2 +- .../rest/profile/ProfileController.java | 52 +++--- .../profile/mapper/ProfileRestMapper.java | 2 +- .../rest/project/ProjectController.java | 49 +++-- .../project/dto/CreateProjectRequest.java | 3 +- .../project/mapper/ProjectRestMapper.java | 2 +- .../rest/skill/SkillController.java | 53 +++--- .../rest/skill/mapper/SkillRestMapper.java | 2 +- .../db/migration/V1__Initial_Setup.sql | 168 ++++++++++++++++++ infrastructure/src/main/resources/ddl.sql | 140 --------------- infrastructure/src/main/resources/ebean.mf | 3 + pom.xml | 139 ++++++++------- 76 files changed, 1161 insertions(+), 943 deletions(-) delete mode 100644 HELP.md create mode 100644 bootstrap/src/main/resources/application.conf delete mode 100644 bootstrap/src/main/resources/application.yml rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/entity/{CertificationJpaEntity.java => CertificationEntity.java} (87%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/mapper/{CertificationJpaMapper.java => CertificationEntityMapper.java} (62%) delete mode 100644 infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/repo/SpringDataCertificationRepository.java rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/entity/{EducationJpaEntity.java => EducationEntity.java} (88%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/mapper/{EducationJpaMapper.java => EducationEntityMapper.java} (64%) delete mode 100644 infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/repo/SpringDataEducationRepository.java rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/{ExperienceAchievementJpaEntity.java => ExperienceAchievementEntity.java} (90%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/{ExperienceJpaEntity.java => ExperienceEntity.java} (83%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/{ExperienceSkillJpaEntity.java => ExperienceSkillEntity.java} (90%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/mapper/{ExperienceJpaMapper.java => ExperienceEntityMapper.java} (63%) delete mode 100644 infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/repo/SpringDataExperienceRepository.java rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/{ProfileJpaEntity.java => ProfileEntity.java} (85%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/{ProfileSocialLinkJpaEntity.java => ProfileSocialLinkEntity.java} (87%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/mapper/{ProfileJpaMapper.java => ProfileEntityMapper.java} (64%) delete mode 100644 infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/repo/SpringDataProfileRepository.java rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/{ProjectJpaEntity.java => ProjectEntity.java} (81%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/{ProjectFeatureJpaEntity.java => ProjectFeatureEntity.java} (87%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/{ProjectTechnologyJpaEntity.java => ProjectTechnologyEntity.java} (87%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/mapper/{ProjectJpaMapper.java => ProjectEntityMapper.java} (63%) delete mode 100644 infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/repo/SpringDataProjectRepository.java rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/{SkillJpaEntity.java => SkillEntity.java} (90%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/{SkillGroupJpaEntity.java => SkillGroupEntity.java} (86%) rename infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/mapper/{SkillJpaMapper.java => SkillMapper.java} (68%) delete mode 100644 infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/repo/SpringDataSkillRepository.java create mode 100644 infrastructure/src/main/resources/db/migration/V1__Initial_Setup.sql delete mode 100644 infrastructure/src/main/resources/ddl.sql create mode 100644 infrastructure/src/main/resources/ebean.mf diff --git a/.env.example b/.env.example index 219c273..aa7b5b9 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,3 @@ -SPRING_PROFILES_ACTIVE=dev - APP_ALLOWED_ORIGINS='http://127.0.0.1:3000, http://localhost:3000' DB_NAME=EXAMPLE_DB diff --git a/.gitignore b/.gitignore index 2f7dbd9..2f2bb05 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ *.db target .env -Icon? \ No newline at end of file +Icon? +.docker \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 86c75db..9fd3d0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,11 +26,14 @@ FROM eclipse-temurin:21-jre-alpine WORKDIR /app # Seguridad: Usuario no-root -RUN addgroup -S spring && adduser -S spring -G spring -USER spring:spring +RUN addgroup -S app && adduser -S app -G app + +RUN mkdir -p /app/tmp && chown app:app /app/tmp && chmod 777 /app/tmp + +USER app:app # Copiamos solo el JAR final (ajustado a tu módulo bootstrap) -COPY --from=build /app/bootstrap/target/*.jar app.jar +COPY --from=build /app/bootstrap/target/bootstrap-*.jar app.jar # Configuración de Memoria y Rendimiento para Microservicios # -XX:+UseSerialGC: Menos consumo de RAM para apps < 1GB diff --git a/HELP.md b/HELP.md deleted file mode 100644 index f80f69c..0000000 --- a/HELP.md +++ /dev/null @@ -1,31 +0,0 @@ -# Getting Started - -### Reference Documentation -For further reference, please consider the following sections: - -* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) -* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/maven-plugin) -* [Create an OCI image](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/maven-plugin/build-image.html) -* [Spring Web](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/web/servlet.html) -* [Spring Data JPA](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/data/sql.html#data.sql.jpa-and-spring-data) -* [Validation](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/io/validation.html) -* [Spring Boot DevTools](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/using/devtools.html) -* [Spring Boot Actuator](https://docs.spring.io/spring-boot/4.0.0-SNAPSHOT/reference/actuator/index.html) - -### Guides -The following guides illustrate how to use some features concretely: - -* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) -* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) -* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) -* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/) -* [Validation](https://spring.io/guides/gs/validating-form-input/) -* [Building a RESTful Web Service with Spring Boot Actuator](https://spring.io/guides/gs/actuator-service/) - -### Maven Parent overrides - -Due to Maven's design, elements are inherited from the parent POM to the project POM. -While most of the inheritance is fine, it also inherits unwanted elements like `` and `` from the parent. -To prevent this, the project POM contains empty overrides for these elements. -If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides. - diff --git a/application/pom.xml b/application/pom.xml index 75ed13b..a6f229f 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -12,9 +12,18 @@ domain ${project.version} + jakarta.validation jakarta.validation-api + 3.0.2 + + + + org.projectlombok + lombok + 1.18.36 + provided \ No newline at end of file diff --git a/application/src/main/java/com/pablotj/portfolio/application/experience/CreateExperienceUseCase.java b/application/src/main/java/com/pablotj/portfolio/application/experience/CreateExperienceUseCase.java index 058d154..fa07935 100644 --- a/application/src/main/java/com/pablotj/portfolio/application/experience/CreateExperienceUseCase.java +++ b/application/src/main/java/com/pablotj/portfolio/application/experience/CreateExperienceUseCase.java @@ -33,13 +33,13 @@ public class CreateExperienceUseCase { } public record Command( - String position, - String company, - String period, - String location, - String description, - List technologies, - List achievements + String position, + String company, + String period, + String location, + String description, + List technologies, + List achievements ) { } } \ No newline at end of file diff --git a/application/src/main/java/com/pablotj/portfolio/application/project/GetProjectUseCase.java b/application/src/main/java/com/pablotj/portfolio/application/project/GetProjectUseCase.java index 2390b21..3b7eb09 100644 --- a/application/src/main/java/com/pablotj/portfolio/application/project/GetProjectUseCase.java +++ b/application/src/main/java/com/pablotj/portfolio/application/project/GetProjectUseCase.java @@ -3,7 +3,6 @@ package com.pablotj.portfolio.application.project; import com.pablotj.portfolio.domain.project.Project; import com.pablotj.portfolio.domain.project.ProjectId; import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort; - import java.util.List; import java.util.Optional; diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index 44dcb76..31672fb 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -1,12 +1,22 @@ - + + 4.0.0 com.pablotj portfolio-api 0.0.1-SNAPSHOT + bootstrap + + 3.6.1 + 42.7.2 + 1.19.7 + + com.pablotj @@ -14,40 +24,90 @@ ${project.version} - + + io.jooby + jooby-netty + ${jooby.version} + + + + io.jooby + jooby-jackson + ${jooby.version} + + + + io.jooby + jooby-jackson + 3.6.1 + + + + io.jooby + jooby-hibernate-validator + 3.6.1 + + + + io.jooby + jooby-openapi + ${jooby.version} + + + + io.jooby + jooby-swagger-ui + ${jooby.version} + + + + io.jooby + jooby-logback + ${jooby.version} + + org.postgresql postgresql - runtime + ${postgresql.version} com.h2database h2 - runtime + 2.2.224 - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - ${springdoc.version} + io.jooby + jooby-hikari + ${jooby.version} - - - org.springframework.boot - spring-boot-starter-actuator + io.jooby + jooby-guice + ${jooby.version} - - + - org.springframework.boot - spring-boot-starter-test + io.jooby + jooby-flyway + ${jooby.version} + + + io.jooby + jooby-test + ${jooby.version} test org.testcontainers postgresql + ${testcontainers.version} test @@ -55,15 +115,77 @@ - org.springframework.boot - spring-boot-maven-plugin + io.jooby + jooby-maven-plugin + ${jooby.version} - repackage + openapi + + com.pablotj.portfolio.bootstrap.PortfolioApplication + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.1 + + + jooby-shade + package + + shade + + + false + + + *:* + + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/MANIFEST.MF + META-INF/DEPENDENCIES* + META-INF/LICENSE* + META-INF/NOTICE* + META-INF/*.txt + META-INF/*.md + + + META-INF/io.netty.versions.properties + draftv3/schema + draftv4/schema + **/module-info.class + META-INF/versions/** + + + LICENSE* + NOTICE* + + + + + + + META-INF/ebean.mf + + + com.pablotj.portfolio.bootstrap.PortfolioApplication + + + + + diff --git a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/PortfolioApplication.java b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/PortfolioApplication.java index 019057d..74bfe65 100644 --- a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/PortfolioApplication.java +++ b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/PortfolioApplication.java @@ -1,16 +1,67 @@ package com.pablotj.portfolio.bootstrap; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import com.pablotj.portfolio.bootstrap.certification.CertificationApplicationConfig; +import com.pablotj.portfolio.bootstrap.education.EducationApplicationConfig; +import com.pablotj.portfolio.bootstrap.experience.ExperienceApplicationConfig; +import com.pablotj.portfolio.bootstrap.profile.ProfileApplicationConfig; +import com.pablotj.portfolio.bootstrap.project.ProjectApplicationConfig; +import com.pablotj.portfolio.bootstrap.skill.SkillApplicationConfig; +import com.pablotj.portfolio.infrastructure.config.CorsConfig; +import com.pablotj.portfolio.infrastructure.rest.api.ApiErrorController; +import com.pablotj.portfolio.infrastructure.rest.api.ApiRootController; +import com.pablotj.portfolio.infrastructure.rest.certification.CertificationController; +import com.pablotj.portfolio.infrastructure.rest.education.EducationController; +import com.pablotj.portfolio.infrastructure.rest.experience.ExperienceController; +import com.pablotj.portfolio.infrastructure.rest.profile.ProfileController; +import com.pablotj.portfolio.infrastructure.rest.project.ProjectController; +import com.pablotj.portfolio.infrastructure.rest.skill.SkillController; +import io.jooby.Jooby; +import io.jooby.OpenAPIModule; +import io.jooby.ebean.EbeanModule; +import io.jooby.flyway.FlywayModule; +import io.jooby.guice.GuiceModule; +import io.jooby.hibernate.validator.HibernateValidatorModule; +import io.jooby.hikari.HikariModule; +import io.jooby.jackson.JacksonModule; +import io.jooby.netty.NettyServer; -@SpringBootApplication(scanBasePackages = "com.pablotj") -@EnableJpaRepositories(basePackages = {"com.pablotj.portfolio.infrastructure.persistence.*.repo"}) -@EntityScan(basePackages = {"com.pablotj.portfolio.infrastructure.persistence.*.entity"}) -public class PortfolioApplication { +public class PortfolioApplication extends Jooby { + + { + install(new NettyServer()); + + install(new JacksonModule()); + install(new HibernateValidatorModule()); + install(new HikariModule()); + install(new FlywayModule()); + install(new EbeanModule()); + install(new CorsConfig()); + + error(ApiErrorController.getHandler()); + + install(new ProfileApplicationConfig()); + install(new ProjectApplicationConfig()); + install(new CertificationApplicationConfig()); + install(new EducationApplicationConfig()); + install(new ExperienceApplicationConfig()); + install(new SkillApplicationConfig()); + + install(new GuiceModule()); // Esto habilita la resolución de @Inject y @Named + + path("/api", () -> { + mvc(ApiRootController.class); + mvc(CertificationController.class); + mvc(EducationController.class); + mvc(ExperienceController.class); + mvc(ProfileController.class); + mvc(ProjectController.class); + mvc(SkillController.class); + }); + install(new OpenAPIModule().swaggerUI("/docs")); - public static void main(String[] args) { - SpringApplication.run(PortfolioApplication.class, args); } -} + + public static void main(final String[] args) { + runApp(args, PortfolioApplication::new); + } +} \ No newline at end of file diff --git a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/certification/CertificationApplicationConfig.java b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/certification/CertificationApplicationConfig.java index a652302..95246a0 100644 --- a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/certification/CertificationApplicationConfig.java +++ b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/certification/CertificationApplicationConfig.java @@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.certification; import com.pablotj.portfolio.application.certification.CreateCertificationUseCase; import com.pablotj.portfolio.application.certification.GetCertificationUseCase; import com.pablotj.portfolio.domain.certification.port.CertificationRepositoryPort; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import com.pablotj.portfolio.infrastructure.persistence.certification.adapter.CertificationRepositoryAdapter; +import io.jooby.Extension; +import io.jooby.Jooby; +import jakarta.annotation.Nonnull; -@Configuration -public class CertificationApplicationConfig { +public class CertificationApplicationConfig implements Extension { - @Bean - public GetCertificationUseCase getCertificationUseCase(CertificationRepositoryPort repo) { - return new GetCertificationUseCase(repo); - } + @Override + public void install(@Nonnull Jooby app) { + CertificationRepositoryAdapter adapter = new CertificationRepositoryAdapter(); - @Bean - public CreateCertificationUseCase createCertificationUseCase(CertificationRepositoryPort repo) { - return new CreateCertificationUseCase(repo); + app.getServices().put(CertificationRepositoryPort.class, adapter); + + app.getServices().put(GetCertificationUseCase.class, new GetCertificationUseCase(adapter)); + app.getServices().put(CreateCertificationUseCase.class, new CreateCertificationUseCase(adapter)); } } \ No newline at end of file diff --git a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/education/EducationApplicationConfig.java b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/education/EducationApplicationConfig.java index 91875ca..33e33f9 100644 --- a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/education/EducationApplicationConfig.java +++ b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/education/EducationApplicationConfig.java @@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.education; import com.pablotj.portfolio.application.education.CreateEducationUseCase; import com.pablotj.portfolio.application.education.GetEducationUseCase; import com.pablotj.portfolio.domain.education.port.EducationRepositoryPort; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import com.pablotj.portfolio.infrastructure.persistence.education.adapter.EducationRepositoryAdapter; +import io.jooby.Extension; +import io.jooby.Jooby; +import jakarta.annotation.Nonnull; -@Configuration -public class EducationApplicationConfig { +public class EducationApplicationConfig implements Extension { - @Bean - public GetEducationUseCase getEducationUseCase(EducationRepositoryPort repo) { - return new GetEducationUseCase(repo); - } + @Override + public void install(@Nonnull Jooby app) { + EducationRepositoryAdapter adapter = new EducationRepositoryAdapter(); - @Bean - public CreateEducationUseCase createEducationUseCase(EducationRepositoryPort repo) { - return new CreateEducationUseCase(repo); + app.getServices().put(EducationRepositoryPort.class, adapter); + + app.getServices().put(GetEducationUseCase.class, new GetEducationUseCase(adapter)); + app.getServices().put(CreateEducationUseCase.class, new CreateEducationUseCase(adapter)); } } \ No newline at end of file diff --git a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/experience/ExperienceApplicationConfig.java b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/experience/ExperienceApplicationConfig.java index 31bf306..2637618 100644 --- a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/experience/ExperienceApplicationConfig.java +++ b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/experience/ExperienceApplicationConfig.java @@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.experience; import com.pablotj.portfolio.application.experience.CreateExperienceUseCase; import com.pablotj.portfolio.application.experience.GetExperienceUseCase; import com.pablotj.portfolio.domain.experience.port.ExperienceRepositoryPort; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import com.pablotj.portfolio.infrastructure.persistence.experience.adapter.ExperienceRepositoryAdapter; +import io.jooby.Extension; +import io.jooby.Jooby; +import jakarta.annotation.Nonnull; -@Configuration -public class ExperienceApplicationConfig { +public class ExperienceApplicationConfig implements Extension { + @Override + public void install(@Nonnull Jooby app) { - @Bean - public GetExperienceUseCase getExperienceUseCase(ExperienceRepositoryPort repo) { - return new GetExperienceUseCase(repo); - } + ExperienceRepositoryAdapter adapter = new ExperienceRepositoryAdapter(); - @Bean - public CreateExperienceUseCase createExperienceUseCase(ExperienceRepositoryPort repo) { - return new CreateExperienceUseCase(repo); + app.getServices().put(ExperienceRepositoryPort.class, adapter); + + app.getServices().put(GetExperienceUseCase.class, new GetExperienceUseCase(adapter)); + app.getServices().put(CreateExperienceUseCase.class, new CreateExperienceUseCase(adapter)); } } \ No newline at end of file diff --git a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/profile/ProfileApplicationConfig.java b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/profile/ProfileApplicationConfig.java index 6aaaeb6..b4cdbcf 100644 --- a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/profile/ProfileApplicationConfig.java +++ b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/profile/ProfileApplicationConfig.java @@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.profile; import com.pablotj.portfolio.application.profile.CreateProfileUseCase; import com.pablotj.portfolio.application.profile.GetProfileUseCase; import com.pablotj.portfolio.domain.profile.port.ProfileRepositoryPort; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import com.pablotj.portfolio.infrastructure.persistence.profile.adapter.ProfileRepositoryAdapter; +import io.jooby.Extension; +import io.jooby.Jooby; +import jakarta.annotation.Nonnull; -@Configuration -public class ProfileApplicationConfig { +public class ProfileApplicationConfig implements Extension { + @Override + public void install(@Nonnull Jooby app) { - @Bean - public GetProfileUseCase getHomeUseCase(ProfileRepositoryPort repo) { - return new GetProfileUseCase(repo); - } + ProfileRepositoryAdapter adapter = new ProfileRepositoryAdapter(); - @Bean - public CreateProfileUseCase createHomeUseCase(ProfileRepositoryPort repo) { - return new CreateProfileUseCase(repo); + app.getServices().put(ProfileRepositoryPort.class, adapter); + + app.getServices().put(GetProfileUseCase.class, new GetProfileUseCase(adapter)); + app.getServices().put(CreateProfileUseCase.class, new CreateProfileUseCase(adapter)); } } \ No newline at end of file diff --git a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/project/ProjectApplicationConfig.java b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/project/ProjectApplicationConfig.java index 19bf1ba..8685337 100644 --- a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/project/ProjectApplicationConfig.java +++ b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/project/ProjectApplicationConfig.java @@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.project; import com.pablotj.portfolio.application.project.CreateProjectUseCase; import com.pablotj.portfolio.application.project.GetProjectUseCase; import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import com.pablotj.portfolio.infrastructure.persistence.project.adapter.ProjectRepositoryAdapter; +import io.jooby.Extension; +import io.jooby.Jooby; +import jakarta.annotation.Nonnull; -@Configuration -public class ProjectApplicationConfig { +public class ProjectApplicationConfig implements Extension { + @Override + public void install(@Nonnull Jooby app) { - @Bean - public CreateProjectUseCase createProjectUseCase(ProjectRepositoryPort repo) { - return new CreateProjectUseCase(repo); - } + ProjectRepositoryAdapter adapter = new ProjectRepositoryAdapter(); - @Bean - public GetProjectUseCase getProjectUseCase(ProjectRepositoryPort repo) { - return new GetProjectUseCase(repo); + app.getServices().put(ProjectRepositoryPort.class, adapter); + + app.getServices().put(CreateProjectUseCase.class, new CreateProjectUseCase(adapter)); + app.getServices().put(GetProjectUseCase.class, new GetProjectUseCase(adapter)); } } \ No newline at end of file diff --git a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/skill/SkillApplicationConfig.java b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/skill/SkillApplicationConfig.java index b78e847..777f895 100644 --- a/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/skill/SkillApplicationConfig.java +++ b/bootstrap/src/main/java/com/pablotj/portfolio/bootstrap/skill/SkillApplicationConfig.java @@ -3,19 +3,20 @@ package com.pablotj.portfolio.bootstrap.skill; import com.pablotj.portfolio.application.skill.CreateSkillUseCase; import com.pablotj.portfolio.application.skill.GetSkillUseCase; import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import com.pablotj.portfolio.infrastructure.persistence.skill.adapter.SkillRepositoryAdapter; +import io.jooby.Extension; +import io.jooby.Jooby; +import jakarta.annotation.Nonnull; -@Configuration -public class SkillApplicationConfig { +public class SkillApplicationConfig implements Extension { + @Override + public void install(@Nonnull Jooby app) { - @Bean - public GetSkillUseCase getSkillUseCase(SkillRepositoryPort repo) { - return new GetSkillUseCase(repo); - } + SkillRepositoryAdapter adapter = new SkillRepositoryAdapter(); - @Bean - public CreateSkillUseCase createSkillUseCase(SkillRepositoryPort repo) { - return new CreateSkillUseCase(repo); + app.getServices().put(SkillRepositoryPort.class, adapter); + + app.getServices().put(GetSkillUseCase.class, new GetSkillUseCase(adapter)); + app.getServices().put(CreateSkillUseCase.class, new CreateSkillUseCase(adapter)); } } \ No newline at end of file diff --git a/bootstrap/src/main/resources/application.conf b/bootstrap/src/main/resources/application.conf new file mode 100644 index 0000000..8076b74 --- /dev/null +++ b/bootstrap/src/main/resources/application.conf @@ -0,0 +1,60 @@ +# Configuración de la App (Sustituye a info y app) +info { + app { + version = "0.0.1-SNAPSHOT" # Maven no rellena esto automáticamente en Jooby sin plugins extra, mejor ponlo fijo + } +} + +app { + cors { + allowed-origins = ${?APP_ALLOWED_ORIGINS} + } +} + +# Configuración del Servidor +server { + port = 8080 + # En Jooby, el context-path se define habitualmente en la clase App, + # pero puedes usar esta propiedad si la gestionas manualmente. +} + +# Base de Datos (Ebean usa estas propiedades automáticamente) +db { + url = "jdbc:postgresql://"${?DB_HOST}":"${?DB_PORT}"/"${?DB_NAME} + user = ${?DB_USER} + password = ${?DB_PASSWORD} + driverClassName = org.postgresql.Driver + + # HikariCP (Configuración de Pool) + hikari { + maximumPoolSize = 3 + minimumIdle = 1 + idleTimeout = 30000 + connectionTimeout = 10000 + } +} + +flyway { + # Ubicación de tus scripts .sql + locations = ["classpath:db/migration"] + cleanDisabled = true + baselineOnMigrate = true + baselineVersion = ${?FLYWAY_BASELINE_VERSION} + + # Si quieres que se ejecute siempre al arrancar + run = [migrate] +} + +ebean { + ddl { + generate = false + run = false + } + # Mostrar SQL en consola + debug = true +} + +# Jackson +jackson { + indent_output = true +} \ No newline at end of file diff --git a/bootstrap/src/main/resources/application.yml b/bootstrap/src/main/resources/application.yml deleted file mode 100644 index c4e5bd1..0000000 --- a/bootstrap/src/main/resources/application.yml +++ /dev/null @@ -1,54 +0,0 @@ -info: - app: - version: @project.version@ -app: - cors: - allowed-origins: ${APP_ALLOWED_ORIGINS:http://localhost:8080} -spring: - application: - name: portfolio-api - web: - resources: - add-mappings: false - - datasource: - url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:portfolio} - username: ${DB_USER:postgres} - password: ${DB_PASSWORD:postgres} - driver-class-name: org.postgresql.Driver - hikari: - maximum-pool-size: 3 - minimum-idle: 1 - idle-timeout: 30000 - connection-timeout: 10000 - leak-detection-threshold: 10000 - - jpa: - hibernate: - ddl-auto: validate - properties: - hibernate.transaction.jta.platform: org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform - hibernate: - format_sql: true - dialect: org.hibernate.dialect.PostgreSQLDialect - show-sql: true - - jackson: - serialization: - INDENT_OUTPUT: true - -springdoc: - api-docs: - path: /v3/api-docs - swagger-ui: - path: /swagger-ui - -server: - port: 8080 - servlet: - context-path: /api - forward-headers-strategy: framework - - - - diff --git a/domain/pom.xml b/domain/pom.xml index 0b15ee1..247cf86 100644 --- a/domain/pom.xml +++ b/domain/pom.xml @@ -10,11 +10,14 @@ org.projectlombok lombok - true + 1.18.36 + provided + jakarta.validation jakarta.validation-api + 3.0.2 \ No newline at end of file diff --git a/domain/src/main/java/com/pablotj/portfolio/domain/certification/CertificationId.java b/domain/src/main/java/com/pablotj/portfolio/domain/certification/CertificationId.java index eeeece6..fc0c6d9 100644 --- a/domain/src/main/java/com/pablotj/portfolio/domain/certification/CertificationId.java +++ b/domain/src/main/java/com/pablotj/portfolio/domain/certification/CertificationId.java @@ -7,6 +7,7 @@ public record CertificationId(Long profileId, Long certificationId) { } public CertificationId { - if (certificationId != null && certificationId < 0) throw new IllegalArgumentException("CertificationId must be positive"); + if (certificationId != null && certificationId < 0) + throw new IllegalArgumentException("CertificationId must be positive"); } } \ No newline at end of file diff --git a/domain/src/main/java/com/pablotj/portfolio/domain/experience/Achievement.java b/domain/src/main/java/com/pablotj/portfolio/domain/experience/Achievement.java index c7780cb..00e2b0e 100644 --- a/domain/src/main/java/com/pablotj/portfolio/domain/experience/Achievement.java +++ b/domain/src/main/java/com/pablotj/portfolio/domain/experience/Achievement.java @@ -1,6 +1,5 @@ package com.pablotj.portfolio.domain.experience; -import java.time.LocalDate; import lombok.Builder; import lombok.Getter; diff --git a/domain/src/main/java/com/pablotj/portfolio/domain/experience/ExperienceId.java b/domain/src/main/java/com/pablotj/portfolio/domain/experience/ExperienceId.java index 455c3ae..5f50a92 100644 --- a/domain/src/main/java/com/pablotj/portfolio/domain/experience/ExperienceId.java +++ b/domain/src/main/java/com/pablotj/portfolio/domain/experience/ExperienceId.java @@ -7,6 +7,7 @@ public record ExperienceId(Long profileId, Long experienceId) { } public ExperienceId { - if (experienceId != null && experienceId < 0) throw new IllegalArgumentException("ProfileSocialLinkId must be positive"); + if (experienceId != null && experienceId < 0) + throw new IllegalArgumentException("ProfileSocialLinkId must be positive"); } } \ No newline at end of file diff --git a/domain/src/main/java/com/pablotj/portfolio/domain/project/port/ProjectRepositoryPort.java b/domain/src/main/java/com/pablotj/portfolio/domain/project/port/ProjectRepositoryPort.java index ed25062..4dc040c 100644 --- a/domain/src/main/java/com/pablotj/portfolio/domain/project/port/ProjectRepositoryPort.java +++ b/domain/src/main/java/com/pablotj/portfolio/domain/project/port/ProjectRepositoryPort.java @@ -2,12 +2,13 @@ package com.pablotj.portfolio.domain.project.port; import com.pablotj.portfolio.domain.project.Project; import com.pablotj.portfolio.domain.project.ProjectId; - import java.util.List; import java.util.Optional; public interface ProjectRepositoryPort { Project save(Project p); + Optional findById(ProjectId id); + List findAll(Long profileId); } \ No newline at end of file diff --git a/infrastructure/pom.xml b/infrastructure/pom.xml index c66f6d9..242d20e 100644 --- a/infrastructure/pom.xml +++ b/infrastructure/pom.xml @@ -1,12 +1,23 @@ - + + 4.0.0 com.pablotj portfolio-api 0.0.1-SNAPSHOT + infrastructure + + 3.6.1 + 15.8.0 + 1.18.36 + 1.6.3 + + com.pablotj @@ -19,36 +30,71 @@ ${project.version} - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-validation + io.jooby + jooby + ${jooby.version} - org.springframework.boot - spring-boot-starter-security + io.jooby + jooby-ebean + ${jooby.version} + + + + io.jooby + jooby-hibernate-validator + 3.6.1 + + + + io.jooby + jooby-jackson + ${jooby.version} - org.mapstruct mapstruct + ${mapstruct.version} - org.projectlombok lombok - true + ${lombok.version} + provided + + + + jakarta.validation + jakarta.validation-api + 3.0.2 + + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + + + + + io.ebean + ebean-maven-plugin + ${ebean.version} + + + main + process-classes + + enhance + + + + + + \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/CorsConfig.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/CorsConfig.java index 09ed57d..b878a8c 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/CorsConfig.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/CorsConfig.java @@ -1,34 +1,26 @@ package com.pablotj.portfolio.infrastructure.config; +import io.jooby.Extension; +import io.jooby.Jooby; +import io.jooby.handler.Cors; +import io.jooby.handler.CorsHandler; +import jakarta.annotation.Nonnull; import java.util.Arrays; import java.util.List; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -@Configuration -public class CorsConfig { - - @Value("${app.cors.allowed-origins}") - private String allowedOriginsString; - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); +public class CorsConfig implements Extension { + @Override + public void install(@Nonnull Jooby app) { + String allowedOriginsString = app.getConfig().getString("app.cors.allowed-origins"); List allowedOrigins = Arrays.asList(allowedOriginsString.split(",")); - config.setAllowedOriginPatterns(allowedOrigins); - config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); - config.setAllowedHeaders(List.of("*")); - config.setAllowCredentials(true); + Cors cors = new Cors() + .setOrigin(allowedOrigins) + .setMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")) + .setHeaders(Arrays.asList("Content-Type", "Authorization", "X-Requested-With")) + .setUseCredentials(true); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - - return source; + app.use(new CorsHandler(cors)); } -} +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/SecurityConfig.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/SecurityConfig.java index 44a3c9d..e5668b2 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/SecurityConfig.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/config/SecurityConfig.java @@ -1,31 +1,13 @@ package com.pablotj.portfolio.infrastructure.config; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.web.cors.CorsConfigurationSource; +import io.jooby.Extension; +import io.jooby.Jooby; +import jakarta.annotation.Nonnull; -@Configuration -@EnableWebSecurity -public class SecurityConfig { +public class SecurityConfig implements Extension { - private final CorsConfigurationSource corsConfigurationSource; + @Override + public void install(@Nonnull Jooby app) { - public SecurityConfig(CorsConfigurationSource corsConfigurationSource) { - this.corsConfigurationSource = corsConfigurationSource; } - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf(AbstractHttpConfigurer::disable) - .cors(cors -> cors.configurationSource(corsConfigurationSource)) - .authorizeHttpRequests(auth -> auth - .anyRequest().permitAll() - ); - return http.build(); - } -} +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/adapter/CertificationRepositoryAdapter.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/adapter/CertificationRepositoryAdapter.java index 4d00617..2e8e735 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/adapter/CertificationRepositoryAdapter.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/adapter/CertificationRepositoryAdapter.java @@ -3,38 +3,45 @@ package com.pablotj.portfolio.infrastructure.persistence.certification.adapter; import com.pablotj.portfolio.domain.certification.Certification; import com.pablotj.portfolio.domain.certification.CertificationId; import com.pablotj.portfolio.domain.certification.port.CertificationRepositoryPort; -import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.certification.mapper.CertificationJpaMapper; -import com.pablotj.portfolio.infrastructure.persistence.certification.repo.SpringDataCertificationRepository; +import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationEntity; +import com.pablotj.portfolio.infrastructure.persistence.certification.mapper.CertificationEntityMapper; +import io.ebean.DB; +import jakarta.inject.Singleton; import java.util.List; import java.util.Optional; -import org.springframework.stereotype.Repository; +import org.mapstruct.factory.Mappers; -@Repository +@Singleton public class CertificationRepositoryAdapter implements CertificationRepositoryPort { - private final SpringDataCertificationRepository repo; - private final CertificationJpaMapper mapper; - - public CertificationRepositoryAdapter(SpringDataCertificationRepository repo, CertificationJpaMapper mapper) { - this.repo = repo; - this.mapper = mapper; - } + private static final CertificationEntityMapper MAPPER = Mappers.getMapper(CertificationEntityMapper.class); @Override public Certification save(Certification p) { - CertificationJpaEntity entity = mapper.toEntity(p); - CertificationJpaEntity saved = repo.save(entity); - return mapper.toDomain(saved); + CertificationEntity entity = MAPPER.toEntity(p); + DB.save(entity); + return MAPPER.toDomain(entity); } @Override public Optional findById(CertificationId id) { - return repo.findByProfileIdAndId(id.profileId(), id.certificationId()).map(mapper::toDomain); + CertificationEntity entity = DB.find(CertificationEntity.class) + .where() + .eq("profile.id", id.profileId()) + .eq("id", id.certificationId()) + .findOne(); + + return Optional.ofNullable(entity).map(MAPPER::toDomain); } @Override public List findAll(Long profileId) { - return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList(); + return DB.find(CertificationEntity.class) + .where() + .eq("profile.id", profileId) + .findList() + .stream() + .map(MAPPER::toDomain) + .toList(); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/entity/CertificationJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/entity/CertificationEntity.java similarity index 87% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/entity/CertificationJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/entity/CertificationEntity.java index 6dc6fc0..3ddc89c 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/entity/CertificationJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/entity/CertificationEntity.java @@ -1,6 +1,7 @@ package com.pablotj.portfolio.infrastructure.persistence.certification.entity; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity; +import io.ebean.Model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -17,7 +18,7 @@ import lombok.Setter; @Table(name = "CERTIFICATION") @Getter @Setter -public class CertificationJpaEntity { +public class CertificationEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -25,7 +26,7 @@ public class CertificationJpaEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "profile_id", nullable = false) - private ProfileJpaEntity profile; + private ProfileEntity profile; @Column private String name; diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/mapper/CertificationJpaMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/mapper/CertificationEntityMapper.java similarity index 62% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/mapper/CertificationJpaMapper.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/mapper/CertificationEntityMapper.java index 59171af..62c292e 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/mapper/CertificationJpaMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/mapper/CertificationEntityMapper.java @@ -1,18 +1,17 @@ package com.pablotj.portfolio.infrastructure.persistence.certification.mapper; import com.pablotj.portfolio.domain.certification.Certification; -import com.pablotj.portfolio.domain.certification.CertificationId; -import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") -public interface CertificationJpaMapper { +@Mapper +public interface CertificationEntityMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "profile.id", source = "id.profileId") - CertificationJpaEntity toEntity(Certification domain); + CertificationEntity toEntity(Certification domain); @Mapping(target = "id", expression = "java(new CertificationId(e.getProfile().getId(), e.getId()))") - Certification toDomain(CertificationJpaEntity e); + Certification toDomain(CertificationEntity e); } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/repo/SpringDataCertificationRepository.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/repo/SpringDataCertificationRepository.java deleted file mode 100644 index 756d43f..0000000 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/certification/repo/SpringDataCertificationRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.pablotj.portfolio.infrastructure.persistence.certification.repo; - -import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity; -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SpringDataCertificationRepository extends JpaRepository { - - Optional findByProfileIdAndId(Long profileId, Long id); - - List findAllByProfileId(Long profileId); - -} diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/adapter/EducationRepositoryAdapter.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/adapter/EducationRepositoryAdapter.java index 77b9bea..78e7e43 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/adapter/EducationRepositoryAdapter.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/adapter/EducationRepositoryAdapter.java @@ -3,38 +3,45 @@ package com.pablotj.portfolio.infrastructure.persistence.education.adapter; import com.pablotj.portfolio.domain.education.Education; import com.pablotj.portfolio.domain.education.EducationId; import com.pablotj.portfolio.domain.education.port.EducationRepositoryPort; -import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.education.mapper.EducationJpaMapper; -import com.pablotj.portfolio.infrastructure.persistence.education.repo.SpringDataEducationRepository; +import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationEntity; +import com.pablotj.portfolio.infrastructure.persistence.education.mapper.EducationEntityMapper; +import io.ebean.DB; +import jakarta.inject.Singleton; import java.util.List; import java.util.Optional; -import org.springframework.stereotype.Repository; +import org.mapstruct.factory.Mappers; -@Repository +@Singleton public class EducationRepositoryAdapter implements EducationRepositoryPort { - private final SpringDataEducationRepository repo; - private final EducationJpaMapper mapper; - - public EducationRepositoryAdapter(SpringDataEducationRepository repo, EducationJpaMapper mapper) { - this.repo = repo; - this.mapper = mapper; - } + private static final EducationEntityMapper MAPPER = Mappers.getMapper(EducationEntityMapper.class); @Override public Education save(Education p) { - EducationJpaEntity entity = mapper.toEntity(p); - EducationJpaEntity saved = repo.save(entity); - return mapper.toDomain(saved); + EducationEntity entity = MAPPER.toEntity(p); + DB.save(entity); + return MAPPER.toDomain(entity); } @Override public Optional findById(EducationId id) { - return repo.findByProfileIdAndId(id.profileId(), id.educationId()).map(mapper::toDomain); + EducationEntity entity = DB.find(EducationEntity.class) + .where() + .eq("profile.id", id.profileId()) + .eq("id", id.educationId()) + .findOne(); + + return Optional.ofNullable(entity).map(MAPPER::toDomain); } @Override public List findAll(Long profileId) { - return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList(); + return DB.find(EducationEntity.class) + .where() + .eq("profile.id", profileId) + .findList() + .stream() + .map(MAPPER::toDomain) + .toList(); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/entity/EducationJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/entity/EducationEntity.java similarity index 88% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/entity/EducationJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/entity/EducationEntity.java index 754de90..b516723 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/entity/EducationJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/entity/EducationEntity.java @@ -1,6 +1,7 @@ package com.pablotj.portfolio.infrastructure.persistence.education.entity; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity; +import io.ebean.Model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -17,7 +18,7 @@ import lombok.Setter; @Table(name = "EDUCATION") @Getter @Setter -public class EducationJpaEntity { +public class EducationEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -25,7 +26,7 @@ public class EducationJpaEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "profile_id", nullable = false) - private ProfileJpaEntity profile; + private ProfileEntity profile; @Column private String institution; @@ -41,4 +42,4 @@ public class EducationJpaEntity { @Column(columnDefinition = "text") private String description; -} +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/mapper/EducationJpaMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/mapper/EducationEntityMapper.java similarity index 64% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/mapper/EducationJpaMapper.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/mapper/EducationEntityMapper.java index be6e424..d6dd26b 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/mapper/EducationJpaMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/mapper/EducationEntityMapper.java @@ -1,18 +1,17 @@ package com.pablotj.portfolio.infrastructure.persistence.education.mapper; import com.pablotj.portfolio.domain.education.Education; -import com.pablotj.portfolio.domain.education.EducationId; -import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") -public interface EducationJpaMapper { +@Mapper +public interface EducationEntityMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "profile.id", source = "id.profileId") - EducationJpaEntity toEntity(Education domain); + EducationEntity toEntity(Education domain); @Mapping(target = "id", expression = "java(new EducationId(e.getProfile().getId(), e.getId()))") - Education toDomain(EducationJpaEntity e); + Education toDomain(EducationEntity e); } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/repo/SpringDataEducationRepository.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/repo/SpringDataEducationRepository.java deleted file mode 100644 index f990076..0000000 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/education/repo/SpringDataEducationRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.pablotj.portfolio.infrastructure.persistence.education.repo; - -import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity; -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SpringDataEducationRepository extends JpaRepository { - Optional findByProfileIdAndId(Long profileId, Long id); - - List findAllByProfileId(Long profileId); -} diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/adapter/ExperienceRepositoryAdapter.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/adapter/ExperienceRepositoryAdapter.java index 9ff7444..0de3f78 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/adapter/ExperienceRepositoryAdapter.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/adapter/ExperienceRepositoryAdapter.java @@ -3,38 +3,49 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.adapter; import com.pablotj.portfolio.domain.experience.Experience; import com.pablotj.portfolio.domain.experience.ExperienceId; import com.pablotj.portfolio.domain.experience.port.ExperienceRepositoryPort; -import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.experience.mapper.ExperienceJpaMapper; -import com.pablotj.portfolio.infrastructure.persistence.experience.repo.SpringDataExperienceRepository; +import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceEntity; +import com.pablotj.portfolio.infrastructure.persistence.experience.mapper.ExperienceEntityMapper; +import io.ebean.DB; +import jakarta.inject.Singleton; import java.util.List; import java.util.Optional; -import org.springframework.stereotype.Repository; +import org.mapstruct.factory.Mappers; -@Repository +@Singleton public class ExperienceRepositoryAdapter implements ExperienceRepositoryPort { - private final SpringDataExperienceRepository repo; - private final ExperienceJpaMapper mapper; - - public ExperienceRepositoryAdapter(SpringDataExperienceRepository repo, ExperienceJpaMapper mapper) { - this.repo = repo; - this.mapper = mapper; - } + private static final ExperienceEntityMapper MAPPER = Mappers.getMapper(ExperienceEntityMapper.class); @Override public Experience save(Experience p) { - ExperienceJpaEntity entity = mapper.toEntity(p); - ExperienceJpaEntity saved = repo.save(entity); - return mapper.toDomain(saved); + ExperienceEntity entity = MAPPER.toEntity(p); + DB.save(entity); + return MAPPER.toDomain(entity); } @Override public Optional findById(ExperienceId id) { - return repo.findByProfileIdAndId(id.profileId(), id.experienceId()).map(mapper::toDomain); + ExperienceEntity entity = DB.find(ExperienceEntity.class) + .fetch("technologies") + .fetch("achievements") + .where() + .eq("profile.id", id.profileId()) + .eq("id", id.experienceId()) + .findOne(); + + return Optional.ofNullable(entity).map(MAPPER::toDomain); } @Override public List findAll(Long profileId) { - return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList(); + return DB.find(ExperienceEntity.class) + .fetch("technologies") + .fetch("achievements") + .where() + .eq("profile.id", profileId) + .findList() + .stream() + .map(MAPPER::toDomain) + .toList(); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceAchievementJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceAchievementEntity.java similarity index 90% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceAchievementJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceAchievementEntity.java index 521111e..4fc70a3 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceAchievementJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceAchievementEntity.java @@ -1,5 +1,6 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.entity; +import io.ebean.Model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -20,7 +21,7 @@ import lombok.Setter; @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) @Builder -public class ExperienceAchievementJpaEntity { +public class ExperienceAchievementEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceEntity.java similarity index 83% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceEntity.java index 06ee091..3ba05d5 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceEntity.java @@ -1,6 +1,7 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.entity; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity; +import io.ebean.Model; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -27,7 +28,7 @@ import lombok.Setter; @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) @Builder -public class ExperienceJpaEntity { +public class ExperienceEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -35,7 +36,7 @@ public class ExperienceJpaEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "profile_id", nullable = false) - private ProfileJpaEntity profile; + private ProfileEntity profile; @Column private String position; @@ -52,11 +53,12 @@ public class ExperienceJpaEntity { @Column(columnDefinition = "text") private String description; + // Ebean soporta perfectamente CascadeType.ALL y orphanRemoval @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "EXPERIENCE_ID") - private List technologies; + private List technologies; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "EXPERIENCE_ID") - private List achievements; + private List achievements; } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceSkillJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceSkillEntity.java similarity index 90% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceSkillJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceSkillEntity.java index 24a84c9..643ab2e 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceSkillJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/entity/ExperienceSkillEntity.java @@ -1,5 +1,6 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.entity; +import io.ebean.Model; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -19,7 +20,7 @@ import lombok.Setter; @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) @Builder -public class ExperienceSkillJpaEntity { +public class ExperienceSkillEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/mapper/ExperienceJpaMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/mapper/ExperienceEntityMapper.java similarity index 63% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/mapper/ExperienceJpaMapper.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/mapper/ExperienceEntityMapper.java index dbce14f..165245d 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/mapper/ExperienceJpaMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/mapper/ExperienceEntityMapper.java @@ -3,33 +3,33 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.mapper; import com.pablotj.portfolio.domain.experience.Achievement; import com.pablotj.portfolio.domain.experience.Experience; import com.pablotj.portfolio.domain.experience.Technology; -import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceAchievementJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceSkillJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceAchievementEntity; +import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceEntity; +import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceSkillEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") -public interface ExperienceJpaMapper { +@Mapper +public interface ExperienceEntityMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "profile.id", source = "id.profileId") - ExperienceJpaEntity toEntity(Experience domain); + ExperienceEntity toEntity(Experience domain); @Mapping(target = "id", expression = "java(new ExperienceId(entity.getProfile().getId(), entity.getId()))") - Experience toDomain(ExperienceJpaEntity entity); + Experience toDomain(ExperienceEntity entity); @Mapping(target = "id", ignore = true) - ExperienceAchievementJpaEntity toEntity(Achievement entity); + ExperienceAchievementEntity toEntity(Achievement entity); @Mapping(target = "id.value", source = "id") - Achievement toDomain(ExperienceAchievementJpaEntity entity); + Achievement toDomain(ExperienceAchievementEntity entity); @Mapping(target = "id", ignore = true) - ExperienceSkillJpaEntity toEntity(Technology entity); + ExperienceSkillEntity toEntity(Technology entity); @Mapping(target = "id.value", source = "id") - Technology toDomain(ExperienceSkillJpaEntity entity); + Technology toDomain(ExperienceSkillEntity entity); } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/repo/SpringDataExperienceRepository.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/repo/SpringDataExperienceRepository.java deleted file mode 100644 index 3a2361d..0000000 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/experience/repo/SpringDataExperienceRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.pablotj.portfolio.infrastructure.persistence.experience.repo; - -import com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity; -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SpringDataExperienceRepository extends JpaRepository { - Optional findByProfileIdAndId(Long profileId, Long id); - - List findAllByProfileId(Long profileId); -} diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/adapter/ProfileRepositoryAdapter.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/adapter/ProfileRepositoryAdapter.java index b746cc0..7d0df52 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/adapter/ProfileRepositoryAdapter.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/adapter/ProfileRepositoryAdapter.java @@ -3,49 +3,56 @@ package com.pablotj.portfolio.infrastructure.persistence.profile.adapter; import com.pablotj.portfolio.domain.profile.Profile; import com.pablotj.portfolio.domain.profile.ProfileId; import com.pablotj.portfolio.domain.profile.port.ProfileRepositoryPort; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileSocialLinkJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.profile.mapper.ProfileJpaMapper; -import com.pablotj.portfolio.infrastructure.persistence.profile.repo.SpringDataProfileRepository; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.mapper.ProfileEntityMapper; +import io.ebean.DB; +import jakarta.inject.Singleton; import java.util.List; import java.util.Optional; -import org.springframework.stereotype.Repository; +import org.mapstruct.factory.Mappers; -@Repository +@Singleton public class ProfileRepositoryAdapter implements ProfileRepositoryPort { - private final SpringDataProfileRepository repo; - private final ProfileJpaMapper mapper; - - public ProfileRepositoryAdapter(SpringDataProfileRepository repo, ProfileJpaMapper mapper) { - this.repo = repo; - this.mapper = mapper; - } + private static final ProfileEntityMapper MAPPER = Mappers.getMapper(ProfileEntityMapper.class); @Override public Profile save(Profile p) { - ProfileJpaEntity entity = mapper.toEntity(p); - if (entity.getSocial() != null) { - for (ProfileSocialLinkJpaEntity socialLinkJpaEntity : entity.getSocial()) { - socialLinkJpaEntity.setProfile(entity); - } - } - ProfileJpaEntity saved = repo.save(entity); - return mapper.toDomain(saved); + ProfileEntity entity = MAPPER.toEntity(p); + DB.save(entity); + + return MAPPER.toDomain(entity); } @Override public Optional findBySlug(ProfileId id) { - return repo.findBySlug(id.slug()).map(mapper::toDomain); + ProfileEntity entity = DB.find(ProfileEntity.class) + .fetch("social") + .where() + .eq("slug", id.slug()) + .findOne(); + + return Optional.ofNullable(entity).map(MAPPER::toDomain); } @Override public Optional findById(ProfileId id) { - return repo.findById(id.id()).map(mapper::toDomain); + ProfileEntity entity = DB.find(ProfileEntity.class) + .fetch("social") + .where() + .eq("id", id.id()) + .findOne(); + + return Optional.ofNullable(entity).map(MAPPER::toDomain); } @Override public List findAll() { - return repo.findAll().stream().map(mapper::toDomain).toList(); + return DB.find(ProfileEntity.class) + .fetch("social") + .findList() + .stream() + .map(MAPPER::toDomain) + .toList(); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileEntity.java similarity index 85% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileEntity.java index 4de0dda..66ba72d 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileEntity.java @@ -1,5 +1,6 @@ package com.pablotj.portfolio.infrastructure.persistence.profile.entity; +import io.ebean.Model; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -17,7 +18,7 @@ import lombok.Setter; @Table(name = "profile") @Getter @Setter -public class ProfileJpaEntity { +public class ProfileEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -47,10 +48,10 @@ public class ProfileJpaEntity { @Column private String avatar; - @Column + @Column(columnDefinition = "text") private String bio; @OneToMany(mappedBy = "profile", cascade = CascadeType.ALL, orphanRemoval = true) - private List social = new ArrayList<>(); + private List social = new ArrayList<>(); } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileSocialLinkJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileSocialLinkEntity.java similarity index 87% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileSocialLinkJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileSocialLinkEntity.java index a92a078..b82ea32 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileSocialLinkJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/entity/ProfileSocialLinkEntity.java @@ -1,5 +1,6 @@ package com.pablotj.portfolio.infrastructure.persistence.profile.entity; +import io.ebean.Model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -16,14 +17,15 @@ import lombok.Setter; @Table(name = "profile_social_link") @Getter @Setter -public class ProfileSocialLinkJpaEntity { +public class ProfileSocialLinkEntity extends Model { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "profile_id", nullable = false) - private ProfileJpaEntity profile; + private ProfileEntity profile; @Column private String url; diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/mapper/ProfileJpaMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/mapper/ProfileEntityMapper.java similarity index 64% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/mapper/ProfileJpaMapper.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/mapper/ProfileEntityMapper.java index 93182b1..68c826c 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/mapper/ProfileJpaMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/mapper/ProfileEntityMapper.java @@ -2,25 +2,25 @@ package com.pablotj.portfolio.infrastructure.persistence.profile.mapper; import com.pablotj.portfolio.domain.profile.Profile; import com.pablotj.portfolio.domain.profile.ProfileSocialLink; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileSocialLinkJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileSocialLinkEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") -public interface ProfileJpaMapper { +@Mapper +public interface ProfileEntityMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "social", source = "social") - ProfileJpaEntity toEntity(Profile domain); + ProfileEntity toEntity(Profile domain); @Mapping(target = "id.id", source = "id") @Mapping(target = "social", source = "social") - Profile toDomain(ProfileJpaEntity e); + Profile toDomain(ProfileEntity e); @Mapping(target = "id", ignore = true) - ProfileSocialLinkJpaEntity toEntitySocial(ProfileSocialLink entity); + ProfileSocialLinkEntity toEntitySocial(ProfileSocialLink entity); @Mapping(target = "id.value", source = "id") - ProfileSocialLink toDomainSocial(ProfileSocialLinkJpaEntity entity); + ProfileSocialLink toDomainSocial(ProfileSocialLinkEntity entity); } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/repo/SpringDataProfileRepository.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/repo/SpringDataProfileRepository.java deleted file mode 100644 index b5a6ad4..0000000 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/profile/repo/SpringDataProfileRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.pablotj.portfolio.infrastructure.persistence.profile.repo; - -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SpringDataProfileRepository extends JpaRepository { - - Optional findBySlug(String slug); -} diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/adapter/ProjectRepositoryAdapter.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/adapter/ProjectRepositoryAdapter.java index bed1735..29dc783 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/adapter/ProjectRepositoryAdapter.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/adapter/ProjectRepositoryAdapter.java @@ -3,39 +3,50 @@ package com.pablotj.portfolio.infrastructure.persistence.project.adapter; import com.pablotj.portfolio.domain.project.Project; import com.pablotj.portfolio.domain.project.ProjectId; import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort; -import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.project.mapper.ProjectJpaMapper; -import com.pablotj.portfolio.infrastructure.persistence.project.repo.SpringDataProjectRepository; -import org.springframework.stereotype.Repository; - +import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectEntity; +import com.pablotj.portfolio.infrastructure.persistence.project.mapper.ProjectEntityMapper; +import io.ebean.DB; +import jakarta.inject.Singleton; import java.util.List; import java.util.Optional; +import org.mapstruct.factory.Mappers; -@Repository +@Singleton public class ProjectRepositoryAdapter implements ProjectRepositoryPort { - private final SpringDataProjectRepository repo; - private final ProjectJpaMapper mapper; + private static final ProjectEntityMapper MAPPER = Mappers.getMapper(ProjectEntityMapper.class); - public ProjectRepositoryAdapter(SpringDataProjectRepository repo, ProjectJpaMapper mapper) { - this.repo = repo; - this.mapper = mapper; - } @Override public Project save(Project p) { - ProjectJpaEntity entity = mapper.toEntity(p); - ProjectJpaEntity saved = repo.save(entity); - return mapper.toDomain(saved); + ProjectEntity entity = MAPPER.toEntity(p); + DB.save(entity); + return MAPPER.toDomain(entity); } @Override public Optional findById(ProjectId id) { - return repo.findByProfileIdAndId(id.profileId(), id.projectId()).map(mapper::toDomain); + ProjectEntity entity = DB.find(ProjectEntity.class) + .fetch("technologies") + .fetch("features") + .where() + .eq("profile.id", id.profileId()) + .eq("id", id.projectId()) + .findOne(); + + return Optional.ofNullable(entity).map(MAPPER::toDomain); } @Override public List findAll(Long profileId) { - return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList(); + return DB.find(ProjectEntity.class) + .fetch("technologies") + .fetch("features") + .where() + .eq("profile.id", profileId) + .findList() + .stream() + .map(MAPPER::toDomain) + .toList(); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectEntity.java similarity index 81% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectEntity.java index d1e6b0e..02d6359 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectEntity.java @@ -1,6 +1,7 @@ package com.pablotj.portfolio.infrastructure.persistence.project.entity; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity; +import io.ebean.Model; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -18,8 +19,9 @@ import lombok.Setter; @Entity @Table(name = "PROJECT") -@Getter @Setter -public class ProjectJpaEntity { +@Getter +@Setter +public class ProjectEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -27,12 +29,12 @@ public class ProjectJpaEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "profile_id", nullable = false) - private ProfileJpaEntity profile; + private ProfileEntity profile; @Column private String title; - @Column + @Column(columnDefinition = "text") private String description; @Column @@ -40,11 +42,11 @@ public class ProjectJpaEntity { @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "PROJECT_ID") - private List technologies; + private List technologies; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "PROJECT_ID") - private List features; + private List features; @Column private String demo; diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectFeatureJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectFeatureEntity.java similarity index 87% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectFeatureJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectFeatureEntity.java index d85afd3..4e89400 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectFeatureJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectFeatureEntity.java @@ -1,5 +1,6 @@ package com.pablotj.portfolio.infrastructure.persistence.project.entity; +import io.ebean.Model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -13,7 +14,7 @@ import lombok.Setter; @Table(name = "PROJECT_FEATURE") @Getter @Setter -public class ProjectFeatureJpaEntity { +public class ProjectFeatureEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectTechnologyJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectTechnologyEntity.java similarity index 87% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectTechnologyJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectTechnologyEntity.java index 1305dde..20a7725 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectTechnologyJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/entity/ProjectTechnologyEntity.java @@ -1,5 +1,6 @@ package com.pablotj.portfolio.infrastructure.persistence.project.entity; +import io.ebean.Model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -13,7 +14,7 @@ import lombok.Setter; @Table(name = "PROJECT_FEATURE_TECHNOLOGY") @Getter @Setter -public class ProjectTechnologyJpaEntity { +public class ProjectTechnologyEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/mapper/ProjectJpaMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/mapper/ProjectEntityMapper.java similarity index 63% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/mapper/ProjectJpaMapper.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/mapper/ProjectEntityMapper.java index d9d6229..de0c584 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/mapper/ProjectJpaMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/mapper/ProjectEntityMapper.java @@ -3,31 +3,31 @@ package com.pablotj.portfolio.infrastructure.persistence.project.mapper; import com.pablotj.portfolio.domain.project.Project; import com.pablotj.portfolio.domain.project.ProjectFeature; import com.pablotj.portfolio.domain.project.ProjectTechnology; -import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectFeatureJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectTechnologyJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectEntity; +import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectFeatureEntity; +import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectTechnologyEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") -public interface ProjectJpaMapper { +@Mapper +public interface ProjectEntityMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "profile.id", source = "id.profileId") - ProjectJpaEntity toEntity(Project domain); + ProjectEntity toEntity(Project domain); @Mapping(target = "id", expression = "java(new ProjectId(e.getProfile().getId(), e.getId()))") - Project toDomain(ProjectJpaEntity e); + Project toDomain(ProjectEntity e); @Mapping(target = "id", ignore = true) - ProjectTechnologyJpaEntity toEntity(ProjectTechnology entity); + ProjectTechnologyEntity toEntity(ProjectTechnology entity); @Mapping(target = "id.value", source = "id") - ProjectTechnology toDomain(ProjectTechnologyJpaEntity entity); + ProjectTechnology toDomain(ProjectTechnologyEntity entity); @Mapping(target = "id", ignore = true) - ProjectFeatureJpaEntity toEntity(ProjectFeature entity); + ProjectFeatureEntity toEntity(ProjectFeature entity); @Mapping(target = "id.value", source = "id") - ProjectFeature toDomain(ProjectFeatureJpaEntity entity); + ProjectFeature toDomain(ProjectFeatureEntity entity); } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/repo/SpringDataProjectRepository.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/repo/SpringDataProjectRepository.java deleted file mode 100644 index 1a7dd6f..0000000 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/project/repo/SpringDataProjectRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.pablotj.portfolio.infrastructure.persistence.project.repo; - -import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity; -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SpringDataProjectRepository extends JpaRepository { - Optional findByProfileIdAndId(Long profileId, Long id); - - List findAllByProfileId(Long profileId); -} diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/adapter/SkillRepositoryAdapter.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/adapter/SkillRepositoryAdapter.java index e076387..11ce5d4 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/adapter/SkillRepositoryAdapter.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/adapter/SkillRepositoryAdapter.java @@ -3,38 +3,47 @@ package com.pablotj.portfolio.infrastructure.persistence.skill.adapter; import com.pablotj.portfolio.domain.skill.SkillGroup; import com.pablotj.portfolio.domain.skill.SkillGroupId; import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort; -import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.skill.mapper.SkillJpaMapper; -import com.pablotj.portfolio.infrastructure.persistence.skill.repo.SpringDataSkillRepository; +import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupEntity; +import com.pablotj.portfolio.infrastructure.persistence.skill.mapper.SkillMapper; +import io.ebean.DB; +import jakarta.inject.Singleton; import java.util.List; import java.util.Optional; -import org.springframework.stereotype.Repository; +import org.mapstruct.factory.Mappers; -@Repository +@Singleton public class SkillRepositoryAdapter implements SkillRepositoryPort { - private final SpringDataSkillRepository repo; - private final SkillJpaMapper mapper; - - public SkillRepositoryAdapter(SpringDataSkillRepository repo, SkillJpaMapper mapper) { - this.repo = repo; - this.mapper = mapper; - } + private static final SkillMapper MAPPER = Mappers.getMapper(SkillMapper.class); @Override public SkillGroup save(SkillGroup p) { - SkillGroupJpaEntity entity = mapper.toEntity(p); - SkillGroupJpaEntity saved = repo.save(entity); - return mapper.toDomain(saved); + SkillGroupEntity entity = MAPPER.toEntity(p); + DB.save(entity); + return MAPPER.toDomain(entity); } @Override public Optional findById(SkillGroupId id) { - return repo.findByProfileIdAndId(id.profileId(), id.skillGroupId()).map(mapper::toDomain); + SkillGroupEntity entity = DB.find(SkillGroupEntity.class) + .fetch("skills") // Carga inmediata de la lista de habilidades + .where() + .eq("profile.id", id.profileId()) + .eq("id", id.skillGroupId()) + .findOne(); + + return Optional.ofNullable(entity).map(MAPPER::toDomain); } @Override public List findAll(Long profileId) { - return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList(); + return DB.find(SkillGroupEntity.class) + .fetch("skills") + .where() + .eq("profile.id", profileId) + .findList() + .stream() + .map(MAPPER::toDomain) + .toList(); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillEntity.java similarity index 90% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillEntity.java index 0550e79..a4c708d 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillEntity.java @@ -1,5 +1,6 @@ package com.pablotj.portfolio.infrastructure.persistence.skill.entity; +import io.ebean.Model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -13,7 +14,7 @@ import lombok.Setter; @Table(name = "SKILL") @Getter @Setter -public class SkillJpaEntity { +public class SkillEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillGroupJpaEntity.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillGroupEntity.java similarity index 86% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillGroupJpaEntity.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillGroupEntity.java index dbd431e..f19f521 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillGroupJpaEntity.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/entity/SkillGroupEntity.java @@ -1,6 +1,7 @@ package com.pablotj.portfolio.infrastructure.persistence.skill.entity; -import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileEntity; +import io.ebean.Model; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -20,7 +21,7 @@ import lombok.Setter; @Table(name = "SKILL_GROUP") @Getter @Setter -public class SkillGroupJpaEntity { +public class SkillGroupEntity extends Model { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -28,7 +29,7 @@ public class SkillGroupJpaEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "profile_id", nullable = false) - private ProfileJpaEntity profile; + private ProfileEntity profile; @Column private String name; @@ -38,5 +39,5 @@ public class SkillGroupJpaEntity { @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "SKILL_ID") - private List skills; + private List skills; } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/mapper/SkillJpaMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/mapper/SkillMapper.java similarity index 68% rename from infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/mapper/SkillJpaMapper.java rename to infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/mapper/SkillMapper.java index 72f893b..e39901f 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/mapper/SkillJpaMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/mapper/SkillMapper.java @@ -2,24 +2,24 @@ package com.pablotj.portfolio.infrastructure.persistence.skill.mapper; import com.pablotj.portfolio.domain.skill.Skill; import com.pablotj.portfolio.domain.skill.SkillGroup; -import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillJpaEntity; +import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillEntity; +import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") -public interface SkillJpaMapper { +@Mapper +public interface SkillMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "profile.id", source = "id.profileId") - SkillGroupJpaEntity toEntity(SkillGroup domain); + SkillGroupEntity toEntity(SkillGroup domain); @Mapping(target = "id", expression = "java(new SkillGroupId(entity.getProfile().getId(), entity.getId()))") - SkillGroup toDomain(SkillGroupJpaEntity entity); + SkillGroup toDomain(SkillGroupEntity entity); @Mapping(target = "id", ignore = true) - SkillJpaEntity toEntity(Skill entity); + SkillEntity toEntity(Skill entity); @Mapping(target = "id.value", source = "id") - Skill toDomain(SkillJpaEntity entity); + Skill toDomain(SkillEntity entity); } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/repo/SpringDataSkillRepository.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/repo/SpringDataSkillRepository.java deleted file mode 100644 index 9db0dc3..0000000 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/persistence/skill/repo/SpringDataSkillRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.pablotj.portfolio.infrastructure.persistence.skill.repo; - -import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity; -import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillGroupJpaEntity; -import java.util.List; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SpringDataSkillRepository extends JpaRepository { - Optional findByProfileIdAndId(Long profileId, Long id); - - List findAllByProfileId(Long profileId); -} diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiErrorController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiErrorController.java index 87df85f..09bf7b7 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiErrorController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiErrorController.java @@ -1,30 +1,28 @@ package com.pablotj.portfolio.infrastructure.rest.api; -import org.springframework.boot.web.error.ErrorAttributeOptions; -import org.springframework.boot.web.servlet.error.ErrorAttributes; -import org.springframework.boot.web.servlet.error.ErrorController; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.context.request.WebRequest; - +import io.jooby.ErrorHandler; +import java.util.LinkedHashMap; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -@Controller -public class ApiErrorController implements ErrorController { +public class ApiErrorController { - private final ErrorAttributes errorAttributes; + private static final Logger log = LoggerFactory.getLogger(ApiErrorController.class); - public ApiErrorController(ErrorAttributes errorAttributes) { - this.errorAttributes = errorAttributes; - } + public static ErrorHandler getHandler() { + return (ctx, cause, statusCode) -> { + log.error("Error en la API: {}", cause.getMessage(), cause); - @RequestMapping("/error") - public ResponseEntity> handleError(WebRequest webRequest) { - Map attributes = errorAttributes.getErrorAttributes(webRequest, - ErrorAttributeOptions.defaults()); - HttpStatus status = HttpStatus.valueOf((int) attributes.getOrDefault("status", 500)); - return new ResponseEntity<>(attributes, status); + Map errorAttributes = new LinkedHashMap<>(); + errorAttributes.put("timestamp", System.currentTimeMillis()); + errorAttributes.put("status", statusCode.value()); + errorAttributes.put("error", statusCode.reason()); + errorAttributes.put("message", cause.getMessage()); + errorAttributes.put("path", ctx.getRequestPath()); + + ctx.setResponseCode(statusCode) + .render(errorAttributes); + }; } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiRootController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiRootController.java index 4200d84..45f9e0f 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiRootController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/api/ApiRootController.java @@ -1,39 +1,39 @@ package com.pablotj.portfolio.infrastructure.rest.api; +import io.jooby.annotation.GET; +import io.jooby.annotation.Path; +import jakarta.inject.Inject; +import jakarta.inject.Named; import java.util.List; import java.util.Map; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -@RestController -@RequestMapping +@Path("/") public class ApiRootController { - @Value("${info.app.version}") - private String appVersion; + private final String appVersion; - @GetMapping - public ResponseEntity> root() { - Map response = Map.of( + @Inject + public ApiRootController(@Named("info.app.version") String appVersion) { + this.appVersion = appVersion; + } + + @GET + public Map root() { + return Map.of( "api", "Portfolio API", "version", appVersion, "doc", "/v3/api-docs", "swagger", "/swagger-ui", "endpoints", List.of( - Map.of("path", "/v1/homes", "description", "Manage projects"), - Map.of("path", "/v1/certifications", "description", "Manage projects"), + Map.of("path", "/v1/homes", "description", "Manage home details"), + Map.of("path", "/v1/certifications", "description", "Manage certifications"), Map.of("path", "/v1/projects", "description", "Manage projects"), - Map.of("path", "/v1/contacts", "description", "Manage projects"), - Map.of("path", "/v1/educations", "description", "Manage projects"), - Map.of("path", "/v1/experiences", "description", "Manage projects"), - Map.of("path", "/v1/projects", "description", "Manage projects"), - Map.of("path", "/v1/technologies", "description", "ProfileSocialLink entries") + Map.of("path", "/v1/contacts", "description", "Manage contact info"), + Map.of("path", "/v1/educations", "description", "Manage education"), + Map.of("path", "/v1/experiences", "description", "Manage experience"), + Map.of("path", "/v1/skills", "description", "Manage skills"), + Map.of("path", "/v1/technologies", "description", "Manage technologies") ) ); - - return ResponseEntity.ok(response); } -} +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/CertificationController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/CertificationController.java index ebe9de7..f8f5dfd 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/CertificationController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/CertificationController.java @@ -5,54 +5,53 @@ import com.pablotj.portfolio.application.certification.GetCertificationUseCase; import com.pablotj.portfolio.infrastructure.rest.certification.dto.CertificationDto; import com.pablotj.portfolio.infrastructure.rest.certification.dto.CreateCertificationRequest; import com.pablotj.portfolio.infrastructure.rest.certification.mapper.CertificationRestMapper; +import io.jooby.annotation.GET; +import io.jooby.annotation.POST; +import io.jooby.annotation.Path; +import io.jooby.annotation.PathParam; +import jakarta.inject.Inject; import jakarta.validation.Valid; -import java.net.URI; import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RestController; +import org.mapstruct.factory.Mappers; -@RestController -@RequestMapping("/v1/profiles/{profileId}/certifications") +@Path("/v1/profiles/{profileId}/certifications") public class CertificationController { + private static final CertificationRestMapper MAPPER = Mappers.getMapper(CertificationRestMapper.class); private final CreateCertificationUseCase createUC; private final GetCertificationUseCase getUC; - private final CertificationRestMapper mapper; - public CertificationController(CreateCertificationUseCase createUC, GetCertificationUseCase getUC, CertificationRestMapper mapper) { + @Inject + public CertificationController(CreateCertificationUseCase createUC, GetCertificationUseCase getUC) { this.createUC = createUC; this.getUC = getUC; - this.mapper = mapper; } - @GetMapping - public List all(@PathVariable Long profileId) { - return getUC.all(profileId).stream().map(mapper::toDto).toList(); + @GET + public List all(@PathParam Long profileId) { + return getUC.all(profileId).stream() + .map(MAPPER::toDto) + .toList(); } - @GetMapping("/{id}") - public ResponseEntity byId(@PathVariable Long profileId, @PathVariable Long id) { + @GET("/{id}") + public CertificationDto byId(@PathParam Long profileId, @PathParam Long id) { + // En Jooby, si devuelves un Optional vacío, automáticamente lanza un 404 return getUC.byId(profileId, id) - .map(mapper::toDto) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + .map(MAPPER::toDto) + .orElseThrow(() -> new io.jooby.exception.NotFoundException("Certification not found")); } - @PostMapping - public ResponseEntity create(@PathVariable Long profileId, @Valid @RequestBody CreateCertificationRequest request) { + @POST + public CertificationDto create(@PathParam Long profileId, @Valid CreateCertificationRequest request) { var cmd = new CreateCertificationUseCase.Command( request.name(), request.issuer(), request.date(), request.credentialId() ); + var created = createUC.handle(profileId, cmd); - var body = mapper.toDto(created); - return ResponseEntity.created(URI.create("/api/certifications/" + body.id())).body(body); + return MAPPER.toDto(created); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/mapper/CertificationRestMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/mapper/CertificationRestMapper.java index 9d52cfc..ea979ca 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/mapper/CertificationRestMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/certification/mapper/CertificationRestMapper.java @@ -5,7 +5,7 @@ import com.pablotj.portfolio.infrastructure.rest.certification.dto.Certification import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") +@Mapper public interface CertificationRestMapper { @Mapping(target = "id", source = "id.certificationId") diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/EducationController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/EducationController.java index 7c2a4fd..209a9ca 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/EducationController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/EducationController.java @@ -5,46 +5,45 @@ import com.pablotj.portfolio.application.education.GetEducationUseCase; import com.pablotj.portfolio.infrastructure.rest.education.dto.CreateEducationRequest; import com.pablotj.portfolio.infrastructure.rest.education.dto.EducationDto; import com.pablotj.portfolio.infrastructure.rest.education.mapper.EducationRestMapper; +import io.jooby.annotation.GET; +import io.jooby.annotation.POST; +import io.jooby.annotation.Path; +import io.jooby.annotation.PathParam; +import io.jooby.exception.NotFoundException; +import jakarta.inject.Inject; import jakarta.validation.Valid; -import java.net.URI; import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RestController; +import org.mapstruct.factory.Mappers; -@RestController -@RequestMapping("/v1/profiles/{profileId}/education") +@Path("/v1/profiles/{profileId}/education") public class EducationController { + private static final EducationRestMapper MAPPER = Mappers.getMapper(EducationRestMapper.class); private final CreateEducationUseCase createUC; private final GetEducationUseCase getUC; - private final EducationRestMapper mapper; - public EducationController(CreateEducationUseCase createUC, GetEducationUseCase getUC, EducationRestMapper mapper) { + @Inject + public EducationController(CreateEducationUseCase createUC, GetEducationUseCase getUC) { this.createUC = createUC; this.getUC = getUC; - this.mapper = mapper; } - @GetMapping - public List all(@PathVariable Long profileId) { - return getUC.all(profileId).stream().map(mapper::toDto).toList(); + @GET + public List all(@PathParam Long profileId) { + return getUC.all(profileId).stream() + .map(MAPPER::toDto) + .toList(); } - @GetMapping("/{id}") - public ResponseEntity byId(@PathVariable Long profileId, @PathVariable Long id) { + @GET("/{id}") + public EducationDto byId(@PathParam Long profileId, @PathParam Long id) { return getUC.byId(profileId, id) - .map(mapper::toDto) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + .map(MAPPER::toDto) + .orElseThrow(() -> new NotFoundException("Education record not found")); } - @PostMapping - public ResponseEntity create(@PathVariable Long profileId, @Valid @RequestBody CreateEducationRequest request) { + @POST + public EducationDto create(@PathParam Long profileId, @Valid CreateEducationRequest request) { var cmd = new CreateEducationUseCase.Command( request.institution(), request.degree(), @@ -52,8 +51,8 @@ public class EducationController { request.grade(), request.description() ); + var created = createUC.handle(profileId, cmd); - var body = mapper.toDto(created); - return ResponseEntity.created(URI.create("/api/educations/" + body.id())).body(body); + return MAPPER.toDto(created); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/mapper/EducationRestMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/mapper/EducationRestMapper.java index 6358297..2e78ec5 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/mapper/EducationRestMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/education/mapper/EducationRestMapper.java @@ -5,7 +5,7 @@ import com.pablotj.portfolio.infrastructure.rest.education.dto.EducationDto; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") +@Mapper public interface EducationRestMapper { @Mapping(target = "id", source = "id.educationId") diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/ExperienceController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/ExperienceController.java index aceac1b..0c4fb4e 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/ExperienceController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/ExperienceController.java @@ -5,46 +5,45 @@ import com.pablotj.portfolio.application.experience.GetExperienceUseCase; import com.pablotj.portfolio.infrastructure.rest.experience.dto.CreateExperienceRequest; import com.pablotj.portfolio.infrastructure.rest.experience.dto.ExperienceDto; import com.pablotj.portfolio.infrastructure.rest.experience.mapper.ExperienceRestMapper; +import io.jooby.annotation.GET; +import io.jooby.annotation.POST; +import io.jooby.annotation.Path; +import io.jooby.annotation.PathParam; +import io.jooby.exception.NotFoundException; +import jakarta.inject.Inject; import jakarta.validation.Valid; -import java.net.URI; import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RestController; +import org.mapstruct.factory.Mappers; -@RestController -@RequestMapping("/v1/profiles/{profileId}/experience") +@Path("/v1/profiles/{profileId}/experience") public class ExperienceController { + private static final ExperienceRestMapper MAPPER = Mappers.getMapper(ExperienceRestMapper.class); private final CreateExperienceUseCase createUC; private final GetExperienceUseCase getUC; - private final ExperienceRestMapper mapper; - public ExperienceController(CreateExperienceUseCase createUC, GetExperienceUseCase getUC, ExperienceRestMapper mapper) { + @Inject + public ExperienceController(CreateExperienceUseCase createUC, GetExperienceUseCase getUC) { this.createUC = createUC; this.getUC = getUC; - this.mapper = mapper; } - @GetMapping - public List all(@PathVariable Long profileId) { - return getUC.all(profileId).stream().map(mapper::toDto).toList(); + @GET + public List all(@PathParam Long profileId) { + return getUC.all(profileId).stream() + .map(MAPPER::toDto) + .toList(); } - @GetMapping("/{id}") - public ResponseEntity byId(@PathVariable Long profileId, @PathVariable Long id) { + @GET("/{id}") + public ExperienceDto byId(@PathParam Long profileId, @PathParam Long id) { return getUC.byId(profileId, id) - .map(mapper::toDto) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + .map(MAPPER::toDto) + .orElseThrow(() -> new NotFoundException("Experience not found")); } - @PostMapping - public ResponseEntity create(@PathVariable Long profileId, @Valid @RequestBody CreateExperienceRequest request) { + @POST + public ExperienceDto create(@PathParam Long profileId, @Valid CreateExperienceRequest request) { var cmd = new CreateExperienceUseCase.Command( request.company(), request.position(), @@ -54,8 +53,8 @@ public class ExperienceController { request.technologies(), request.achievements() ); + var created = createUC.handle(profileId, cmd); - var body = mapper.toDto(created); - return ResponseEntity.created(URI.create("/api/experiences/" + body.id())).body(body); + return MAPPER.toDto(created); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/CreateExperienceRequest.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/CreateExperienceRequest.java index 89404af..b40bef1 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/CreateExperienceRequest.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/CreateExperienceRequest.java @@ -11,4 +11,5 @@ public record CreateExperienceRequest( String description, List technologies, List achievements -) {} \ No newline at end of file +) { +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/ExperienceDto.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/ExperienceDto.java index 81450c1..3f16767 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/ExperienceDto.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/dto/ExperienceDto.java @@ -12,4 +12,5 @@ public record ExperienceDto( String description, List technologies, List achievements -) {} \ No newline at end of file +) { +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/mapper/ExperienceRestMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/mapper/ExperienceRestMapper.java index 3d67b24..068bf39 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/mapper/ExperienceRestMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/experience/mapper/ExperienceRestMapper.java @@ -8,7 +8,7 @@ import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") +@Mapper public interface ExperienceRestMapper { @Mapping(target = "id", source = "id.experienceId") diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/ProfileController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/ProfileController.java index c7de454..4eecf57 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/ProfileController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/ProfileController.java @@ -5,47 +5,45 @@ import com.pablotj.portfolio.application.profile.GetProfileUseCase; import com.pablotj.portfolio.infrastructure.rest.profile.dto.ProfileCreateRequest; import com.pablotj.portfolio.infrastructure.rest.profile.dto.ProfileDto; import com.pablotj.portfolio.infrastructure.rest.profile.mapper.ProfileRestMapper; +import io.jooby.annotation.GET; +import io.jooby.annotation.POST; +import io.jooby.annotation.Path; +import io.jooby.annotation.PathParam; +import io.jooby.exception.NotFoundException; +import jakarta.inject.Inject; import jakarta.validation.Valid; -import java.net.URI; import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RestController; +import org.mapstruct.factory.Mappers; -@RestController -@RequestMapping("/v1/profiles") +@Path("/v1/profiles") public class ProfileController { + private static final ProfileRestMapper MAPPER = Mappers.getMapper(ProfileRestMapper.class); private final CreateProfileUseCase createUC; private final GetProfileUseCase getUC; - private final ProfileRestMapper mapper; - public ProfileController(CreateProfileUseCase createUC, GetProfileUseCase getUC, ProfileRestMapper mapper) { + @Inject + public ProfileController(CreateProfileUseCase createUC, GetProfileUseCase getUC) { this.createUC = createUC; this.getUC = getUC; - this.mapper = mapper; } - - @GetMapping + @GET public List all() { - return getUC.all().stream().map(mapper::toDto).toList(); + return getUC.all().stream() + .map(MAPPER::toDto) + .toList(); } - @GetMapping("/{slug}") - public ResponseEntity byId(@PathVariable String slug) { + @GET("/{slug}") + public ProfileDto byId(@PathParam String slug) { return getUC.bySlug(slug) - .map(mapper::toDto) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + .map(MAPPER::toDto) + .orElseThrow(() -> new NotFoundException("Profile not found")); } - @PostMapping - public ResponseEntity create(@Valid @RequestBody ProfileCreateRequest request) { + @POST + public ProfileDto create(@Valid ProfileCreateRequest request) { var cmd = new CreateProfileUseCase.Command( request.slug(), request.name(), @@ -56,10 +54,12 @@ public class ProfileController { request.location(), request.avatar(), request.bio(), - request.social().stream().map(l -> new CreateProfileUseCase.Link(l.platform(), l.url())).toList() + request.social().stream() + .map(l -> new CreateProfileUseCase.Link(l.platform(), l.url())) + .toList() ); + var created = createUC.handle(cmd); - var body = mapper.toDto(created); - return ResponseEntity.created(URI.create("/api/homes/" + body.id())).body(body); + return MAPPER.toDto(created); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/mapper/ProfileRestMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/mapper/ProfileRestMapper.java index 8a81b29..c6138f6 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/mapper/ProfileRestMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/profile/mapper/ProfileRestMapper.java @@ -8,7 +8,7 @@ import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") +@Mapper public interface ProfileRestMapper { @Mapping(target = "id", source = "id.id") diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/ProjectController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/ProjectController.java index e22f0af..245cde0 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/ProjectController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/ProjectController.java @@ -5,46 +5,45 @@ import com.pablotj.portfolio.application.project.GetProjectUseCase; import com.pablotj.portfolio.infrastructure.rest.project.dto.CreateProjectRequest; import com.pablotj.portfolio.infrastructure.rest.project.dto.ProjectDto; import com.pablotj.portfolio.infrastructure.rest.project.mapper.ProjectRestMapper; +import io.jooby.annotation.GET; +import io.jooby.annotation.POST; +import io.jooby.annotation.Path; +import io.jooby.annotation.PathParam; +import io.jooby.exception.NotFoundException; +import jakarta.inject.Inject; import jakarta.validation.Valid; -import java.net.URI; import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RestController; +import org.mapstruct.factory.Mappers; -@RestController -@RequestMapping("/v1/profiles/{profileId}/projects") +@Path("/v1/profiles/{profileId}/projects") public class ProjectController { + private static final ProjectRestMapper MAPPER = Mappers.getMapper(ProjectRestMapper.class); private final CreateProjectUseCase createUC; private final GetProjectUseCase getUC; - private final ProjectRestMapper mapper; - public ProjectController(CreateProjectUseCase createUC, GetProjectUseCase getUC, ProjectRestMapper mapper) { + @Inject + public ProjectController(CreateProjectUseCase createUC, GetProjectUseCase getUC) { this.createUC = createUC; this.getUC = getUC; - this.mapper = mapper; } - @GetMapping - public List all(@PathVariable Long profileId) { - return getUC.all(profileId).stream().map(mapper::toDto).toList(); + @GET + public List all(@PathParam Long profileId) { + return getUC.all(profileId).stream() + .map(MAPPER::toDto) + .toList(); } - @GetMapping("/{id}") - public ResponseEntity byId(@PathVariable Long profileId, @PathVariable Long id) { + @GET("/{id}") + public ProjectDto byId(@PathParam Long profileId, @PathParam Long id) { return getUC.byId(profileId, id) - .map(mapper::toDto) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + .map(MAPPER::toDto) + .orElseThrow(() -> new NotFoundException("Project not found")); } - @PostMapping - public ResponseEntity create(@PathVariable Long profileId, @Valid @RequestBody CreateProjectRequest request) { + @POST + public ProjectDto create(@PathParam Long profileId, @Valid CreateProjectRequest request) { var cmd = new CreateProjectUseCase.Command( request.title(), request.description(), @@ -54,8 +53,8 @@ public class ProjectController { request.demo(), request.repository() ); + var created = createUC.handle(profileId, cmd); - var body = mapper.toDto(created); - return ResponseEntity.created(URI.create("/api/projects/" + body.id())).body(body); + return MAPPER.toDto(created); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/dto/CreateProjectRequest.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/dto/CreateProjectRequest.java index 2553b4e..85ce69b 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/dto/CreateProjectRequest.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/dto/CreateProjectRequest.java @@ -10,4 +10,5 @@ public record CreateProjectRequest( List features, String demo, String repository -) {} \ No newline at end of file +) { +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/mapper/ProjectRestMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/mapper/ProjectRestMapper.java index b581c8d..565308f 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/mapper/ProjectRestMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/project/mapper/ProjectRestMapper.java @@ -8,7 +8,7 @@ import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") +@Mapper public interface ProjectRestMapper { @Mapping(target = "id", source = "id.projectId") diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/SkillController.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/SkillController.java index 7a9d0c4..b58e0a3 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/SkillController.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/SkillController.java @@ -5,53 +5,54 @@ import com.pablotj.portfolio.application.skill.GetSkillUseCase; import com.pablotj.portfolio.infrastructure.rest.skill.dto.CreateSkillGroupRequest; import com.pablotj.portfolio.infrastructure.rest.skill.dto.SkillGroupDto; import com.pablotj.portfolio.infrastructure.rest.skill.mapper.SkillRestMapper; +import io.jooby.annotation.GET; +import io.jooby.annotation.POST; +import io.jooby.annotation.Path; +import io.jooby.annotation.PathParam; +import io.jooby.exception.NotFoundException; +import jakarta.inject.Inject; import jakarta.validation.Valid; -import java.net.URI; import java.util.List; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -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.RestController; +import org.mapstruct.factory.Mappers; -@RestController -@RequestMapping("/v1/profiles/{profileId}/skills") +@Path("/v1/profiles/{profileId}/skills") public class SkillController { + private static final SkillRestMapper MAPPER = Mappers.getMapper(SkillRestMapper.class); private final CreateSkillUseCase createUC; private final GetSkillUseCase getUC; - private final SkillRestMapper mapper; - public SkillController(CreateSkillUseCase createUC, GetSkillUseCase getUC, SkillRestMapper mapper) { + @Inject + public SkillController(CreateSkillUseCase createUC, GetSkillUseCase getUC) { this.createUC = createUC; this.getUC = getUC; - this.mapper = mapper; } - @GetMapping - public List all(@PathVariable Long profileId) { - return getUC.all(profileId).stream().map(mapper::toDto).toList(); + @GET + public List all(@PathParam Long profileId) { + return getUC.all(profileId).stream() + .map(MAPPER::toDto) + .toList(); } - @GetMapping("/{id}") - public ResponseEntity byId(@PathVariable Long profileId, @PathVariable Long id) { + @GET("/{id}") + public SkillGroupDto byId(@PathParam Long profileId, @PathParam Long id) { return getUC.byId(profileId, id) - .map(mapper::toDto) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + .map(MAPPER::toDto) + .orElseThrow(() -> new NotFoundException("Skill group not found")); } - @PostMapping - public ResponseEntity create(@PathVariable Long profileId, @Valid @RequestBody CreateSkillGroupRequest request) { + @POST + public SkillGroupDto create(@PathParam Long profileId, @Valid CreateSkillGroupRequest request) { var cmd = new CreateSkillUseCase.CommandGroup( request.name(), request.icon(), - request.skills().stream().map(s -> new CreateSkillUseCase.CommandSkill(s.name(), s.level(), s.years())).toList() + request.skills().stream() + .map(s -> new CreateSkillUseCase.CommandSkill(s.name(), s.level(), s.years())) + .toList() ); + var created = createUC.handle(profileId, cmd); - var body = mapper.toDto(created); - return ResponseEntity.created(URI.create("/api/skills/" + body.id())).body(body); + return MAPPER.toDto(created); } } \ No newline at end of file diff --git a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/mapper/SkillRestMapper.java b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/mapper/SkillRestMapper.java index 7ac3540..740e191 100644 --- a/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/mapper/SkillRestMapper.java +++ b/infrastructure/src/main/java/com/pablotj/portfolio/infrastructure/rest/skill/mapper/SkillRestMapper.java @@ -7,7 +7,7 @@ import com.pablotj.portfolio.infrastructure.rest.skill.dto.SkillGroupDto; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(componentModel = "spring") +@Mapper public interface SkillRestMapper { @Mapping(target = "id", source = "id.skillGroupId") diff --git a/infrastructure/src/main/resources/db/migration/V1__Initial_Setup.sql b/infrastructure/src/main/resources/db/migration/V1__Initial_Setup.sql new file mode 100644 index 0000000..2be014b --- /dev/null +++ b/infrastructure/src/main/resources/db/migration/V1__Initial_Setup.sql @@ -0,0 +1,168 @@ +create table personal +( + id bigint not null + primary key, + avatar varchar(255), + bio varchar(255), + email varchar(255), + location varchar(255), + name varchar(255), + phone varchar(255), + subtitle varchar(255), + title varchar(255) +); + +create table personal_social_link +( + id bigint not null + primary key, + platform varchar(255), + url varchar(255), + personal_id bigint not null + constraint fkfh1pbfvvg3palcr1yip6jffik + references personal +); + +create table profile +( + id bigint not null + primary key, + avatar varchar(255), + bio varchar(4000), + email varchar(255), + location varchar(255), + name varchar(255), + phone varchar(255), + slug varchar(255), + subtitle varchar(255), + title varchar(255) +); + +create table certification +( + id bigint not null + primary key, + credential_id varchar(255), + date varchar(255), + issuer varchar(255), + name varchar(255), + profile_id bigint not null + constraint fko6ve4ysx15lc2vcjt84sal1yc + references profile +); + +create table education +( + id bigint not null + primary key, + degree varchar(255), + description varchar, + grade varchar(255), + institution varchar(255), + period varchar(255), + profile_id bigint not null + constraint fkelocxwwcyf5acj85hgke1c0fl + references profile +); + +create table experience +( + id bigint not null + primary key, + company varchar(255), + description varchar, + location varchar(255), + period varchar(255), + position varchar(255), + profile_id bigint not null + constraint fkhlkosu9yvtv1ptp01x4tfh9ut + references profile +); + +create table experience_achievement +( + id bigint not null + primary key, + description varchar, + experience_id bigint + constraint fk94xrk6stofkung8skwplo29nd + references experience +); + +create table experience_skill +( + id bigint not null + primary key, + name varchar(255), + experience_id bigint + constraint fkpr3jdfjjlaubuayoafpwyx2al + references experience +); + +create table profile_social_link +( + id bigint not null + primary key, + platform varchar(255), + url varchar(255), + profile_id bigint not null + constraint fkqfxt1g0xm211i7qjnlcuqfes9 + references profile +); + +create table project +( + id bigint not null + primary key, + demo varchar(255), + description varchar(255), + image varchar(255), + repository varchar(255), + title varchar(255), + profile_id bigint not null + constraint fk2i9umkiuu36osx3afamsxq39h + references profile +); + +create table project_feature +( + id bigint not null + primary key, + name varchar(255), + project_id bigint + constraint fkdifppyvrfito5in15ox4db0up + references project +); + +create table project_feature_technology +( + id bigint not null + primary key, + name varchar(255), + project_id bigint + constraint fk15krsajtovetpg5vsaqj3icwf + references project +); + +create table skill_group +( + id bigint not null + primary key, + icon varchar(255), + name varchar(255), + profile_id bigint not null + constraint fko26hcvag49ctl3ciddsqm6mn1 + references profile +); + +create table skill +( + id bigint not null + primary key, + level integer, + name varchar(255), + years integer, + skill_id bigint + constraint fki819li5g5cp5qbsyenhr3kmef + references skill_group +); diff --git a/infrastructure/src/main/resources/ddl.sql b/infrastructure/src/main/resources/ddl.sql deleted file mode 100644 index 48fe643..0000000 --- a/infrastructure/src/main/resources/ddl.sql +++ /dev/null @@ -1,140 +0,0 @@ -create table certification -( - id bigint generated by default as identity, - credential_id varchar(255), - date varchar(255), - issuer varchar(255), - name varchar(255), - primary key (id) -); - -create table education -( - id bigint generated by default as identity, - degree varchar(255), - description text, - grade varchar(255), - institution varchar(255), - period varchar(255), - primary key (id) -); - -create table experience -( - id bigint generated by default as identity, - company varchar(255), - description text, - location varchar(255), - period varchar(255), - position varchar(255), - primary key (id) -); - -create table experience_achievement -( - id bigint generated by default as identity, - description text, - experience_id bigint, - primary key (id) -); - -create table experience_skill -( - id bigint generated by default as identity, - name varchar(255), - experience_id bigint, - primary key (id) -); - -create table personal -( - id bigint generated by default as identity, - avatar varchar(255), - bio varchar(255), - email varchar(255), - location varchar(255), - name varchar(255), - phone varchar(255), - subtitle varchar(255), - title varchar(255), - primary key (id) -); - -create table personal_social_link -( - id bigint generated by default as identity, - platform varchar(255), - url varchar(255), - personal_id bigint not null, - primary key (id) -); - -create table project -( - id bigint generated by default as identity, - demo varchar(255), - description varchar(255), - image varchar(255), - repository varchar(255), - title varchar(255), - primary key (id) -); - -create table project_feature -( - id bigint generated by default as identity, - name varchar(255), - project_id bigint, - primary key (id) -); - -create table project_feature_technology -( - id bigint generated by default as identity, - name varchar(255), - project_id bigint, - primary key (id) -); - -create table skill -( - id bigint generated by default as identity, - level integer, - name varchar(255), - years integer, - skill_id bigint, - primary key (id) -); - -create table skill_group -( - id bigint generated by default as identity, - icon varchar(255), - name varchar(255), - primary key (id) -); - -alter table if exists experience_achievement - add constraint FK94xrk6stofkung8skwplo29nd - foreign key (experience_id) - references experience; - -alter table if exists experience_skill - add constraint FKpr3jdfjjlaubuayoafpwyx2al - foreign key (experience_id) - references experience; - -alter table if exists project_feature - add constraint FKdifppyvrfito5in15ox4db0up - foreign key (project_id) - references project; - -alter table if exists project_feature_technology - add constraint FK15krsajtovetpg5vsaqj3icwf - foreign key (project_id) - references project; - -alter table if exists skill - add constraint FKi819li5g5cp5qbsyenhr3kmef - foreign key (skill_id) - references skill_group; \ No newline at end of file diff --git a/infrastructure/src/main/resources/ebean.mf b/infrastructure/src/main/resources/ebean.mf new file mode 100644 index 0000000..a176067 --- /dev/null +++ b/infrastructure/src/main/resources/ebean.mf @@ -0,0 +1,3 @@ +entity-packages: com.pablotj.portfolio.infrastructure.persistence +transactional-packages: com.pablotj.portfolio +querybean-packages: com.pablotj.portfolio \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9253a6b..42adc47 100644 --- a/pom.xml +++ b/pom.xml @@ -1,73 +1,84 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - 4.0.0 + 4.0.0 - com.pablotj - portfolio-api - 0.0.1-SNAPSHOT - pom + com.pablotj + portfolio-api + 0.0.1-SNAPSHOT + pom - - domain - application - infrastructure - bootstrap - + + domain + application + infrastructure + bootstrap + - - org.springframework.boot - spring-boot-starter-parent - 3.3.2 - - + + 21 + UTF-8 + 3.2.0 + 1.5.5.Final + 1.18.32 + 2.17.2 + - - 21 - 1.5.5.Final - 2.6.0 - + + + + io.jooby + jooby-bom + ${jooby.version} + pom + import + - - - - org.mapstruct - mapstruct - ${mapstruct.version} - - - org.mapstruct - mapstruct-processor - ${mapstruct.version} - - - + + org.mapstruct + mapstruct + ${mapstruct.version} + + + io.jooby + jooby-apt + ${jooby.version} + provided + + + - - - - - org.apache.maven.plugins - maven-compiler-plugin - - ${java.version} - ${java.version} - - - org.projectlombok - lombok - 1.18.32 - - - org.mapstruct - mapstruct-processor - ${mapstruct.version} - - - - - - - + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + ${java.version} + + + io.jooby + jooby-apt + ${jooby.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + + \ No newline at end of file