2
\$\begingroup\$

I am a young computer science student programming mainly in Java.

I wrote a simple RESTful web service using Spring Boot Framework and the DDD architecture pattern. The project is a library management system that allows users to browse, book and borrow books.

I was wondering if I could get feedback from more experienced programmers about my code in general.

Here is the entire project repository: Project repository

@NamedEntityGraph(
        name = "book-entity-graph",
        attributeNodes = {
                @NamedAttributeNode(value = "author"),
                @NamedAttributeNode(value = "genre"),
                @NamedAttributeNode(value = "publisher")
        }
)
@AggregateRoot
@Entity
public class Book extends BaseEntity {

    @Column(nullable = false)
    private String title;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Author author;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Genre genre;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Publisher publisher;

    @Column(nullable = false)
    private String description;

    @Column(nullable = false)
    private LocalDate publicationDate;

    private boolean loaned;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "book")
    @OrderColumn(name = "index")
    private List<Reservation> reservations;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "book")
    private List<Loan> loans;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "book")
    private List<Rating> ratings;

    private double averageRating = 0.; // Additional field to increase performance

    protected Book() {
        reservations = new ArrayList<>();
        loans = new ArrayList<>();
        ratings = new ArrayList<>();
    }

    public Book(String title, Author author, Genre genre, Publisher publisher, String description,
                LocalDate publicationDate) {
        this();
        setTitle(title);
        setAuthor(author);
        setGenre(genre);
        setPublisher(publisher);
        setDescription(description);
        setPublicationDate(publicationDate);
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = Objects.requireNonNull(title);
    }

    public Author getAuthor() {
        return author;
    }

    private void setAuthor(Author author) {
        this.author = Objects.requireNonNull(author);

        if (!author.getBooks().contains(this)) {
            author.addBook(this);
        }
    }

    public void changeAuthor(Author author) {
        Objects.requireNonNull(author);
        this.author.removeBook(this);
        setAuthor(author);
    }

    public Genre getGenre() {
        return genre;
    }

    private void setGenre(Genre genre) {
        this.genre = Objects.requireNonNull(genre);

        if (!genre.getBooks().contains(this)) {
            genre.addBook(this);
        }
    }

    public void changeGenre(Genre genre) {
        Objects.requireNonNull(genre);
        this.genre.removeBook(this);
        setGenre(genre);
    }

    public Publisher getPublisher() {
        return publisher;
    }

    private void setPublisher(Publisher publisher) {
        this.publisher = Objects.requireNonNull(publisher);

        if (!publisher.getBooks().contains(this)) {
            publisher.addBook(this);
        }
    }

    public void changePublisher(Publisher publisher) {
        Objects.requireNonNull(publisher);
        this.publisher.removeBook(this);
        setPublisher(publisher);
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = Objects.requireNonNull(description);
    }

    public LocalDate getPublicationDate() {
        return publicationDate;
    }

    public void setPublicationDate(LocalDate publicationDate) {
        this.publicationDate = Objects.requireNonNull(publicationDate);
    }

    public boolean isLoaned() {
        return loaned;
    }

    public void setLoaned(boolean loaned) {
        this.loaned = loaned;
    }

    public List<Reservation> getReservations() {
        return Collections.unmodifiableList(reservations);
    }

    public List<Reservation> getReservations(Reservation.Status status) {
        return reservations.stream()
                .filter(r -> r.getStatus() == status)
                .collect(Collectors.toUnmodifiableList());
    }

    public void addReservation(Reservation reservation) {
        Objects.requireNonNull(reservation);

        if (!reservation.getBook().equals(this)) {
            throw new IllegalStateException("The reservation has a different book");
        }
        reservations.add(reservation);
    }

    public List<Loan> getLoans() {
        return Collections.unmodifiableList(loans);
    }

    public void addLoan(Loan loan) {
        Objects.requireNonNull(loan);

        if (!loan.getBook().equals(this)) {
            throw new IllegalArgumentException("The loan has a different book");
        }
        loans.add(loan);
    }

    public List<Rating> getRatings() {
        return Collections.unmodifiableList(ratings);
    }

    public void addRating(Rating rating) {
        Objects.requireNonNull(rating);

        if (!rating.getBook().equals(this)) {
            throw new IllegalArgumentException("The rating has a different book");
        }
        averageRating = (averageRating * ratings.size() + rating.getValue()) / (ratings.size() + 1);
        ratings.add(rating);
    }

    public void removeRating(Rating rating) {
        if (!ratings.contains(rating)) {
            throw new IllegalArgumentException("The book doesn't contain this rating");
        }
        if (ratings.size() > 1) {
            averageRating = (averageRating * ratings.size() - rating.getValue()) / (ratings.size() - 1);
        } else {
            averageRating = 0.;
        }
        ratings.remove(rating);
    }

    public double getAverageRating() {
        return averageRating;
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Code Review. I have a question: a Book instance is a physical copy of one book present in the library? \$\endgroup\$ Mar 10, 2020 at 13:33
  • \$\begingroup\$ @dariosicily Yes. To simplify the model, each book has only one copy. \$\endgroup\$ Mar 10, 2020 at 15:48

1 Answer 1

1
\$\begingroup\$

I checked your code from project repository and one feedback from my end -

Use proper HTTP response codes as mentioned here.

You have used 200 for POST which can be 201 (CREATED)

and 201 for DELETE requests which can be 200 (OK) or 202 (Not Accepted) or 204 (No Content)

\$\endgroup\$
2
  • \$\begingroup\$ Only code included in the question should be reviewed. Links to more code can sometimes be useful but the code in them should not be reviewed (one reason is that the link can break or the code there can change). \$\endgroup\$ Apr 28, 2020 at 0:45
  • \$\begingroup\$ Oh.. Thanks for head up 👍 \$\endgroup\$ Apr 28, 2020 at 0:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.