refactor(api): refactor endpoints, services, and domain logic

This commit is contained in:
Pablo de la Torre Jamardo 2025-09-09 19:53:42 +02:00
parent 9f5306545e
commit 1b55d9ab29
144 changed files with 1357 additions and 1221 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
.idea .idea
*.toml
*.db
target target

View File

@ -1,26 +0,0 @@
package com.pablotj.portfolio.application.about;
import com.pablotj.portfolio.domain.about.About;
import com.pablotj.portfolio.domain.about.port.AboutRepositoryPort;
public class CreateAboutUseCase {
private final AboutRepositoryPort repository;
public CreateAboutUseCase(AboutRepositoryPort repository) {
this.repository = repository;
}
public About handle(Command cmd) {
var about = About.builder()
.id(null)
.title(cmd.title())
.description(cmd.description())
.url(cmd.url())
.build();
return repository.save(about);
}
public record Command(String title, String description, String url) {
}
}

View File

@ -1,24 +0,0 @@
package com.pablotj.portfolio.application.about;
import com.pablotj.portfolio.domain.about.About;
import com.pablotj.portfolio.domain.about.AboutId;
import com.pablotj.portfolio.domain.about.port.AboutRepositoryPort;
import java.util.List;
import java.util.Optional;
public class GetAboutUseCase {
private final AboutRepositoryPort repository;
public GetAboutUseCase(AboutRepositoryPort repository) {
this.repository = repository;
}
public Optional<About> byId(Long id) {
return repository.findById(new AboutId(id));
}
public List<About> all() {
return repository.findAll();
}
}

View File

@ -1,26 +1,33 @@
package com.pablotj.portfolio.application.certification; package com.pablotj.portfolio.application.certification;
import com.pablotj.portfolio.domain.certification.Certification; 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.domain.certification.port.CertificationRepositoryPort;
public class CreateCertificationUseCase { public class CreateCertificationUseCase {
private final CertificationRepositoryPort repository; CertificationRepositoryPort repository;
public CreateCertificationUseCase(CertificationRepositoryPort repository) { public CreateCertificationUseCase(CertificationRepositoryPort repository) {
this.repository = repository; this.repository = repository;
} }
public Certification handle(Command cmd) { public Certification handle(Long profileId, Command cmd) {
var certification = Certification.builder() var certification = Certification.builder()
.id(null) .id(new CertificationId(profileId))
.title(cmd.title()) .name(cmd.name())
.description(cmd.description()) .issuer(cmd.issuer())
.url(cmd.url()) .date(cmd.date())
.credentialId(cmd.credentialId())
.build(); .build();
return repository.save(certification); return repository.save(certification);
} }
public record Command(String title, String description, String url) { public record Command(
String name,
String issuer,
String date,
String credentialId
) {
} }
} }

View File

@ -14,11 +14,11 @@ public class GetCertificationUseCase {
this.repository = repository; this.repository = repository;
} }
public Optional<Certification> byId(Long id) { public Optional<Certification> byId(Long profileId, Long id) {
return repository.findById(new CertificationId(id)); return repository.findById(new CertificationId(profileId, id));
} }
public List<Certification> all() { public List<Certification> all(Long profileId) {
return repository.findAll(); return repository.findAll(profileId);
} }
} }

View File

@ -1,38 +0,0 @@
package com.pablotj.portfolio.application.contact;
import com.pablotj.portfolio.domain.contact.Contact;
import com.pablotj.portfolio.domain.contact.port.ContactRepositoryPort;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
public class CreateContactUseCase {
private final ContactRepositoryPort repository;
public CreateContactUseCase(ContactRepositoryPort repository) {
this.repository = repository;
}
public Contact handle(Command cmd) {
var contact = Contact.builder()
.id(null)
.country(cmd.country())
.city(cmd.city())
.email(cmd.email())
.phone(cmd.phone())
.linkedin(cmd.linkedin())
.github(cmd.github())
.build();
return repository.save(contact);
}
public record Command(
String country,
String city,
String email,
String phone,
String linkedin,
String github
) {
}
}

View File

@ -1,24 +0,0 @@
package com.pablotj.portfolio.application.contact;
import com.pablotj.portfolio.domain.contact.Contact;
import com.pablotj.portfolio.domain.contact.ContactId;
import com.pablotj.portfolio.domain.contact.port.ContactRepositoryPort;
import java.util.List;
import java.util.Optional;
public class GetContactUseCase {
private final ContactRepositoryPort repository;
public GetContactUseCase(ContactRepositoryPort repository) {
this.repository = repository;
}
public Optional<Contact> byId(Long id) {
return repository.findById(new ContactId(id));
}
public List<Contact> all() {
return repository.findAll();
}
}

View File

@ -1,6 +1,7 @@
package com.pablotj.portfolio.application.education; package com.pablotj.portfolio.application.education;
import com.pablotj.portfolio.domain.education.Education; 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.domain.education.port.EducationRepositoryPort;
public class CreateEducationUseCase { public class CreateEducationUseCase {
@ -11,16 +12,24 @@ public class CreateEducationUseCase {
this.repository = repository; this.repository = repository;
} }
public Education handle(Command cmd) { public Education handle(Long profileId, Command cmd) {
var education = Education.builder() var education = Education.builder()
.id(null) .id(new EducationId(profileId))
.title(cmd.title()) .institution(cmd.institution())
.degree(cmd.degree())
.period(cmd.period())
.grade(cmd.grade())
.description(cmd.description()) .description(cmd.description())
.url(cmd.url())
.build(); .build();
return repository.save(education); return repository.save(education);
} }
public record Command(String title, String description, String url) { public record Command(
String institution,
String degree,
String period,
String grade,
String description
) {
} }
} }

View File

@ -14,11 +14,11 @@ public class GetEducationUseCase {
this.repository = repository; this.repository = repository;
} }
public Optional<Education> byId(Long id) { public Optional<Education> byId(Long profileId, Long id) {
return repository.findById(new EducationId(id)); return repository.findById(new EducationId(profileId, id));
} }
public List<Education> all() { public List<Education> all(Long profileId) {
return repository.findAll(); return repository.findAll(profileId);
} }
} }

View File

