Repository vs. DAO (à nouveau)

En général, cette histoire n’a pas d’importance, mais juste pour expliquer le code ci-dessous:

Le serveur gère les utilisateurs et les groupes d’utilisateurs. Les groupes d’utilisateurs peuvent “découvrir” des lieux – à ce stade, ces lieux proviennent exclusivement de l’API Google Adresses.


Mise en œuvre actuelle


Actuellement, j’ai beaucoup d’objects JpaRepository , que j’appelle le référentiel , dans ma couche de service. Je mets l’accent sur ” Référentiel ” car dans ma solution proposée ci-dessous, ils seraient rétrogradés aux DAO.

Cependant, ce que je n’aime pas dans mon code actuel, ainsi que la raison de ma question, c’est la quantité de référentiels que l’on peut trouver dans UserGroupService .

 @Service public class UserGroupService { private final static Logger LOGGER = LogManager.getLogger(UserGroupService.class); @Autowired private UserGroupRepository userGroupRepository; @Autowired private UserGroupPlaceRepository userGroupPlaceRepository; @Autowired private PlaceRepository placeRepository; @Autowired private GooglePlaceRepository googlePlaceRepository; @Autowired private GooglePlaces googlePlaces; public UserGroupService() { } @Transactional public void discoverPlaces(Long groupId) { final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null); if (userGroup == null) { throw new EntityNotFoundException(Ssortingng.format("User group with id %s not found.", groupId)); } List allPlaces = this.googlePlaces.findPlaces( userGroup.getLatitude(), userGroup.getLongitude(), userGroup.getSearchRadius()); allPlaces.forEach(googlePlaceResult -> { GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId); if (googlePlace != null) { return; } Place place = new Place(); place.setLatitude(googlePlaceResult.geometry.location.lat); place.setLongitude(googlePlaceResult.geometry.location.lng); place.setPlaceType(Place.PlaceType.GOOGLE_PLACE); place.setName(googlePlaceResult.name); place.setVicinity(googlePlaceResult.vicinity); place = this.placeRepository.save(place); UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new UserGroupPlace.UserGroupPlaceId(); userGroupPlaceId.setUserGroup(userGroup); userGroupPlaceId.setPlace(place); UserGroupPlace userGroupPlace = new UserGroupPlace(); userGroupPlace.setUserGroupPlaceId(userGroupPlaceId); this.userGroupPlaceRepository.save(userGroupPlace); googlePlace = new GooglePlace(); googlePlace.setPlace(place); googlePlace.setGooglePlaceId(googlePlaceResult.placeId); this.googlePlaceRepository.save(googlePlace); }); } } 

Une solution qui ne fonctionne pas


Ce qui pourrait rendre ce code beaucoup plus simple et pourrait résoudre ce problème, ce serait @Inheritance :

 @Entity @Table(name = "place") @Inheritance(strategy InheritanceType.JOINED) public class Place { /* .. */ } @Entity @Table(name = "google_place") public class GooglePlace extends Place { /* .. */ } 

Cependant, ce n’est pas une option car je ne peux pas avoir un PlaceRepository qui enregistre simplement une place . Hibernate ne semble pas l’apprécier. .


Ma proposition


