Search code examples
javaspringjpaspring-data-jpa

Building a domain model independent from JPA @PersistenceCreator or @NoArgsConstructor


Ok, I'm doing a sample project on which I'm documenting studies of different kinds. I'm trying to refactor the project to follow a hexagonal-ish (hopefully hexagonal someday) architecture.

I currently have this domain class:

package com.kaue.ticketservice.domain.model;
import io.swagger.v3.oas.annotations.media.Schema;

import lombok.*;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.PersistenceCreator;

import java.time.Instant;
import java.time.OffsetDateTime;

@ToString
public class Ticket {
    @Schema(description = "User email")
    @Getter
    final private String requesterEmail;
    @Schema(description = "Assignee / Agent email")
    @Getter @Setter
    private String assigneeEmail;
    @Schema(description = "Title of the ticket")
    @Getter
    final private String title;
    @Schema(description = "Ticket description")
    @Getter @Setter
    private String description;
    @Schema(description = "Create date")
    @Getter
    final private Instant createDate;
    @Getter @Setter @LastModifiedDate
    private Instant  updatedDate;
    @Schema(description = "Resolution date")
    @Getter @Setter
    private OffsetDateTime resolutionDate;

    public Ticket(String requesterEmail, String title, String description) {
        this.requesterEmail = requesterEmail;
        this.title = title;
        this.description = description;
        this.createDate = Instant.now();
    }
    @PersistenceCreator /* infrastucture code, we need to fix this.*/
    private Ticket(String requesterEmail, String assigneeEmail, String title, String description, Instant createDate, Instant updatedDate, OffsetDateTime resolutionDate) {
        this.requesterEmail = requesterEmail;
        this.assigneeEmail = assigneeEmail;
        this.title = title;
        this.description = description;
        this.createDate = createDate;
        this.updatedDate = updatedDate;
        this.resolutionDate = resolutionDate;
    }
}

The catch is: I need the @PersistenceCreator constructor with all fields so my repository can return a list of tickets.It makes sense that the domain class also needs a constructor with all fields, which should the repository use. However, to indicate to JPA / Mongo that it needs to use the constructor, I need to put @PersistenceCreator.

I want to know which ways I have to refactor the code and make it infrastructure-redundant

I tried the code without annotations, expecting Spring Data MongoDb would use it automatically


Solution

  • By reading the spring data documentation I found this

    Use factory methods instead of overloaded constructors to avoid @PersistenceCreator — With an all-argument constructor needed for optimal performance, we usually want to expose more application use case specific constructors that omit things like auto-generated identifiers etc. It’s an established pattern to rather use static factory methods to expose these variants of the all-args constructor.

    The alternative is:

    1. Keep a public all args constructor
    2. Create a factory Method and use it for other domain purposes
    package com.kaue.ticketservice.domain.model.factories;
    
    import com.kaue.ticketservice.domain.model.Ticket;
    
    import java.time.Instant;
    
    public class TicketFactory {
      public static Ticket createTicket(String requesterEmail, String title, String description){
        return new Ticket(requesterEmail,null,title,description,Instant.now(),null,null);
      }
    }
    
    

    (it can also be done as a method in ticket class)