@ -2,10 +2,9 @@ package com.pablotj.portfolio.application.experience;
import com.pablotj.portfolio.domain.experience.Achievement; import com.pablotj.portfolio.domain.experience.Achievement;
import com.pablotj.portfolio.domain.experience.Experience; import com.pablotj.portfolio.domain.experience.Experience;
import com.pablotj.portfolio.domain.experience.Skill; import com.pablotj.portfolio.domain.experience.ExperienceId;
import com.pablotj.portfolio.domain.experience.Technology;
import com.pablotj.portfolio.domain.experience.port.ExperienceRepositoryPort; import com.pablotj.portfolio.domain.experience.port.ExperienceRepositoryPort;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -17,22 +16,18 @@ public class CreateExperienceUseCase {
this.repository = repository; this.repository = repository;
} }
public Experience handle(Command cmd) { public Experience handle(Long profileId, Command cmd) {
var experience = Experience.builder() var experience = Experience.builder()
.id(null) .id(new ExperienceId(profileId))
.position(cmd.position())
.company(cmd.company()) .company(cmd.company())
.startDate(cmd.startDate()) .position(cmd.position())
.endDate(cmd.endDate()) .period(cmd.period())
.city(cmd.city()) .location(cmd.location())
.region(cmd.region())
.country(cmd.country())
.remote(cmd.remote())
.description(cmd.description()) .description(cmd.description())
.skills(new ArrayList<>()) .technologies(new ArrayList<>())
.achievements(new ArrayList<>()) .achievements(new ArrayList<>())
.build(); .build();
cmd.skills.forEach(name -> experience.getSkills().add(Skill.builder().id(null).name(name).build())); cmd.technologies.forEach(name -> experience.getTechnologies().add(Technology.builder().id(null).name(name).build()));
cmd.achievements.forEach(description -> experience.getAchievements().add(Achievement.builder().id(null).description(description).build())); cmd.achievements.forEach(description -> experience.getAchievements().add(Achievement.builder().id(null).description(description).build()));
return repository.save(experience); return repository.save(experience);
} }
@ -40,14 +35,10 @@ public class CreateExperienceUseCase {
public record Command( public record Command(
String position, String position,
String company, String company,
LocalDate startDate, String period,
LocalDate endDate, String location,
String city,
String region,
String country,
Boolean remote,
String description, String description,
List<String> skills, List<String> technologies,
List<String> achievements List<String> achievements
) { ) {
} }

View File

@ -14,11 +14,11 @@ public class GetExperienceUseCase {
this.repository = repository; this.repository = repository;
} }
public Optional<Experience> byId(Long id) { public Optional<Experience> byId(Long profileId, Long id) {
return repository.findById(new ExperienceId(id)); return repository.findById(new ExperienceId(profileId, id));
} }
public List<Experience> all() { public List<Experience> all(Long profileId) {
return repository.findAll(); return repository.findAll(profileId);
} }
} }

View File

@ -0,0 +1,50 @@
package com.pablotj.portfolio.application.profile;
import com.pablotj.portfolio.domain.profile.Profile;
import com.pablotj.portfolio.domain.profile.ProfileSocialLink;
import com.pablotj.portfolio.domain.profile.port.ProfileRepositoryPort;
import java.util.List;
public class CreateProfileUseCase {
private final ProfileRepositoryPort repository;
public CreateProfileUseCase(ProfileRepositoryPort repository) {
this.repository = repository;
}
public Profile handle(Command cmd) {
var personalBuilder = Profile.builder()
.id(null)
.slug(cmd.slug())
.name(cmd.name())
.title(cmd.title())
.subtitle(cmd.subtitle())
.email(cmd.email())
.phone(cmd.phone())
.location(cmd.location())
.avatar(cmd.avatar())
.bio(cmd.bio());
if (cmd.socialLinks != null) {
cmd.socialLinks.forEach(l -> personalBuilder.social(ProfileSocialLink.builder().id(null).platform(l.platform()).url(l.url()).build()));
}
return repository.save(personalBuilder.build());
}
public record Command(
String slug,
String name,
String title,
String subtitle,
String email,
String phone,
String location,
String avatar,
String bio,
List<Link> socialLinks
) {
}
public record Link(String platform, String url) {
}
}

View File

@ -0,0 +1,30 @@
package com.pablotj.portfolio.application.profile;
import com.pablotj.portfolio.domain.profile.Profile;
import com.pablotj.portfolio.domain.profile.ProfileId;
import com.pablotj.portfolio.domain.profile.port.ProfileRepositoryPort;
import java.util.List;
import java.util.Optional;
public class GetProfileUseCase {
private final ProfileRepositoryPort repository;
public GetProfileUseCase(ProfileRepositoryPort repository) {
this.repository = repository;
}
public Optional<Profile> byId(Long id) {
return repository.findById(new ProfileId(id));
}
public Optional<Profile> bySlug(String slug) {
return repository.findBySlug(new ProfileId(slug));
}
public List<Profile> all() {
return repository.findAll();
}
}

View File

@ -1,25 +1,44 @@
package com.pablotj.portfolio.application.project; package com.pablotj.portfolio.application.project;
import com.pablotj.portfolio.domain.project.Project; import com.pablotj.portfolio.domain.project.Project;
import com.pablotj.portfolio.domain.project.ProjectFeature;
import com.pablotj.portfolio.domain.project.ProjectId;
import com.pablotj.portfolio.domain.project.ProjectTechnology;
import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort; import com.pablotj.portfolio.domain.project.port.ProjectRepositoryPort;
import java.util.ArrayList;
import java.util.List;
public class CreateProjectUseCase { public class CreateProjectUseCase {
private final ProjectRepositoryPort repository; private final ProjectRepositoryPort repository;
public record Command(String title, String description, String url) {}
public CreateProjectUseCase(ProjectRepositoryPort repository) { public CreateProjectUseCase(ProjectRepositoryPort repository) {
this.repository = repository; this.repository = repository;
} }
public Project handle(Command cmd) { public Project handle(Long profileId, Command cmd) {
var project = Project.builder() var project = Project.builder()
.id(null) .id(new ProjectId(profileId))
.title(cmd.title()) .title(cmd.title())
.description(cmd.description()) .description(cmd.description())
.url(cmd.url()) .image(cmd.image())
.technologies(new ArrayList<>())
.features(new ArrayList<>())
.demo(cmd.demo())
.repository(cmd.repository())
.build(); .build();
cmd.technologies.forEach(name -> project.getTechnologies().add(ProjectTechnology.builder().id(null).name(name).build()));
cmd.features.forEach(description -> project.getFeatures().add(ProjectFeature.builder().id(null).name(description).build()));
return repository.save(project); return repository.save(project);
} }
public record Command(String title,
String description,
String image,
List<String> technologies,
List<String> features,
String demo,
String repository) {
}
} }

View File

@ -15,11 +15,11 @@ public class GetProjectUseCase {
this.repository = repository; this.repository = repository;
} }
public Optional<Project> byId(Long id) { public Optional<Project> byId(Long profileId, Long id) {
return repository.findById(new ProjectId(id)); return repository.findById(new ProjectId(profileId, id));
} }
public List<Project> all() { public List<Project> all(Long profileId) {
return repository.findAll(); return repository.findAll(profileId);
} }
} }

View File

@ -1,35 +0,0 @@
package com.pablotj.portfolio.application.resume;
import com.pablotj.portfolio.domain.resume.Resume;
import com.pablotj.portfolio.domain.resume.port.ResumeRepositoryPort;
import jakarta.validation.constraints.NotBlank;
public class CreateResumeUseCase {
private final ResumeRepositoryPort repository;
public CreateResumeUseCase(ResumeRepositoryPort repository) {
this.repository = repository;
}
public Resume handle(Command cmd) {
var home = Resume.builder()
.id(null)
.name(cmd.title())
.surnames(cmd.surnames())
.title(cmd.title())
.summary(cmd.summary())
.icon(cmd.icon())
.build();
return repository.save(home);
}
public record Command(
String name,
String surnames,
String title,
String summary,
String icon
) {
}
}

View File

@ -1,24 +0,0 @@
package com.pablotj.portfolio.application.resume;
import com.pablotj.portfolio.domain.resume.Resume;
import com.pablotj.portfolio.domain.resume.ResumeId;
import com.pablotj.portfolio.domain.resume.port.ResumeRepositoryPort;
import java.util.List;
import java.util.Optional;
public class GetResumeUseCase {
private final ResumeRepositoryPort repository;
public GetResumeUseCase(ResumeRepositoryPort repository) {
this.repository = repository;
}
public Optional<Resume> byId(Long id) {
return repository.findById(new ResumeId(id));
}
public List<Resume> all() {
return repository.findAll();
}
}

