Nazewnictwo:
- Nazwy pakietów z dużej litery - tego ogólnie nie robimy. Np. zamiast
com.example.carrentalsystem.Controllers
powinno być com.example.carrentalsystem.controllers
- Nazywanie enumów od
E
, np. ERole
. Zazwyczaj pisze się RoleEnum
, ewentualnie Roles
(chociaż to dużo rzadsze).
- Korzystaj z camelCase i nie używaj podkreślnika w nazwach (np.
getBrand_Name()
-> getBrandName()
)
Design:
- Brak warstwy serwisów, cała logika siedzi w kontrolerach
- Brak testów jednostkowych
- Z jakiegoś powodu
Rental.price
jest Integerem, a nie Longiem, przy czym w samym kodzie jest sprawdzenie, czy przy liczeniu nowej ceny licznik się nie przekręci
- Używasz
LocalDate.now()
zamiast oddzielnego serwisu do dat - takie coś utrudnia testowalność.
Kod:
- Dziwne nullchecki. Np.
Kopiuj
Brand brand;
if (brandRepository.findByName(carRequest.getBrand()) != null)
brand = brandRepository.findByName(carRequest.getBrand);
} else {
brand = brandRepository.save(new Brand(carRequest.getBrand()));
}
Oznacza to, że zawołasz bazę danych dwa razy - raz, żeby sprawdzić, czy dany brand istnieje, drugi - żeby przypisać. Nie ma to większego sensu i można napisać to tak:
Kopiuj
Brand brand = brandRepository.findByName(carRequest.getBrand());
if (brand == null) {
brand = brandRepository.save(new Brand(carRequest.getBrand()));
}
Albo jeszcze lepiej - użyć optionala:
Kopiuj
Brand brand = Optional.ofNullable(brandRepository.findByName(carRequest.getBrand()))
.orElseGet(() -> brandRepository.save(new Brand(carRequest.getBrand()));
Ewentualnie przerobić BrandRepository.findByName(String)
tak, żeby zwracało Optional<Brand>
. Tego typu rzeczy jest trochę w kodzie - to tylko jeden z przykładów.
- Ogólnie zacznij wydzielać kod do oddzielnych metod lub klas (to się łączy z poprzednim komentarzem o kontrolerach). Na przykładzie
RentalController.changeRentalInformation(EditCarRentalRequest)
:
Jest:
Kopiuj
public ResponseEntity<?> changeRentalInformation(@RequestBody @Valid EditCarRentalRequest request){
if(rentalRepository.existsById(request.getRentId())){
Rental rental = rentalRepository.getReferenceById(request.getRentId());
rental.setPrice(Math.toIntExact((ChronoUnit.DAYS.between(request.getStartDate(), request.getEndDate())+1) * rental.getCar().getPrice()));
rental.setStartDate(request.getStartDate());
rental.setEndDate(request.getEndDate());
rentalRepository.save(rental);
return new ResponseEntity<>("Rent details changed", HttpStatus.OK);
}
return new ResponseEntity<>("No rental found", HttpStatus.NOT_FOUND);
}
}
Napisałbym to mniej-więcej tak:
Kopiuj
public ResponseEntity<?> changeRentalInformation(@RequestBody @Valid EditCarRentalRequest request){
if(rentalRepository.existsById(request.getRentId())){
Rental rental = rentalRepository.getReferenceById(request.getRentId());
updateEntity(rental, request);
rentalRepository.save(rental);
return new ResponseEntity<>("Rent details changed", HttpStatus.OK);
}
return new ResponseEntity<>("No rental found", HttpStatus.NOT_FOUND);
}
}
private void updateEntity(Rental entity, EditCarRentalRequest request) {
entity.setPrice(Math.toIntExact((ChronoUnit.DAYS.between(request.getStartDate(), request.getEndDate())+1) * rental.getCar().getPrice()));
entity.setStartDate(request.getStartDate());
entity.setEndDate(request.getEndDate());
}
Oczywiście w tym przypadku może nie widać zbytnich korzyści z tego, ale już jeśli zrobisz coś podobnego przy CarController.editCar(EditCarRequest)
to różnica będzie duża.
- Brak spójności - czasem sprawdzasz, czy encja istnieje w bazie danych przez
existsBy
, czasem pobierając listę przez findAll
i sprawdzając, czy są elementy, a czasem przez countBy
.
- Brak spójności x2 - tj. w niektórych repozytoriach używasz
findById
, a np. CarRepository
ma już getCarById
który robi praktycznie to samo