Rest API Controller (pattern)

Which is the best pattern to use in the controller?

Status

DONE

Impact

HIGH

Driver

@Alessandro Domanico 

Approver

@Alessandro Domanico

Stakeholders

@Niccolò Pasquetto @Vito Romano @Antonio Verni @Roberto C @Riccardo Costa

Informed

 

Due date

Mar 25, 2020

Outcome

Final pattern in the Outcome section

Background

The REST API layer is still under design. Different ideas has been proposed up to now, but still we are not sure which is the best one to adopt for the project.

Initally, the https://openhospital.atlassian.net/browse/OP-4 was proposed. Then the https://openhospital.atlassian.net/browse/OP-6 has started and divided into a number of subtasks.

One of these subtask (the https://openhospital.atlassian.net/browse/OP-118) changed the initial proposal to something different while not yet completed (premature merge) and while other contributors were relying on the initially proposed pattern in other isses (e.g. https://openhospital.atlassian.net/browse/OP-178 and https://openhospital.atlassian.net/browse/OP-127)

So now the point is to adopt one of the two, with an agnostic comparison between different solutions.

Relevant data

  • almost 90% of ModelDTOs in “api” would be identical to Models in “core”

  • some ModelDTO (10%) may need a dedicated mapping

  • the Swagger documentation need to be coherent with the code

Options considered

 

Option 1:

Option 2:

Option 3:

 

Option 1:

Option 2:

Option 3:

Description

Linear mapping

Automapping

AbstractController

Pros and cons

doesn’t need more knowledge for Java Base contributors (more linear)

makes the controllers ticker

offer a centralized point for models mapping

can be customized or disabled where not required

it needs annotations (less linear)

it needs adjustments for Swagger documentation

it binds to a 1:1 mapping that may be useless in the future

it should simplify the writing of a standard controller

it works only for controllers managing one Model/DTO

it binds to a 1:1 mapping that may be useless in the future

it may lead to a “GOD Class” known problem

Estimated cost

Small

small

SMALL

Action items

These are the proposed steps in order to take a decision

@Riccardo Costa continues with the linear mapping on module “OPD” as per his own OP-118
@Uni2grow Cameroun continues with his own work on the OP-115
@Antonio Verni propose a change to the initial pattern in order to make it not exlusive and applying it on other OP-6 unassigned subtasks (e.g. OP-119)
@Vito Romano continues his own work on OP-116 trying using the the change to the initial pattern coming from Antonio
Other Contributors to feel free to follow one of the two patterns or to propose a different one in their developments
@Alessandro Domanico To decide the final pattern before the end of March

Outcome

The final pattern approved pattern (temporary but the same for all OP-6 subtasks) is the following and it is implemented here https://github.com/informatici/openhospital-api/tree/OP-6-new-proposed-pattern

/openhospital-api/src/main/java/org/isf/shared/mapper/converter/ModelMapperConfig.java

package org.isf.shared.mapper.converter; @Configuration public class ModelMapperConfig { @Autowired protected BlobToByteArrayConverter blobToByteArrayConverter; @Autowired protected ByteArrayToBlobConverter byteArrayToBlobConverter; @Bean public ModelMapper modelMapper() { ModelMapper modelMapper = new ModelMapper(); modelMapper.addConverter(blobToByteArrayConverter); modelMapper.addConverter(byteArrayToBlobConverter); return modelMapper; } }

 

/openhospital-api/src/main/java/org/isf/shared/mapper/Mapper.java

package org.isf.shared.mapper; public interface Mapper <FromType, ToType> { public ToType map2DTO(FromType fromObj); public FromType map2Model(ToType toObj); public List<ToType> map2DTOList(List<FromType> list); }

 

/openhospital-api/src/main/java/org/isf/shared/mapper/GenericMapper.java

package org.isf.shared.mapper; public class GenericMapper<SourceType, DestType> implements Mapper<SourceType, DestType> { @Autowired protected ModelMapper modelMapper; private Type sourceClass; private Type destClass; public GenericMapper(Class<SourceType> sourceClass, Class<DestType> destClass) { this.sourceClass = sourceClass; this.destClass = destClass; } @Override public DestType map2DTO(SourceType fromObj) { return modelMapper.map(fromObj, destClass); } @Override public SourceType map2Model(DestType toObj) { return modelMapper.map(toObj, sourceClass); } @Override public List<DestType> map2DTOList(List<SourceType> list) { return (List<DestType>) list.stream().map(it -> modelMapper.map(it, destClass)).collect(Collectors.toList()); } }

 

/openhospital-api/src/main/java/org/isf/patient/mapper/PatientMapper.java (example)

package org.isf.patient.mapper; @Component public class PatientMapper extends GenericMapper<Patient, PatientDTO> { public PatientMapper() { super(Patient.class, PatientDTO.class); } }

 

Then in /openhospital-api/src/main/java/org/isf/patient/rest/PatientController.java (example)

@Autowired protected PatientMapper mapper; ... boolean isCreated = patientManager.newPatient(mapper.map2Model(newPatient)); ... List<PatientDTO> patientDTOS = mapper.map2DTOList(patients); ... return ResponseEntity.ok(mapper.map2DTO(patient)); ...

 

 

Open Hospital powered by ISF
2005 - 2016 ISF © Informatici senza frontiere - ONLUS