View File

@ -1,7 +1,11 @@
package com.pablotj.portfolio.application.skill; package com.pablotj.portfolio.application.skill;
import com.pablotj.portfolio.domain.skill.Skill; import com.pablotj.portfolio.domain.skill.Skill;
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.domain.skill.port.SkillRepositoryPort;
import java.util.ArrayList;
import java.util.List;
public class CreateSkillUseCase { public class CreateSkillUseCase {
@ -11,16 +15,29 @@ public class CreateSkillUseCase {
this.repository = repository; this.repository = repository;
} }
public Skill handle(Command cmd) { public SkillGroup handle(Long profileId, CommandGroup cmd) {
var skill = Skill.builder() var skillGroup = SkillGroup.builder()
.id(null) .id(new SkillGroupId(profileId))
.title(cmd.title()) .name(cmd.name())
.description(cmd.description()) .icon(cmd.icon())
.url(cmd.url()) .skills(new ArrayList<>())
.build(); .build();
return repository.save(skill); cmd.skills.forEach(s -> skillGroup.getSkills().add(
Skill.builder().id(null).name(s.name).level(s.level).years(s.years).build()));
return repository.save(skillGroup);
} }
public record Command(String title, String description, String url) { public record CommandGroup(
String name,
String icon,
List<CommandSkill> skills
) {
}
public record CommandSkill(
String name,
Integer level,
Integer years
) {
} }
} }

View File

@ -1,7 +1,7 @@
package com.pablotj.portfolio.application.skill; package com.pablotj.portfolio.application.skill;
import com.pablotj.portfolio.domain.skill.Skill; import com.pablotj.portfolio.domain.skill.SkillGroup;
import com.pablotj.portfolio.domain.skill.SkillId; import com.pablotj.portfolio.domain.skill.SkillGroupId;
import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort; import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -14,11 +14,11 @@ public class GetSkillUseCase {
this.repository = repository; this.repository = repository;
} }
public Optional<Skill> byId(Long id) { public Optional<SkillGroup> byId(Long profileId, Long id) {
return repository.findById(new SkillId(id)); return repository.findById(new SkillGroupId(profileId, id));
} }
public List<Skill> all() { public List<SkillGroup> all(Long profileId) {
return repository.findAll(); return repository.findAll(profileId);
} }
} }

View File

@ -51,4 +51,20 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -1,21 +0,0 @@
package com.pablotj.portfolio.bootstrap.about;
import com.pablotj.portfolio.application.about.CreateAboutUseCase;
import com.pablotj.portfolio.application.about.GetAboutUseCase;
import com.pablotj.portfolio.domain.about.port.AboutRepositoryPort;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AboutApplicationConfig {
@Bean
public GetAboutUseCase getAboutUseCase(AboutRepositoryPort repo) {
return new GetAboutUseCase(repo);
}
@Bean
public CreateAboutUseCase createAboutUseCase(AboutRepositoryPort repo) {
return new CreateAboutUseCase(repo);
}
}

View File

@ -1,21 +0,0 @@
package com.pablotj.portfolio.bootstrap.contact;
import com.pablotj.portfolio.application.contact.CreateContactUseCase;
import com.pablotj.portfolio.application.contact.GetContactUseCase;
import com.pablotj.portfolio.domain.contact.port.ContactRepositoryPort;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ContactApplicationConfig {
@Bean
public GetContactUseCase getContactUseCase(ContactRepositoryPort repo) {
return new GetContactUseCase(repo);
}
@Bean
public CreateContactUseCase createContactUseCase(ContactRepositoryPort repo) {
return new CreateContactUseCase(repo);
}
}

View File

@ -0,0 +1,21 @@
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;
@Configuration
public class ProfileApplicationConfig {
@Bean
public GetProfileUseCase getHomeUseCase(ProfileRepositoryPort repo) {
return new GetProfileUseCase(repo);
}
@Bean
public CreateProfileUseCase createHomeUseCase(ProfileRepositoryPort repo) {
return new CreateProfileUseCase(repo);
}
}

View File

@ -1,21 +0,0 @@
package com.pablotj.portfolio.bootstrap.resume;
import com.pablotj.portfolio.application.resume.CreateResumeUseCase;
import com.pablotj.portfolio.application.resume.GetResumeUseCase;
import com.pablotj.portfolio.domain.resume.port.ResumeRepositoryPort;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ResumeApplicationConfig {
@Bean
public GetResumeUseCase getHomeUseCase(ResumeRepositoryPort repo) {
return new GetResumeUseCase(repo);
}
@Bean
public CreateResumeUseCase createHomeUseCase(ResumeRepositoryPort repo) {
return new CreateResumeUseCase(repo);
}
}

View File

@ -1,13 +0,0 @@
package com.pablotj.portfolio.domain.about;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class About {
private final AboutId id;
private final String title;
private final String description;
private final String url;
}

View File

@ -1,7 +0,0 @@
package com.pablotj.portfolio.domain.about;
public record AboutId(Long value) {
public AboutId {
if (value != null && value < 0) throw new IllegalArgumentException("AboutId must be positive");
}
}

View File

@ -1,15 +0,0 @@
package com.pablotj.portfolio.domain.about.port;
import com.pablotj.portfolio.domain.about.About;
import com.pablotj.portfolio.domain.about.AboutId;
import java.util.List;
import java.util.Optional;
public interface AboutRepositoryPort {
About save(About p);
Optional<About> findById(AboutId id);
List<About> findAll();
}

View File

@ -7,7 +7,8 @@ import lombok.Getter;
@Builder @Builder
public class Certification { public class Certification {
private final CertificationId id; private final CertificationId id;
private final String title; private final String name;
private final String description; private final String issuer;
private final String url; private final String date;
private final String credentialId;
} }

View File

@ -1,7 +1,12 @@
package com.pablotj.portfolio.domain.certification; package com.pablotj.portfolio.domain.certification;
public record CertificationId(Long value) { public record CertificationId(Long profileId, Long certificationId) {
public CertificationId(Long profileId) {
this(profileId, null);
}
public CertificationId { public CertificationId {
if (value != null && value < 0) throw new IllegalArgumentException("CertificationId must be positive"); if (certificationId != null && certificationId < 0) throw new IllegalArgumentException("CertificationId must be positive");
} }
} }

View File

@ -10,5 +10,5 @@ public interface CertificationRepositoryPort {
Optional<Certification> findById(CertificationId id); Optional<Certification> findById(CertificationId id);
List<Certification> findAll(); List<Certification> findAll(Long profileId);
} }

View File

@ -1,19 +0,0 @@
package com.pablotj.portfolio.domain.contact;
import jakarta.validation.constraints.Email;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class Contact {
private final ContactId id;
private final String country;
private final String city;
private final String email;
private final String phone;
private final String linkedin;
private final String github;
}

View File

@ -1,7 +0,0 @@
package com.pablotj.portfolio.domain.contact;
public record ContactId(Long value) {
public ContactId {
if (value != null && value < 0) throw new IllegalArgumentException("ContactId must be positive");
}
}

View File

@ -1,14 +0,0 @@
package com.pablotj.portfolio.domain.contact.port;
import com.pablotj.portfolio.domain.contact.Contact;
import com.pablotj.portfolio.domain.contact.ContactId;
import java.util.List;
import java.util.Optional;
public interface ContactRepositoryPort {
Contact save(Contact p);
Optional<Contact> findById(ContactId id);
List<Contact> findAll();
}