Je pense que ma confusion commence par les noms que Spring utilise. JpaRepository exemple – Je ne suis pas sûr que ce soit le “bon” nom. Parce que, pour autant que je sache, ces objects fonctionnent comme des objects d’access aux données (DAO). Je pense que cela devrait ressembler à quelque chose comme ceci:

 public interface PlaceDao extends JpaRepository { } public interface GooglePlaceDao extends JpaRepository { } @Repository public class GooglePlaceRepository { @Autowired private PlaceDao placeDao; @Autowired private GooglePlaceDao googlePlaceDao; public List findByGroupId(Long groupId) { // .. } public void save(GooglePlace googlePlace) { // .. } public void saveAll(List googlePlaces) { // .. } } @Service public class UserGroupService { @Autowired private GooglePlaceRepository googlePlaceRepository; @Autowired private UserGroupRepository userGroupRepository; @Transactional public void discoverPlaces(Long groupId) { final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null) .orElseThrow(throw new EntityNotFoundException(Ssortingng.format("User group with id %s not found.", groupId))); List fetched = this.googlePlaces.findPlaces( userGroup.getLatitude(), userGroup.getLongitude(), userGroup.getSearchRadius()); // Either do the mapping here or let GooglePlaces return // List instead of List List places = fetched.stream().map(googlePlaceResult -> { GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId); if (googlePlace != null) { return googlePlace; } Place place = new Place(); place.setLatitude(googlePlaceResult.geometry.location.lat); place.setLongitude(googlePlaceResult.geometry.location.lng); place.setPlaceType(Place.PlaceType.GOOGLE_PLACE); place.setName(googlePlaceResult.name); place.setVicinity(googlePlaceResult.vicinity); googlePlace = new GooglePlace(); googlePlace.setPlace(place); googlePlace.setGooglePlaceId(googlePlaceResult.placeId); return googlePlace; }).collect(Collectors.toList()); this.googlePlaceRepository.saveAll(places); // Add places to group.. } } 

Résumé


Je voudrais savoir ce que je ne vois pas. Est-ce que je lutte contre le framework ou est-ce que mon modèle de données n’a pas de sens et c’est pourquoi je me trouve confronté à ce problème? Ou suis-je toujours en train de me demander comment les deux patterns “Repository” et “DAO” sont censés être utilisés?

Comment pourrait-on mettre en œuvre cela?

Je dirais que vous avez raison de dire qu’il y a trop de dépendances de référentiel dans votre service. Personnellement, j’essaie de limiter le nombre de dépendances @Autowired et j’essaie d’utiliser un référentiel uniquement dans un seul service et d’exposer ses fonctionnalités de niveau supérieur via ce service. Dans notre société, nous appelons cela la souveraineté des données (en allemand: Datenhoheit ) et son but est de veiller à ce qu’il n’y ait qu’un seul endroit dans l’application où ces entités sont modifiées.

D’après ce que je comprends de votre code, PlacesService un PlacesService qui possède toutes les dépendances du PlaceRepository , GooglePlaceRepository et GooglePlaces . Si vous pensez que Service n’est pas le bon nom, vous pouvez également l’appeler PlacesDao , le marquer avec une annotation Spring @Component et injecter tous les référentiels , qui sont par définition des collections d’éléments.

 @Component public class PlacesDao { @Autowired private PlaceRepository placeRepository; @Autowired private GooglePlaceRepository googlePlaceRepository; 

Ce service / DAO pourrait offrir une API findPlacesForGroup(userGroup) et createNewPlace(...) et ainsi rendre votre boucle for plus petite et plus élégante.

Remarque: vous pouvez fusionner vos quatre premières lignes en une seule. Les orElseThrow() Java prennent en charge une méthode orElseThrow() :

 UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() -> new EntityNotFoundException(Ssortingng.format("User group with id %s not found.", groupId)); 

Je pense que le foreach ne semble pas être une bonne approche pour moi. Vous faites beaucoup pour une seule responsabilité d’une fonction. Je refactoriserais ceci pour un standart pour la boucle.

  Place place = new Place(); place.setLatitude(googlePlaceResult.geometry.location.lat); place.setLongitude(googlePlaceResult.geometry.location.lng); place.setPlaceType(Place.PlaceType.GOOGLE_PLACE); place.setName(googlePlaceResult.name); place.setVicinity(googlePlaceResult.vicinity); place = this.placeRepository.save(place); 

Cette partie peut facilement être une méthode dans un service.

  UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new UserGroupPlace.UserGroupPlaceId(); userGroupPlaceId.setUserGroup(userGroup); userGroupPlaceId.setPlace(place); UserGroupPlace userGroupPlace = new UserGroupPlace(); userGroupPlace.setUserGroupPlaceId(userGroupPlaceId); this.userGroupPlaceRepository.save(userGroupPlace); 

Cette partie aussi.

  googlePlace = new GooglePlace(); googlePlace.setPlace(place); googlePlace.setGooglePlaceId(googlePlaceResult.placeId); this.googlePlaceRepository.save(googlePlace); 

Et cette partie: je ne comprends pas pourquoi tu fais ça. Vous pouvez simplement mettre à jour l’instance de googlePlace que vous avez chargée depuis le repository. Hibernate / Transactions font le rest pour vous.