Reputation: 1158
I have the following entities with a many-to-many releationship
User entity
@Entity
@Table(name = "users")
public class User {
...
@ManyToMany
@JoinTable(
name = "team_members",
joinColumns = @JoinColumn(name = "team_id"),
inverseJoinColumns = @JoinColumn(name = "user_id"))
private Set<Team> teams;
Team entity
@Entity
@Table(name = "teams")
public class Team {
...
@ManyToMany
@JoinTable(
name = "team_members",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "team_id"))
private Set<User> members;
}
And I have this UserService
public interface UserService {
UserDTO getById(Integer userId);
...
}
I want to implement a method inside a TeamService like this:
@Override
@Transactional
public TeamDTO addMemberToTeam(Integer teamId, Integer userId, User currentUser) {
checkCurrentUserIsAdmin(currentUser);
User user = userService.getById(userId); <-------- PROBLEM LINE
Team team = teamRepository.findById(teamId)
.orElseThrow(() -> new RuntimeException("No such team"));
team.getMembers().add(user);
return TeamDTO.fromTeam(teamRepository.save(team));
}
Problem is I have the service to return UserDTO as it should, but in order to make the releationship work I need a User.
Possible solutions I could think of:
Solution 1:
Have another method in UserService which explicitly returns User like:
User getUserById(Integer userId);
But won't this brake the idea of UserService only returning UserDTOs. Is there a standard way of doing this and still follow best practices of Service interfaces to work with DTOs.
Solution 2:
Have another method in UserService like:
@Override
public void addUserToTeam(Integer userId, Team team) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("No such user"));
user.getTeams().add(team);
team.getMembers().add(user);
userRepository.save(user);
}
And change the method in TeamService to this:
@Override
@Transactional
public TeamDTO addMemberToTeam(Integer teamId, Integer userId, User currentUser) {
checkCurrentUserIsAdmin(currentUser);
Team team = teamRepository.findById(teamId)
.orElseThrow(() -> new RuntimeException("No such team"));
userService.addUserToTeam(userId,team); <--- THIS NEW METHOD CALL
return TeamDTO.fromTeam(teamRepository.save(team));
}
This seems like a better solution, but I still have a feeling I am over complicating it.
Which would be the better solution in my case?
Upvotes: 0
Views: 50
Reputation: 9266
If I understand correctly, the best practice you're trying to follow have 1 @Service
owns an @Entity
, which is why you're trying so hard not to use userRepository
directly inside TeamService
.
IMHO, DTO
is only required when you're communicating with external parties like when you have a REST endpoint that returns a UserDTO
to the consumer instead of returning the @Entity
itself. Within the boundary of your own application and services, I think it's OK to use repositories wherever you need it instead of making it difficult by creating a hard line in the sand for yourself.
Just in case you still want to use the UserDTO
to communicate between services and with external parties, I suggest this.
public class UserDTO {
...
// your fields
...
@JsonIgnore
private User userRecord;
}
This way, your services will still have access to the actual @Entity
and when you return this object to API consumers, they will not see that field.
Upvotes: 1