View File

@ -7,7 +7,9 @@ import lombok.Getter;
@Builder @Builder
public class Education { public class Education {
private final EducationId id; private final EducationId id;
private final String title; private final String institution;
private final String degree;
private final String period;
private final String grade;
private final String description; private final String description;
private final String url;
} }

View File

@ -1,7 +1,11 @@
package com.pablotj.portfolio.domain.education; package com.pablotj.portfolio.domain.education;
public record EducationId(Long value) { public record EducationId(Long profileId, Long educationId) {
public EducationId(Long profileId) {
this(profileId, null);
}
public EducationId { public EducationId {
if (value != null && value < 0) throw new IllegalArgumentException("EducationId must be positive"); if (educationId != null && educationId < 0) throw new IllegalArgumentException("EducationId must be positive");
} }
} }

View File

@ -10,5 +10,5 @@ public interface EducationRepositoryPort {
Optional<Education> findById(EducationId id); Optional<Education> findById(EducationId id);
List<Education> findAll(); List<Education> findAll(Long profileId);
} }

View File

@ -1,6 +1,5 @@
package com.pablotj.portfolio.domain.experience; package com.pablotj.portfolio.domain.experience;
import java.time.LocalDate;
import java.util.List; import java.util.List;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
@ -9,15 +8,11 @@ import lombok.Getter;
@Builder @Builder
public class Experience { public class Experience {
private final ExperienceId id; private final ExperienceId id;
private final String position;
private final String company; private final String company;
private final LocalDate startDate; private final String position;
private final LocalDate endDate; private final String period;
private final String city; private final String location;
private final String region;
private final String country;
private final Boolean remote;
private final String description; private final String description;
private final List<Skill> skills; private final List<Technology> technologies;
private final List<Achievement> achievements; private final List<Achievement> achievements;
} }

View File

@ -1,7 +1,12 @@
package com.pablotj.portfolio.domain.experience; package com.pablotj.portfolio.domain.experience;
public record ExperienceId(Long value) { public record ExperienceId(Long profileId, Long experienceId) {
public ExperienceId(Long profileId) {
this(profileId, null);
}
public ExperienceId { public ExperienceId {
if (value != null && value < 0) throw new IllegalArgumentException("ExperienceId must be positive"); if (experienceId != null && experienceId < 0) throw new IllegalArgumentException("ProfileSocialLinkId must be positive");
} }
} }

View File

@ -1,12 +1,11 @@
package com.pablotj.portfolio.domain.experience; package com.pablotj.portfolio.domain.experience;
import java.time.LocalDate;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
@Builder @Builder
public class Skill { public class Technology {
private final SkillId id; private final TechnologyId id;
private String name; private String name;
} }

View File

@ -1,7 +1,7 @@
package com.pablotj.portfolio.domain.experience; package com.pablotj.portfolio.domain.experience;
public record SkillId(Long value) { public record TechnologyId(Long value) {
public SkillId { public TechnologyId {
if (value != null && value < 0) throw new IllegalArgumentException("SkillId must be positive"); if (value != null && value < 0) throw new IllegalArgumentException("TechnologyId must be positive");
} }
} }

View File

@ -10,5 +10,6 @@ public interface ExperienceRepositoryPort {
Optional<Experience> findById(ExperienceId id); Optional<Experience> findById(ExperienceId id);
List<Experience> findAll(); List<Experience> findAll(Long profileId);
} }

View File

@ -0,0 +1,23 @@
package com.pablotj.portfolio.domain.profile;
import java.util.List;
import lombok.Builder;
import lombok.Getter;
import lombok.Singular;
@Getter
@Builder
public class Profile {
private final ProfileId id;
private final String slug;
private final String name;
private final String title;
private final String subtitle;
private final String email;
private final String phone;
private final String location;
private final String avatar;
private final String bio;
@Singular("social")
private final List<ProfileSocialLink> social;
}

View File

@ -0,0 +1,16 @@
package com.pablotj.portfolio.domain.profile;
public record ProfileId(Long id, String slug) {
public ProfileId(Long id) {
this(id, null);
}
public ProfileId(String slug) {
this(null, slug);
}
public ProfileId {
if (id != null && id < 0) throw new IllegalArgumentException("ProfileId must be positive");
}
}

View File

@ -0,0 +1,12 @@
package com.pablotj.portfolio.domain.profile;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class ProfileSocialLink {
private final ProfileSocialLinkId id;
private final String url;
private final String platform;
}

View File

@ -0,0 +1,7 @@
package com.pablotj.portfolio.domain.profile;
public record ProfileSocialLinkId(Long value) {
public ProfileSocialLinkId {
if (value != null && value < 0) throw new IllegalArgumentException("ProfileSocialLinkId must be positive");
}
}

View File

@ -0,0 +1,16 @@
package com.pablotj.portfolio.domain.profile.port;
import com.pablotj.portfolio.domain.profile.Profile;
import com.pablotj.portfolio.domain.profile.ProfileId;
import java.util.List;
import java.util.Optional;
public interface ProfileRepositoryPort {
Profile save(Profile p);
Optional<Profile> findBySlug(ProfileId id);
Optional<Profile> findById(ProfileId id);
List<Profile> findAll();
}

View File

@ -1,5 +1,6 @@
package com.pablotj.portfolio.domain.project; package com.pablotj.portfolio.domain.project;
import java.util.List;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
@ -9,5 +10,9 @@ public class Project {
private final ProjectId id; private final ProjectId id;
private final String title; private final String title;
private final String description; private final String description;
private final String url; private final String image;
private final List<ProjectTechnology> technologies;
private final List<ProjectFeature> features;
private final String demo;
private final String repository;
} }

View File

@ -0,0 +1,11 @@
package com.pablotj.portfolio.domain.project;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class ProjectFeature {
private final ProjectFeatureId id;
private final String name;
}

View File

@ -0,0 +1,7 @@
package com.pablotj.portfolio.domain.project;
public record ProjectFeatureId(Long value) {
public ProjectFeatureId {
if (value != null && value < 0) throw new IllegalArgumentException("ProjectFeatureId must be positive");
}
}

View File

@ -1,7 +1,12 @@
package com.pablotj.portfolio.domain.project; package com.pablotj.portfolio.domain.project;
public record ProjectId(Long value) { public record ProjectId(Long profileId, Long projectId) {
public ProjectId(Long profileId) {
this(profileId, null);
}
public ProjectId { public ProjectId {
if (value != null && value < 0) throw new IllegalArgumentException("ProjectId must be positive"); if (projectId != null && projectId < 0) throw new IllegalArgumentException("ProjectId must be positive");
} }
} }

View File

@ -0,0 +1,11 @@
package com.pablotj.portfolio.domain.project;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class ProjectTechnology {
private final ProjectTechnologyId id;
private final String name;
}

View File

@ -0,0 +1,7 @@
package com.pablotj.portfolio.domain.project;
public record ProjectTechnologyId(Long value) {
public ProjectTechnologyId {
if (value != null && value < 0) throw new IllegalArgumentException("ProjectTechnologyId must be positive");
}
}

View File

@ -9,5 +9,5 @@ import java.util.Optional;
public interface ProjectRepositoryPort { public interface ProjectRepositoryPort {
Project save(Project p); Project save(Project p);
Optional<Project> findById(ProjectId id); Optional<Project> findById(ProjectId id);
List<Project> findAll(); List<Project> findAll(Long profileId);
} }

