Microservices
Within our system, five main types of operations occur:
-
Getting data by ID: This operation involves retrieving specific data entries from the microservice based on their unique identifier (ID). This allows for the retrieval of individual records as needed.
-
Getting all data: This operation involves retrieving all available data from the microservice.
-
Deletion: Deleting existing records. This typically involves interacting with the microservice to remove specific records.
-
Update of existing entries: This refers to making changes by sending data to the microservice for processing and storage.
-
Creation: This involves creating new records by sending data to the microservice for processing and storage.
Basics
When creating entities for microservices, the process is largely similar to creating entities for database access, with a few notable exceptions.
1) Instead of creating an entity, we establish a mapping entity through which data will be sent to the microservice.
2) Instead of using FieldMetaBuilder, we utilize AnySourceFieldMetaBuilder._
3) Instead of using VersionAwareResponseService, we utilize AnySourceVersionAwareResponseService.
4) Create extends AbstractAnySourceBaseDAO implements AnySourceBaseDAO service
Example
- Step1.1 Create mapping entity through which data will be sent to the microservice
-
Step1.2 Create DAO extends AbstractAnySourceBaseDAO implements AnySourceBaseDAO Override methods:
-
Getting data by ID: method getByIdIgnoringFirstLevelCache
-
Getting all data: method getList
-
Deletion: method delete
-
Update of existing entries: method update
-
Create : method create
@Service @RequiredArgsConstructor public class MyEntityDao extends AbstractAnySourceBaseDAO<MyEntityOutServiceDTO> implements AnySourceBaseDAO<MyEntityOutServiceDTO> { private final IntegrationConfiguration integrationConfig; private final RestTemplate restTemplate; @Override public String getId(final MyEntityOutServiceDTO entity) { return entity.getId(); } @Override public void setId(final String id, final MyEntityOutServiceDTO entity) { entity.setId(id); } // --8<-- [start:getByIdIgnoringFirstLevelCache] @Override public MyEntityOutServiceDTO getByIdIgnoringFirstLevelCache(final BusinessComponent bc) { return restTemplate.exchange( fromUriString(integrationConfig.getExistingMicroservicesDataServerUrl() + "/{id}").build() .expand(bc.getIdAsLong()).normalize().encode() .toUriString(), GET, null, MyEntityOutServiceDTO.class ).getBody(); } // --8<-- [end:getByIdIgnoringFirstLevelCache] // --8<-- [start:getList] @Override public Page<MyEntityOutServiceDTO> getList(final BusinessComponent bc, final QueryParameters queryParameters) { //Page size String page = bc.getParameters().getParameter("_page"); //Limit String limit = bc.getParameters().getParameter("_limit"); //Filter List<String> filterCustomField = getFilterFieldName(queryParameters, "customField", "contains"); Optional<String> filter = filterCustomField.isEmpty() ? Optional.empty() : Optional.of(filterCustomField.get(0)); //Sorting List<String> sortCustomField = getSortFieldName(queryParameters, "customField"); Optional<String> sort = sortCustomField.isEmpty() ? Optional.empty() : Optional.of(sortCustomField.get(0)); String urlTemplate = UriComponentsBuilder.fromHttpUrl(integrationConfig.getExistingMicroservicesDataServerUrl()) .queryParam("number", page) .queryParam("size", limit) .queryParamIfPresent("filterCustomField", filter) .queryParamIfPresent("sortCustomField", sort) .encode() .toUriString(); ResponseEntity<RestResponsePage<MyEntityOutServiceDTO>> responseEntity = restTemplate.exchange( urlTemplate, HttpMethod.GET, null, new ParameterizedTypeReference<>() { }, filter ); return responseEntity.getBody(); } private List<String> getSortFieldName(QueryParameters queryParameters, String fieldName) { return queryParameters.getParameters().entrySet().stream() .filter(f -> f.getKey().contains("_sort")) .filter(f -> f.getValue().contains(fieldName)) .map(m -> { String[] splitOperation = m.getKey().split("\\."); return splitOperation[splitOperation.length - 1]; } ).toList(); } private List<String> getFilterFieldName(QueryParameters queryParameters, String fieldName, String searchSpec) { return queryParameters.getParameters().entrySet().stream() .filter(f -> f.getKey().contains(fieldName + "." + searchSpec)) .map(Map.Entry::getValue) .toList(); } // --8<-- [end:getList] @Override // --8<-- [start:delete] public void delete(BusinessComponent bc) { restTemplate.exchange( fromUriString(integrationConfig.getExistingMicroservicesDataServerUrl() + "/{id}").build().expand(bc.getIdAsLong()).normalize().encode() .toUriString(), DELETE, null, Void.class ); } // --8<-- [end:delete] @Override // --8<-- [start:create] public MyEntityOutServiceDTO create(BusinessComponent bc, MyEntityOutServiceDTO entity) { entity.setId(null); return restTemplate.exchange( fromUriString(integrationConfig.getExistingMicroservicesDataServerUrl()).build().normalize().encode().toUriString(), POST, new HttpEntity<>(entity), MyEntityOutServiceDTO.class ).getBody(); } // --8<-- [end:create] @Override // --8<-- [start:update] public MyEntityOutServiceDTO update(BusinessComponent bc, MyEntityOutServiceDTO entity) { return restTemplate.exchange( fromUriString(integrationConfig.getExistingMicroservicesDataServerUrl()).build().normalize().encode().toUriString(), PUT, new HttpEntity<>(entity), MyEntityOutServiceDTO.class ).getBody(); } // --8<-- [end:update] }
-
-
Step1.3 Create DTO extends DataResponseDTO Creating fields in DTO with the necessary properties, such as, for example,filtering is described in the article field types
@Getter @Setter @NoArgsConstructor public class MyExampleDTO extends DataResponseDTO { @SearchParameter(name = "customField", provider = StringValueProvider.class) private String customField; public MyExampleDTO(MyEntityOutServiceDTO entity) { this.id = entity.getId(); this.customField = entity.getCustomField(); } }
-
Step1.4 Create MetaBuilder extends AnySourceFieldMetaBuilder
see more Meta builder
@Service public class MyExampleMeta extends AnySourceFieldMetaBuilder<MyExampleDTO> { // --8<-- [start:buildRowDependentMeta] @Override public void buildRowDependentMeta(RowDependentFieldsMeta<MyExampleDTO> fields, BcDescription bc, String id, String parentId) { fields.setEnabled(MyExampleDTO_.customField); } // --8<-- [end:buildRowDependentMeta] // --8<-- [start:buildIndependentMeta] @Override public void buildIndependentMeta(FieldsMeta<MyExampleDTO> fields, BcDescription bcDescription, String parentId) { fields.enableFilter(MyExampleDTO_.customField); fields.enableSort(MyExampleDTO_.customField); } // --8<-- [end:buildIndependentMeta] }
-
Step1.5 Create Service extends AnySourceVersionAwareResponseService
@Service public class MyExampleService extends AnySourceVersionAwareResponseService<MyExampleDTO, MyEntityOutServiceDTO> { public MyExampleService() { super(MyExampleDTO.class, MyEntityOutServiceDTO.class, MyExampleMeta.class, MyEntityDao.class); } @Override protected CreateResult<MyExampleDTO> doCreateEntity(MyEntityOutServiceDTO entity, BusinessComponent bc) { return new CreateResult<>(entityToDto(bc, entity)); } @Override protected ActionResultDTO<MyExampleDTO> doUpdateEntity(MyEntityOutServiceDTO entity, MyExampleDTO data, BusinessComponent bc) { if (data.isFieldChanged(MyExampleDTO_.customField)) { entity.setCustomField(data.getCustomField()); } return new ActionResultDTO<>(entityToDto(bc, entity)); } // --8<-- [start:getActions] @Override public Actions<MyExampleDTO> getActions() { return Actions.<MyExampleDTO>builder() .create(crt -> crt.text("Add")) .addGroup( "actions", "Actions", 0, Actions.<MyExampleDTO>builder() .action(act -> act .action("delete", "delete") ) .action(act -> act .action("save", "save") ) .build()). build(); } }
-
Step1.6 Create PlatformController implements EnumBcIdentifier
@Getter public enum PlatformMyExampleController implements EnumBcIdentifier { myExampleBc(MyExampleService.class); public static final EnumBcIdentifier.Holder<PlatformMyExampleController> Holder = new Holder<>( PlatformMyExampleController.class); private final BcDescription bcDescription; PlatformMyExampleController(String parentName, Class<?> serviceClass, boolean refresh) { this.bcDescription = buildDescription(parentName, serviceClass, refresh); } PlatformMyExampleController(Class<?> serviceClass) { this((String) null, serviceClass, false); } @Component public static class BcSupplier extends AbstractEnumBcSupplier<PlatformMyExampleController> { public BcSupplier() { super(PlatformMyExampleController.Holder); } } }
Methods
Getting data by ID (getByIdIgnoringFirstLevelCache)
Tips
In this example, we're addressing the scenario where the service obtaining data only by ID. If your service relies solely on natural keys for data retrieval, you may find the following article helpful.
Example
Step1 Method getByIdIgnoringFirstLevelCache
takes a BusinessComponent as input.
When calling the service, it's essential to provide the Id record as a parameter for which data will be returned.
Long Id = bc.getIdAsLong().
Example of fetching data using REST:
@Override
public MyEntityOutServiceDTO getByIdIgnoringFirstLevelCache(final BusinessComponent bc) {
return restTemplate.exchange(
fromUriString(integrationConfig.getExistingMicroservicesDataServerUrl() + "/{id}").build()
.expand(bc.getIdAsLong()).normalize().encode()
.toUriString(),
GET, null, MyEntityOutServiceDTO.class
).getBody();
}
Getting data all (getList)
This method incorporates additional peculiaritys such as filtering, sorting, record limits, and page numbers.
-
Page size: This parameter refers to the number of items or records displayed on a single page of a user interface or returned in a single response from an API. It's used in pagination systems to control how much data is fetched or displayed at once.
-
Limit: This parameter sets a maximum limit on the number of items or records that can be returned or processed by the service. It's a way to prevent overloading the system with too much data at once.
-
Filter: This parameter allows users to specify criteria for filtering the data they want to retrieve or process. Filters could be based on various attributes or properties of the data, allowing users to narrow down their results to only the items that meet specific conditions.
-
Sorting: This parameter would involve specifying the order in which the results are presented. Might want to sort data based on certain attributes, such as alphabetical order, numerical order, date, etc. Sorting can typically be done in ascending or descending order.
Combining these parameters allows users to control and customize the behavior of the service according to their needs, enabling efficient data retrieval and processing.
Example
Method getList
takes a BusinessComponent as input.
Step1 Page size.
Step2 Limit.
Step3 Sorting.
If the application lacks a sorting feature, it implies that the parameter associated with sorting would be absent.
queryParameters.getParameters().entrySet().stream().filter(f->f.getKey().contains("sort")).toList();
In the map key, receive the sorting direction: 'desc' for descending or 'asc' for ascending
For example, map key = _sort.0.desc
In the map value, obtain the name of the filtered field specified in to corresponding field "key" to corresponding file widget.json
For example, map value = customField
Step4 Filter.
If the application lacks a filtration feature, it implies that the parameter associated with filtration would be absent.
This example demonstrates how to select filtering conditions for a field with the String type. For comprehensive information on all fields available for filtering, please refer to the article
queryParameters.getParameters().entrySet().stream().filter(f->f.getKey().contains("contains")).toList();
In the map key, receive the filtration type. Can observe the relationship between standard filtering and standard field types here .
For example, map key = customField.contains
In the map value, obtain the filtering criteria for selecting specific data.
For example, map value = Test data
@Override
public Page<MyEntityOutServiceDTO> getList(final BusinessComponent bc, final QueryParameters queryParameters) {
//Page size
String page = bc.getParameters().getParameter("_page");
//Limit
String limit = bc.getParameters().getParameter("_limit");
//Filter
List<String> filterCustomField = getFilterFieldName(queryParameters, "customField", "contains");
Optional<String> filter = filterCustomField.isEmpty() ? Optional.empty() : Optional.of(filterCustomField.get(0));
//Sorting
List<String> sortCustomField = getSortFieldName(queryParameters, "customField");
Optional<String> sort = sortCustomField.isEmpty() ? Optional.empty() : Optional.of(sortCustomField.get(0));
String urlTemplate = UriComponentsBuilder.fromHttpUrl(integrationConfig.getExistingMicroservicesDataServerUrl())
.queryParam("number", page)
.queryParam("size", limit)
.queryParamIfPresent("filterCustomField", filter)
.queryParamIfPresent("sortCustomField", sort)
.encode()
.toUriString();
ResponseEntity<RestResponsePage<MyEntityOutServiceDTO>> responseEntity = restTemplate.exchange(
urlTemplate,
HttpMethod.GET,
null,
new ParameterizedTypeReference<>() {
},
filter
);
return responseEntity.getBody();
}
private List<String> getSortFieldName(QueryParameters queryParameters, String fieldName) {
return queryParameters.getParameters().entrySet().stream()
.filter(f -> f.getKey().contains("_sort"))
.filter(f -> f.getValue().contains(fieldName))
.map(m -> {
String[] splitOperation = m.getKey().split("\\.");
return splitOperation[splitOperation.length - 1];
}
).toList();
}
private List<String> getFilterFieldName(QueryParameters queryParameters, String fieldName, String searchSpec) {
return queryParameters.getParameters().entrySet().stream()
.filter(f -> f.getKey().contains(fieldName + "." + searchSpec))
.map(Map.Entry::getValue)
.toList();
}
Delete
Tips
In this example, we're addressing the scenario where the service obtaining data only by ID. If your service relies solely on natural keys for data retrieval, you may find the following article helpful.
Example
Step1 Method delete
takes a BusinessComponent as input.
When calling the service, it's essential to provide the Id record as a parameter for which data will be returned.
Long Id = bc.getIdAsLong().
Example of fetching data using REST:
Update
Tips
In this example, we're addressing the scenario where the service obtaining data only by ID. If your service relies solely on natural keys for data retrieval, you may find the following article helpful.
Example
Step1 Method update
takes a BusinessComponent as input.
When calling the service, it's essential to provide theId record as a parameter for which data will be returned.
Long Id = bc.getIdAsLong().
Example of fetching data using REST:
public MyEntityOutServiceDTO update(BusinessComponent bc, MyEntityOutServiceDTO entity) {
return restTemplate.exchange(
fromUriString(integrationConfig.getExistingMicroservicesDataServerUrl()).build().normalize().encode().toUriString(),
PUT, new HttpEntity<>(entity), MyEntityOutServiceDTO.class
).getBody();
}
Create
Tips
In this example, we're addressing the scenario where the service obtaining data only by ID. If your service relies solely on natural keys for data retrieval, you may find the following article helpful.
Example
Step1 Method create
takes a BusinessComponent as input.
When calling the service, it's essential to provide the Id record as a parameter for which data will be returned.
Long Id = bc.getIdAsLong().
Example of fetching data using REST:
public MyEntityOutServiceDTO create(BusinessComponent bc, MyEntityOutServiceDTO entity) {
entity.setId(null);
return restTemplate.exchange(
fromUriString(integrationConfig.getExistingMicroservicesDataServerUrl()).build().normalize().encode().toUriString(),
POST, new HttpEntity<>(entity), MyEntityOutServiceDTO.class
).getBody();
}