Skip to content

How Backend CRUD generic service works?

Łukasz Latusik edited this page May 22, 2020 · 1 revision

What for?

Presented solution was created in order to avoid unnecessary boilerplate code for handling basic CRUD operations for entities represented in database.

Solution is composed from 5 parts which should be base for each entity and can be extended with additional required methods.

  1. EntityModel
@MappedSuperclass
@Getter
@Setter
public abstract class AbstractEntityModel

This class should be base class for all entities which are representing tables in database. It contains all common values for model objects. By using:

@MappedSuperclass

Class extends entities with declared fields without generating additional table. Class defines two common fields:

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "id")
   protected long id;

   @Column(name = "model_code")
   protected @NotBlank String code;

Which is representation of record id in table and code which is generated during object creation and used outside database for object identification.

  1. AbstractModelDto
@Getter
@Setter
public abstract class AbstractModelDto<T extends RepresentationModel<? extends T>>
        extends RepresentationModel<T>

Class is base JSON representation of entities which is send via API or used to created new entity with API.

It contains common property which is object code. In project we use HATEOAS so class extends RepresentationModel to allow adding links to created JSON.

  1. EntityRepository
@NoRepositoryBean
public interface EntityRepository<T,ID> extends CrudRepository<T,ID>

Base class for creating CrudRepository for defined entities. The main purpose is to define common methods used in other parts of solution.

  1. AbstractModelService
@Slf4j
public abstract class AbstractModelService<M extends AbstractEntityModel, T extends AbstractModelDto, R extends EntityRepository>
        implements ModelService<T>
{

This class is responsible for converting passed from controllers DTO's to Model objects and handling CRUD operations delegated form API controller.

Class defines 3 abstract methods:

protected abstract String getModelNameForError();
protected abstract T getDtoFromModel(final M modelObject);
protected abstract M getModelFromDto(final T dtoObject);

First one is responsible for providing prober model name depending on class for which it is a super class. Name is used in case of some error occurence to be appended to error message.

Next two methods defines conversion between DTO and Model object.

  1. BaseModelController
@Validated
public abstract class BaseModelController<T extends AbstractModelDto, S extends AbstractModelService<?,T,?>>

This controller creates generic endpoints for CRUD operations. It is responsible for sending/receiving DTO and delegating work to given service. It defines one abstract method:

public abstract void addLinks(final T dto);

This method is used to append defined links do DTO before sending it via API.

Summary


Providing such a solution allowed us to avoid boilerplate code for handling CRUD operations via API on entities. To provide it you need only to:

  • Create Model which represents entity.
  • Create DTO.
  • Create empty service, repository and controller which extend prepared generic solution.

After this three steps you provided basic CRUD for entities via API. You can add additional methods in created Service and Controller which at start are empty.

In our code You can look for practical examples of usage.

For Patch operation we use ModelPatchService you can read about in in other wiki page.