View File

@ -1,15 +0,0 @@
package com.pablotj.portfolio.domain.resume;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class Resume {
private final ResumeId id;
private final String name;
private final String surnames;
private final String title;
private final String summary;
private final String icon;
}

View File

@ -1,7 +0,0 @@
package com.pablotj.portfolio.domain.resume;
public record ResumeId(Long value) {
public ResumeId {
if (value != null && value < 0) throw new IllegalArgumentException("ResumeId must be positive");
}
}

View File

@ -1,14 +0,0 @@
package com.pablotj.portfolio.domain.resume.port;
import com.pablotj.portfolio.domain.resume.Resume;
import com.pablotj.portfolio.domain.resume.ResumeId;
import java.util.List;
import java.util.Optional;
public interface ResumeRepositoryPort {
Resume save(Resume p);
Optional<Resume> findById(ResumeId id);
List<Resume> findAll();
}

View File

@ -7,7 +7,7 @@ import lombok.Getter;
@Builder @Builder
public class Skill { public class Skill {
private final SkillId id; private final SkillId id;
private final String title; private final String name;
private final String description; private final Integer level;
private final String url; private final Integer years;
} }

View File

@ -0,0 +1,14 @@
package com.pablotj.portfolio.domain.skill;
import java.util.List;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class SkillGroup {
private final SkillGroupId id;
private final String name;
private final String icon;
private final List<Skill> skills;
}

View File

@ -0,0 +1,12 @@
package com.pablotj.portfolio.domain.skill;
public record SkillGroupId(Long profileId, Long skillGroupId) {
public SkillGroupId(Long profileId) {
this(profileId, null);
}
public SkillGroupId {
if (skillGroupId != null && skillGroupId < 0) throw new IllegalArgumentException("SkillId must be positive");
}
}

View File

@ -1,14 +1,14 @@
package com.pablotj.portfolio.domain.skill.port; package com.pablotj.portfolio.domain.skill.port;
import com.pablotj.portfolio.domain.skill.Skill; import com.pablotj.portfolio.domain.skill.SkillGroup;
import com.pablotj.portfolio.domain.skill.SkillId; import com.pablotj.portfolio.domain.skill.SkillGroupId;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
public interface SkillRepositoryPort { public interface SkillRepositoryPort {
Skill save(Skill p); SkillGroup save(SkillGroup p);
Optional<Skill> findById(SkillId id); Optional<SkillGroup> findById(SkillGroupId id);
List<Skill> findAll(); List<SkillGroup> findAll(Long profileId);
} }

View File

@ -0,0 +1,17 @@
package com.pablotj.portfolio.infrastructure.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // todos los endpoints que comiencen con /api/
.allowedOrigins("http://127.0.0.1:3000", "http://localhost:3000", "https://pablotj.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*");
}
}

View File

@ -1,40 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.about.adapter;
import com.pablotj.portfolio.domain.about.About;
import com.pablotj.portfolio.domain.about.AboutId;
import com.pablotj.portfolio.domain.about.port.AboutRepositoryPort;
import com.pablotj.portfolio.infrastructure.persistence.about.entity.AboutJpaEntity;
import com.pablotj.portfolio.infrastructure.persistence.about.mapper.AboutJpaMapper;
import com.pablotj.portfolio.infrastructure.persistence.about.repo.SpringDataAboutRepository;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;
@Repository
public class AboutRepositoryAdapter implements AboutRepositoryPort {
private final SpringDataAboutRepository repo;
private final AboutJpaMapper mapper;
public AboutRepositoryAdapter(SpringDataAboutRepository repo, AboutJpaMapper mapper) {
this.repo = repo;
this.mapper = mapper;
}
@Override
public About save(About p) {
AboutJpaEntity entity = mapper.toEntity(p);
AboutJpaEntity saved = repo.save(entity);
return mapper.toDomain(saved);
}
@Override
public Optional<About> findById(AboutId id) {
return repo.findById(id.value()).map(mapper::toDomain);
}
@Override
public List<About> findAll() {
return repo.findAll().stream().map(mapper::toDomain).toList();
}
}

View File

@ -1,17 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.about.mapper;
import com.pablotj.portfolio.domain.about.About;
import com.pablotj.portfolio.domain.about.AboutId;
import com.pablotj.portfolio.infrastructure.persistence.about.entity.AboutJpaEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(componentModel = "spring")
public interface AboutJpaMapper {
@Mapping(target = "id", ignore = true)
AboutJpaEntity toEntity(About domain);
@Mapping(target = "id.value", source = "id")
About toDomain(AboutJpaEntity e);
}

View File

@ -1,7 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.about.repo;
import com.pablotj.portfolio.infrastructure.persistence.about.entity.AboutJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataAboutRepository extends JpaRepository<AboutJpaEntity, Long> {
}

View File

@ -30,11 +30,11 @@ public class CertificationRepositoryAdapter implements CertificationRepositoryPo
@Override @Override
public Optional<Certification> findById(CertificationId id) { public Optional<Certification> findById(CertificationId id) {
return repo.findById(id.value()).map(mapper::toDomain); return repo.findByProfileIdAndId(id.profileId(), id.certificationId()).map(mapper::toDomain);
} }
@Override @Override
public List<Certification> findAll() { public List<Certification> findAll(Long profileId) {
return repo.findAll().stream().map(mapper::toDomain).toList(); return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
} }
} }

View File

@ -1,16 +1,20 @@
package com.pablotj.portfolio.infrastructure.persistence.certification.entity; package com.pablotj.portfolio.infrastructure.persistence.certification.entity;
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Entity @Entity
@Table(name = "certifications") @Table(name = "CERTIFICATION")
@Getter @Getter
@Setter @Setter
public class CertificationJpaEntity { public class CertificationJpaEntity {
@ -19,11 +23,19 @@ public class CertificationJpaEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(nullable = false) @ManyToOne(fetch = FetchType.LAZY)
private String title; @JoinColumn(name = "profile_id", nullable = false)
private ProfileJpaEntity profile;
@Column(columnDefinition = "text") @Column
private String description; private String name;
private String url; @Column
private String issuer;
@Column
private String date;
@Column
private String credentialId;
} }

View File

@ -10,8 +10,9 @@ import org.mapstruct.Mapping;
public interface CertificationJpaMapper { public interface CertificationJpaMapper {
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
@Mapping(target = "profile.id", source = "id.profileId")
CertificationJpaEntity toEntity(Certification domain); CertificationJpaEntity toEntity(Certification domain);
@Mapping(target = "id.value", source = "id") @Mapping(target = "id", expression = "java(new CertificationId(e.getProfile().getId(), e.getId()))")
Certification toDomain(CertificationJpaEntity e); Certification toDomain(CertificationJpaEntity e);
} }

View File

@ -1,7 +1,14 @@
package com.pablotj.portfolio.infrastructure.persistence.certification.repo; package com.pablotj.portfolio.infrastructure.persistence.certification.repo;
import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity; import com.pablotj.portfolio.infrastructure.persistence.certification.entity.CertificationJpaEntity;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataCertificationRepository extends JpaRepository<CertificationJpaEntity, Long> { public interface SpringDataCertificationRepository extends JpaRepository<CertificationJpaEntity, Long> {
Optional<CertificationJpaEntity> findByProfileIdAndId(Long profileId, Long id);
List<CertificationJpaEntity> findAllByProfileId(Long profileId);
} }

