The Case for BeanMapper

Introduction

For a Spring Web developer, the situation is probably well known; you have an Entity, defined as a class that is persisted to some kind of persistence layer. The Entity must be partially exposed to the outside world and the Entity must be creatable and updatable.

mapping_with_entities

Typically, Jackson would be instructed to map the JSON to the Entity when a create/update takes place, and the Entity to JSON when a read request takes place.

Let us assume we have the following Entity class (JPA annotations removed):


public class Car {
    private Long id;
    private String overrideCode;
    private String licensePlate; 
    private String owner;
    private List relatedCars; // deep fetch tree

    // appropriate getters & setters
}

Problem - Lack of control

Let us suppose that the domain requires that the overrideCode must not be exposed. Also, the owner is set once and may not be overwritten.

Issue I - Clean the input

By default, all fields from the JSON object will be mapped to the Entity. Therefore it is possible to:

  • set your own ID and force and Entity to be merged on that basis
  • set an owner different from the one that Car had

The application will then have to scrub the entity to make sure it was not passed values it should not be able to update. For example, the ID might have to be checked against the authorities of the current user and the owner field must be taken from the existing record if it already exists.

Issue II - Clean the output

When the object is mapped back to JSON, the secret code must be scrubbed from the output. This could be done by annotating the entity with JSON specific instructions to make sure the field is scrubbed.

Showing the related cars in the result, leads to a large JSON file with lots of data that is not appropriate for the calling system. This field must be scrubbed as well to prevent fetches of these records.

Consciously scrubbing

Bottom line is that the default is to pass everything, requiring a conscious decision on the side of the developer to scrub data, both for incoming and outgoing traffic.

Failing to foresee what must be scrubbed results in a failure to uphold an implicit contract, at best introducing mild data leakage and at worst critical security flaws.

Solution - Mapping with intermediate objects

It would be possible to work on the basis of Data Transfer Objects (DTOs). In this case JSON will be transformed into a Form (incoming DTO), before being transformed into an Entity. The Entity will be transformed into a Result (outgoing DTO), before being transformed into JSON.

mapping_with_intermediate_objects In our example, the Form looks like this:

public class CarForm {
    public String overrideCode;
    public String licensePlate; 
}

ID is not passed, since this is a given. Owner is not passed either, since we decided it cannot be changed in our domain. Related cars are probably determined somewhere else in the application, so no use to pass those. The form becomes very simple and contains only what we need.

The Result looks like this


public class CarResult {
    public Long id;
    public String licensePlate; 
    public String owner;
}

Now we do pass the ID, since it helps our consumer to retrieve the object or to initiate an update call. The overrideCode is dropped, because we do not want it to be exposed. Also, the related cars are dropped, because we do not want to trigger the fetches and we do not require them.

New problem - Lots of manual mappings

Regrettably, we now have a new problem. Our application becomes responsible for mapping from Result to Entity and from Entity to Form:


// Mapping from Form to Entity
Car car = new Car();
car.setOverrideCode(form.overrideCode);
car.setLicensePlate(form.licensePlate);

// Mapping from Entity to Result
CarResult carResult = new CarResult();
carResult.id = car.getId();
carResult.licensePlate = car.getLicensePlate();
carResult.owner = car.getOwner();

manual_mapping

This logic is very brittle, since for an Entity it will need to be maintained in two different places. It is no wonder that confronted by this situation, scrubbing does not seem so bad.

Solution - Enter BeanMapper

How about if mapping from Form to Entity and from Entity to Result can be done automatically? Let us suppose we have a tool that is able to map similar fields from dissimilar classes. In this case, it would be just a matter of passing both instances and delegating the task of mapping from source to target to this tool.

BeanMapper does just that:


BeanMapper beanMapper = new BeanMapper();

// Mapping from Form to Entity
Car car = beanMapper.map(form, Car.class);

// Mapping from Entity to Result
CarResult carResult = beanMapper.map(car, CarResult.class);

Fields that do not exist, are simply not mapped. It does what you expect at virtually no cost. There are many ways you can configure and guide the BeanMapper, which is well beyond the scope of this article. If the above case sounds familiar to you, it is worth checking out BeanMapper

Later articles will show in-depth examples.