When developing custom extensions in SAP Commerce you will be implementing the Converters and Populators pattern a lot in your project. If you look into any of the accelerator code base you will see that this pattern is widely used.
In this post we will take a deep dive and explore why we need to use this pattern, some of the advantages of using this and we will look into how this patten is implemented using an example.
Why do we need them?
In most applications we need to create a subset of data from a larger data structure. For e.g:- we might have a customer table in the database that has 100 fields but we might only need only 10 of them (with some logic applied to them) to display on the UI. Your model object (source) is that larger data structure (that has all the data from the database) and let’s call the subset of data that your screen needs the target data structurethe DTO (Data Transfer Object). We call it the target because we are trying to convert from the source (Model) to the target(DTO).
So using converters and populations we are trying to create a target DTO by converting a source Model object using Populators to populate the DTO.
The below diagram depicts a high level view of what we are trying to accomplish.
I know you might be thinking why can’t we just select the data that we need from the model object and apply the business logic we want to those fields.
A few reasons SAP Commerce defined this as a reusable pattern are:-
1. Extensibility:- Since SAP Commerce is built on an extendable platform, its easier to add additional Popluators if we need to add data to an existing DTO from an extension.
2. Performance:- We typically do not want to send all the data in the model object to a consuming storefront or web service. This pattern allows us to convert and populate only the required fields on the storefront.
3. Testability:- Its easy to develop test cases for code that does the populations by creating mock interfaces for the dependencies of those Populators.
4. Customization:- By delegating the responsibility of populating a target data object to a Populator we can have other extensions add/delete/override data within a target object using custom business logic that they implement.
Now that we have defined what we are trying to do and why we need Converters and Populators and the advantages of using the pattern. Lets see how SAP Commerce implements Converters and Populators.
The typical flow of program execution within SAP Commerce is this.
Controller —> Facades —> Services —> DAO
Converters and Populators exist in the Facade layer.
From the diagram above you might be thinking where do converters fit in?
Converters provide a uniform interface for the facade to invoke all the Populators configured for that Converter.
The below diagram captures the high level flow of how Converters trigger Populators and how the DTO is sent to the Facades.
We will follow along using an example from CMS facades.
The particular example we will pick is responsible for serving responsive images using the ResponsiveMediaFacade. This facade is called by a controller SimpleResponsiveBannerComponentController.
The facades instantiate a model object either by calling the Services layer (for most cases) or by instantiating the model directly. After which, the facade then calls the Converter which returns the DTO that the controller requires for the UI.
Before starting on implementing this you need to have an idea of what data your Storefront requires and from which model object(s) you will need to get that data and also identify if there are any existing Converters that you can extend and modify/add existing Populators.
The following are the steps on how to define converters and Populators.
1. Define a Converter in *facades-spring.xml.
When defining a Converter we need to specify what our target object is. In other words, the DTO that we want our Converter to build.
Note that in this particular the responsiveMediaContainerPopulator needs another Converter to do its job.
All Populators implement the Populator<SOURCE,TARGET> interface and implement the populate method.
The populate method can be as simple as getting and setting values from the Model object (Source) to the target (DTO) or calling Services to get additional data or calling other convertors to get another DTO.
public void populate(final MediaContainerModel source, final List<ImageData> target) throws ConversionException
populateImages(source, target, ImageDataType.GALLERY); //GALLERY by default
3. Create a Facade that has a reference to the Convertor.