View File

@ -1,40 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.contact.adapter;
import com.pablotj.portfolio.domain.contact.Contact;
import com.pablotj.portfolio.domain.contact.ContactId;
import com.pablotj.portfolio.domain.contact.port.ContactRepositoryPort;
import com.pablotj.portfolio.infrastructure.persistence.contact.entity.ContactJpaEntity;
import com.pablotj.portfolio.infrastructure.persistence.contact.mapper.ContactJpaMapper;
import com.pablotj.portfolio.infrastructure.persistence.contact.repo.SpringDataContactRepository;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;
@Repository
public class ContactRepositoryAdapter implements ContactRepositoryPort {
private final SpringDataContactRepository repo;
private final ContactJpaMapper mapper;
public ContactRepositoryAdapter(SpringDataContactRepository repo, ContactJpaMapper mapper) {
this.repo = repo;
this.mapper = mapper;
}
@Override
public Contact save(Contact p) {
ContactJpaEntity entity = mapper.toEntity(p);
ContactJpaEntity saved = repo.save(entity);
return mapper.toDomain(saved);
}
@Override
public Optional<Contact> findById(ContactId id) {
return repo.findById(id.value()).map(mapper::toDomain);
}
@Override
public List<Contact> findAll() {
return repo.findAll().stream().map(mapper::toDomain).toList();
}
}

View File

@ -1,43 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.contact.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Email;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Table(name = "contacts")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class ContactJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String country;
private String city;
@Column(nullable = false)
@Email
private String email;
private String phone;
@Column(name = "linkedin_url")
private String linkedin;
@Column(name = "github_url")
private String github;
}

View File

@ -1,17 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.contact.mapper;
import com.pablotj.portfolio.domain.contact.Contact;
import com.pablotj.portfolio.domain.contact.ContactId;
import com.pablotj.portfolio.infrastructure.persistence.contact.entity.ContactJpaEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(componentModel = "spring")
public interface ContactJpaMapper {
@Mapping(target = "id", ignore = true)
ContactJpaEntity toEntity(Contact domain);
@Mapping(target = "id.value", source = "id")
Contact toDomain(ContactJpaEntity e);
}

View File

@ -1,7 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.contact.repo;
import com.pablotj.portfolio.infrastructure.persistence.contact.entity.ContactJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataContactRepository extends JpaRepository<ContactJpaEntity, Long> {
}

View File

@ -30,11 +30,11 @@ public class EducationRepositoryAdapter implements EducationRepositoryPort {
@Override @Override
public Optional<Education> findById(EducationId id) { public Optional<Education> findById(EducationId id) {
return repo.findById(id.value()).map(mapper::toDomain); return repo.findByProfileIdAndId(id.profileId(), id.educationId()).map(mapper::toDomain);
} }
@Override @Override
public List<Education> findAll() { public List<Education> findAll(Long profileId) {
return repo.findAll().stream().map(mapper::toDomain).toList(); return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
} }
} }

View File

@ -1,16 +1,20 @@
package com.pablotj.portfolio.infrastructure.persistence.education.entity; package com.pablotj.portfolio.infrastructure.persistence.education.entity;
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Entity @Entity
@Table(name = "educations") @Table(name = "EDUCATION")
@Getter @Getter
@Setter @Setter
public class EducationJpaEntity { public class EducationJpaEntity {
@ -19,11 +23,22 @@ public class EducationJpaEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(nullable = false) @ManyToOne(fetch = FetchType.LAZY)
private String title; @JoinColumn(name = "profile_id", nullable = false)
private ProfileJpaEntity profile;
@Column
private String institution;
@Column
private String degree;
@Column
private String period;
@Column
private String grade;
@Column(columnDefinition = "text") @Column(columnDefinition = "text")
private String description; private String description;
}
private String url;
}

View File

@ -10,8 +10,9 @@ import org.mapstruct.Mapping;
public interface EducationJpaMapper { public interface EducationJpaMapper {
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
@Mapping(target = "profile.id", source = "id.profileId")
EducationJpaEntity toEntity(Education domain); EducationJpaEntity toEntity(Education domain);
@Mapping(target = "id.value", source = "id") @Mapping(target = "id", expression = "java(new EducationId(e.getProfile().getId(), e.getId()))")
Education toDomain(EducationJpaEntity e); Education toDomain(EducationJpaEntity e);
} }

View File

@ -1,7 +1,13 @@
package com.pablotj.portfolio.infrastructure.persistence.education.repo; 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 com.pablotj.portfolio.infrastructure.persistence.education.entity.EducationJpaEntity;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataEducationRepository extends JpaRepository<EducationJpaEntity, Long> { public interface SpringDataEducationRepository extends JpaRepository<EducationJpaEntity, Long> {
Optional<EducationJpaEntity> findByProfileIdAndId(Long profileId, Long id);
List<EducationJpaEntity> findAllByProfileId(Long profileId);
} }

View File

@ -30,11 +30,11 @@ public class ExperienceRepositoryAdapter implements ExperienceRepositoryPort {
@Override @Override
public Optional<Experience> findById(ExperienceId id) { public Optional<Experience> findById(ExperienceId id) {
return repo.findById(id.value()).map(mapper::toDomain); return repo.findByProfileIdAndId(id.profileId(), id.experienceId()).map(mapper::toDomain);
} }
@Override @Override
public List<Experience> findAll() { public List<Experience> findAll(Long profileId) {
return repo.findAll().stream().map(mapper::toDomain).toList(); return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
} }
} }

View File

@ -1,10 +1,20 @@
package com.pablotj.portfolio.infrastructure.persistence.experience.entity; package com.pablotj.portfolio.infrastructure.persistence.experience.entity;
import jakarta.persistence.*; import jakarta.persistence.Column;
import lombok.*; import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity @Entity
@Table(name = "experience_achievements") @Table(name = "EXPERIENCE_ACHIEVEMENT")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED)

View File

@ -1,15 +1,17 @@
package com.pablotj.portfolio.infrastructure.persistence.experience.entity; package com.pablotj.portfolio.infrastructure.persistence.experience.entity;
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.time.LocalDate;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -19,7 +21,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
@Entity @Entity
@Table(name = "experiences") @Table(name = "EXPERIENCE")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED)
@ -31,25 +33,30 @@ public class ExperienceJpaEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "profile_id", nullable = false)
private ProfileJpaEntity profile;
@Column
private String position; private String position;
@Column
private String company; private String company;
private LocalDate startDate; @Column
private LocalDate endDate; private String period;
private String city; @Column
private String region; private String location;
private String country;
private Boolean remote;
@Column(columnDefinition = "text") @Column(columnDefinition = "text")
private String description; private String description;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "experience_id") @JoinColumn(name = "EXPERIENCE_ID")
private List<ExperienceSkillJpaEntity> skills; private List<ExperienceSkillJpaEntity> technologies;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "experience_id") @JoinColumn(name = "EXPERIENCE_ID")
private List<ExperienceAchievementJpaEntity> achievements; private List<ExperienceAchievementJpaEntity> achievements;
} }

View File

@ -1,10 +1,19 @@
package com.pablotj.portfolio.infrastructure.persistence.experience.entity; package com.pablotj.portfolio.infrastructure.persistence.experience.entity;
import jakarta.persistence.*; import jakarta.persistence.Entity;
import lombok.*; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity @Entity
@Table(name = "experience_skills") @Table(name = "EXPERIENCE_SKILL")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED)

View File

@ -2,7 +2,7 @@ package com.pablotj.portfolio.infrastructure.persistence.experience.mapper;
import com.pablotj.portfolio.domain.experience.Achievement; import com.pablotj.portfolio.domain.experience.Achievement;
import com.pablotj.portfolio.domain.experience.Experience; import com.pablotj.portfolio.domain.experience.Experience;
import com.pablotj.portfolio.domain.experience.Skill; import com.pablotj.portfolio.domain.experience.Technology;
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceAchievementJpaEntity; 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.ExperienceJpaEntity;
import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceSkillJpaEntity; import com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceSkillJpaEntity;
@ -13,12 +13,12 @@ import org.mapstruct.Mapping;
public interface ExperienceJpaMapper { public interface ExperienceJpaMapper {
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
@Mapping(target = "profile.id", source = "id.profileId")
ExperienceJpaEntity toEntity(Experience domain); ExperienceJpaEntity toEntity(Experience domain);
@Mapping(target = "id.value", source = "id") @Mapping(target = "id", expression = "java(new ExperienceId(entity.getProfile().getId(), entity.getId()))")
Experience toDomain(ExperienceJpaEntity entity); Experience toDomain(ExperienceJpaEntity entity);
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
ExperienceAchievementJpaEntity toEntity(Achievement entity); ExperienceAchievementJpaEntity toEntity(Achievement entity);
@ -26,10 +26,10 @@ public interface ExperienceJpaMapper {
Achievement toDomain(ExperienceAchievementJpaEntity entity); Achievement toDomain(ExperienceAchievementJpaEntity entity);
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
ExperienceSkillJpaEntity toEntity(Skill entity); ExperienceSkillJpaEntity toEntity(Technology entity);
@Mapping(target = "id.value", source = "id") @Mapping(target = "id.value", source = "id")
Skill toDomain(ExperienceSkillJpaEntity entity); Technology toDomain(ExperienceSkillJpaEntity entity);
} }

View File

@ -1,7 +1,13 @@
package com.pablotj.portfolio.infrastructure.persistence.experience.repo; 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 com.pablotj.portfolio.infrastructure.persistence.experience.entity.ExperienceJpaEntity;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataExperienceRepository extends JpaRepository<ExperienceJpaEntity, Long> { public interface SpringDataExperienceRepository extends JpaRepository<ExperienceJpaEntity, Long> {
Optional<ExperienceJpaEntity> findByProfileIdAndId(Long profileId, Long id);
List<ExperienceJpaEntity> findAllByProfileId(Long profileId);
} }

View File

@ -0,0 +1,51 @@
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 java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;
@Repository
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;
}
@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);
}
@Override
public Optional<Profile> findBySlug(ProfileId id) {
return repo.findBySlug(id.slug()).map(mapper::toDomain);
}
@Override
public Optional<Profile> findById(ProfileId id) {
return repo.findById(id.id()).map(mapper::toDomain);
}
@Override
public List<Profile> findAll() {
return repo.findAll().stream().map(mapper::toDomain).toList();
}
}

View File

@ -0,0 +1,56 @@
package com.pablotj.portfolio.infrastructure.persistence.profile.entity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "profile")
@Getter
@Setter
public class ProfileJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String slug;
@Column
private String name;
@Column
private String title;
@Column
private String subtitle;
@Column
private String email;
@Column
private String phone;
@Column
private String location;
@Column
private String avatar;
@Column
private String bio;
@OneToMany(mappedBy = "profile", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProfileSocialLinkJpaEntity> social = new ArrayList<>();
}

View File

@ -0,0 +1,33 @@
package com.pablotj.portfolio.infrastructure.persistence.profile.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "profile_social_link")
@Getter
@Setter
public class ProfileSocialLinkJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "profile_id", nullable = false)
private ProfileJpaEntity profile;
@Column
private String url;
@Column
private String platform;
}

View File

@ -0,0 +1,26 @@
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 org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(componentModel = "spring")
public interface ProfileJpaMapper {
@Mapping(target = "id", ignore = true)
@Mapping(target = "social", source = "social")
ProfileJpaEntity toEntity(Profile domain);
@Mapping(target = "id.id", source = "id")
@Mapping(target = "social", source = "social")
Profile toDomain(ProfileJpaEntity e);
@Mapping(target = "id", ignore = true)
ProfileSocialLinkJpaEntity toEntitySocial(ProfileSocialLink entity);
@Mapping(target = "id.value", source = "id")
ProfileSocialLink toDomainSocial(ProfileSocialLinkJpaEntity entity);
}

View File

@ -0,0 +1,10 @@
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<ProfileJpaEntity, Long> {
Optional<ProfileJpaEntity> findBySlug(String slug);
}

View File

@ -31,11 +31,11 @@ public class ProjectRepositoryAdapter implements ProjectRepositoryPort {
@Override @Override
public Optional<Project> findById(ProjectId id) { public Optional<Project> findById(ProjectId id) {
return repo.findById(id.value()).map(mapper::toDomain); return repo.findByProfileIdAndId(id.profileId(), id.projectId()).map(mapper::toDomain);
} }
@Override @Override
public List<Project> findAll() { public List<Project> findAll(Long profileId) {
return repo.findAll().stream().map(mapper::toDomain).toList(); return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
} }
} }

View File

@ -1,4 +1,4 @@
package com.pablotj.portfolio.infrastructure.persistence.about.entity; package com.pablotj.portfolio.infrastructure.persistence.project.entity;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@ -10,20 +10,16 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Entity @Entity
@Table(name = "abouts") @Table(name = "PROJECT_FEATURE")
@Getter @Getter
@Setter @Setter
public class AboutJpaEntity { public class ProjectFeatureJpaEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(nullable = false) @Column
private String title; private String name;
@Column(columnDefinition = "text")
private String description;
private String url;
} }

View File

@ -1,11 +1,23 @@
package com.pablotj.portfolio.infrastructure.persistence.project.entity; package com.pablotj.portfolio.infrastructure.persistence.project.entity;
import jakarta.persistence.*; import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.List;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Entity @Entity
@Table(name = "projects") @Table(name = "PROJECT")
@Getter @Setter @Getter @Setter
public class ProjectJpaEntity { public class ProjectJpaEntity {
@ -13,11 +25,31 @@ public class ProjectJpaEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(nullable = false) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "profile_id", nullable = false)
private ProfileJpaEntity profile;
@Column
private String title; private String title;
@Column(columnDefinition = "text") @Column
private String description; private String description;
private String url; @Column
private String image;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "PROJECT_ID")
private List<ProjectTechnologyJpaEntity> technologies;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "PROJECT_ID")
private List<ProjectFeatureJpaEntity> features;
@Column
private String demo;
@Column
private String repository;
} }

View File

@ -0,0 +1,25 @@
package com.pablotj.portfolio.infrastructure.persistence.project.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "PROJECT_FEATURE_TECHNOLOGY")
@Getter
@Setter
public class ProjectTechnologyJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
}

View File

@ -1,8 +1,11 @@
package com.pablotj.portfolio.infrastructure.persistence.project.mapper; package com.pablotj.portfolio.infrastructure.persistence.project.mapper;
import com.pablotj.portfolio.domain.project.Project; import com.pablotj.portfolio.domain.project.Project;
import com.pablotj.portfolio.domain.project.ProjectId; 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.ProjectJpaEntity;
import com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectTechnologyJpaEntity;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
@ -10,8 +13,21 @@ import org.mapstruct.Mapping;
public interface ProjectJpaMapper { public interface ProjectJpaMapper {
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
@Mapping(target = "profile.id", source = "id.profileId")
ProjectJpaEntity toEntity(Project domain); ProjectJpaEntity toEntity(Project domain);
@Mapping(target = "id.value", source = "id") @Mapping(target = "id", expression = "java(new ProjectId(e.getProfile().getId(), e.getId()))")
Project toDomain(ProjectJpaEntity e); Project toDomain(ProjectJpaEntity e);
@Mapping(target = "id", ignore = true)
ProjectTechnologyJpaEntity toEntity(ProjectTechnology entity);
@Mapping(target = "id.value", source = "id")
ProjectTechnology toDomain(ProjectTechnologyJpaEntity entity);
@Mapping(target = "id", ignore = true)
ProjectFeatureJpaEntity toEntity(ProjectFeature entity);
@Mapping(target = "id.value", source = "id")
ProjectFeature toDomain(ProjectFeatureJpaEntity entity);
} }

View File

@ -1,6 +1,13 @@
package com.pablotj.portfolio.infrastructure.persistence.project.repo; 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 com.pablotj.portfolio.infrastructure.persistence.project.entity.ProjectJpaEntity;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataProjectRepository extends JpaRepository<ProjectJpaEntity, Long> {} public interface SpringDataProjectRepository extends JpaRepository<ProjectJpaEntity, Long> {
Optional<ProjectJpaEntity> findByProfileIdAndId(Long profileId, Long id);
List<ProjectJpaEntity> findAllByProfileId(Long profileId);
}

View File

@ -1,40 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.resume.adapter;
import com.pablotj.portfolio.domain.resume.Resume;
import com.pablotj.portfolio.domain.resume.ResumeId;
import com.pablotj.portfolio.domain.resume.port.ResumeRepositoryPort;
import com.pablotj.portfolio.infrastructure.persistence.resume.entity.ResumeJpaEntity;
import com.pablotj.portfolio.infrastructure.persistence.resume.mapper.ResumeJpaMapper;
import com.pablotj.portfolio.infrastructure.persistence.resume.repo.SpringDataResumeRepository;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;
@Repository
public class ResumeRepositoryAdapter implements ResumeRepositoryPort {
private final SpringDataResumeRepository repo;
private final ResumeJpaMapper mapper;
public ResumeRepositoryAdapter(SpringDataResumeRepository repo, ResumeJpaMapper mapper) {
this.repo = repo;
this.mapper = mapper;
}
@Override
public Resume save(Resume p) {
ResumeJpaEntity entity = mapper.toEntity(p);
ResumeJpaEntity saved = repo.save(entity);
return mapper.toDomain(saved);
}
@Override
public Optional<Resume> findById(ResumeId id) {
return repo.findById(id.value()).map(mapper::toDomain);
}
@Override
public List<Resume> findAll() {
return repo.findAll().stream().map(mapper::toDomain).toList();
}
}

View File

@ -1,37 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.resume.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "resumes")
@Getter
@Setter
public class ResumeJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name", nullable = false)
private String name;
@Column(name = "last_name", nullable = false)
private String surnames;
@Column(nullable = false)
private String title;
@Column(columnDefinition = "text", nullable = false)
private String summary;
@Column
private String icon;
}

View File

@ -1,17 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.resume.mapper;
import com.pablotj.portfolio.domain.resume.Resume;
import com.pablotj.portfolio.domain.resume.ResumeId;
import com.pablotj.portfolio.infrastructure.persistence.resume.entity.ResumeJpaEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(componentModel = "spring")
public interface ResumeJpaMapper {
@Mapping(target = "id", ignore = true)
ResumeJpaEntity toEntity(Resume domain);
@Mapping(target = "id.value", source = "id")
Resume toDomain(ResumeJpaEntity e);
}

View File

@ -1,7 +0,0 @@
package com.pablotj.portfolio.infrastructure.persistence.resume.repo;
import com.pablotj.portfolio.infrastructure.persistence.resume.entity.ResumeJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataResumeRepository extends JpaRepository<ResumeJpaEntity, Long> {
}

View File

@ -1,9 +1,9 @@
package com.pablotj.portfolio.infrastructure.persistence.skill.adapter; package com.pablotj.portfolio.infrastructure.persistence.skill.adapter;
import com.pablotj.portfolio.domain.skill.Skill; import com.pablotj.portfolio.domain.skill.SkillGroup;
import com.pablotj.portfolio.domain.skill.SkillId; import com.pablotj.portfolio.domain.skill.SkillGroupId;
import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort; import com.pablotj.portfolio.domain.skill.port.SkillRepositoryPort;
import com.pablotj.portfolio.infrastructure.persistence.skill.entity.SkillJpaEntity; 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.mapper.SkillJpaMapper;
import com.pablotj.portfolio.infrastructure.persistence.skill.repo.SpringDataSkillRepository; import com.pablotj.portfolio.infrastructure.persistence.skill.repo.SpringDataSkillRepository;
import java.util.List; import java.util.List;
@ -22,19 +22,19 @@ public class SkillRepositoryAdapter implements SkillRepositoryPort {
} }
@Override @Override
public Skill save(Skill p) { public SkillGroup save(SkillGroup p) {
SkillJpaEntity entity = mapper.toEntity(p); SkillGroupJpaEntity entity = mapper.toEntity(p);
SkillJpaEntity saved = repo.save(entity); SkillGroupJpaEntity saved = repo.save(entity);
return mapper.toDomain(saved); return mapper.toDomain(saved);
} }
@Override @Override
public Optional<Skill> findById(SkillId id) { public Optional<SkillGroup> findById(SkillGroupId id) {
return repo.findById(id.value()).map(mapper::toDomain); return repo.findByProfileIdAndId(id.profileId(), id.skillGroupId()).map(mapper::toDomain);
} }
@Override @Override
public List<Skill> findAll() { public List<SkillGroup> findAll(Long profileId) {
return repo.findAll().stream().map(mapper::toDomain).toList(); return repo.findAllByProfileId(profileId).stream().map(mapper::toDomain).toList();
} }
} }

View File

@ -0,0 +1,42 @@
package com.pablotj.portfolio.infrastructure.persistence.skill.entity;
import com.pablotj.portfolio.infrastructure.persistence.profile.entity.ProfileJpaEntity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "SKILL_GROUP")
@Getter
@Setter
public class SkillGroupJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "profile_id", nullable = false)
private ProfileJpaEntity profile;
@Column
private String name;
@Column
private String icon;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "SKILL_ID")
private List<SkillJpaEntity> skills;
}

View File

@ -10,7 +10,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Entity @Entity
@Table(name = "skills") @Table(name = "SKILL")
@Getter @Getter
@Setter @Setter
public class SkillJpaEntity { public class SkillJpaEntity {
@ -19,11 +19,12 @@ public class SkillJpaEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(nullable = false) @Column
private String title; private String name;
@Column(columnDefinition = "text") @Column
private String description; private Integer level;
private String url; @Column
private Integer years;
} }

Some files were not shown because too many files have changed in this diff Show More