---
title: Refactor the elements backend
tags: análise BTB
---
# Refactor the elements backend
## Prós e contras
:heavy_check_mark: Organização do codigo
`MetaScreenElementValidator` está com mais de 1800 linhas
:heavy_check_mark: Facilitar manutenção
Aplicar regras de ordenação hoje é laboroso
:heavy_check_mark: Facilitar evolução
:heavy_multiplication_x: Recursos
## O que deve ser feito
### Tipos
Hoje os elementos são divididos em dois tipos, são eles:
- `ListMetaScreenElement`
- Serve para identificar os elementos que podem ser adicionados na tela de listagem.
- `FormMetaScreenElement`
- Serve para identificar os elementos que podem ser adicionados no formulário.
Apenas esses tipos de elementos estam se tornando insuficentes, pois quando foram criados esses tipos não havia elementos como tabs, sessão, form, list, entre outros que podem surgir futuramente.
- `ColumnMetaScreenElement`
- Para elementos que podem ser adicionados a listas
- `InputMetaScreenElement`
- Para elementos que são de inserção e apresentação de dados do formulário
- `WrapperMetaScreenElement`
- Para elementos que agrupam outros elementos
#### ColumnMetaScreenElement
Atualmente não é nessessário nenhuma alteração além da renomeação da classe pai de `ListMetaScreenElement` para `ColumnMetaScreenElement`.
- `BooleanColumnElement`
- `DateColumnElement`
- `DateTimeColumnElement`
- `FormulaColumnElement`
- `LookupColumnElement`
- `NumberColumnElement`
- `RemoteLookupColumnElement`
- `TextColumnElement`
Esses elementos devem estar vinculados a um atributo.
#### InputMetaScreenElement
Renomear a classe pai de `FormMetaScreenElement` para `InputMetaScreenElement` e aplicar as interfaces de label, helper e required.
- `FormulaElement`
- `InputDateElement`
- `InputDateTimeElement`
- `InputNumberElement`
- `InputTextAreaElement`
- `InputTextElement`
- `LookupElement`
- `RemoteLookupElement`
- `SwitchElement`
Esses elementos devem estar vinculados a um atributo.
#### WrapperMetaScreenElement
Renomear a classe pai de `FormMetaScreenElement` para `WrapperMetaScreenElement` e aplicar as interfaces de label, helper e required.
- `FormElement`
- `ListElement`
- `ListWithActionsElement`
- Validar se wrapper é a melhor opção para esse elemento, ou se deveria ficar como `InputMetaScreenElement`
- `ModalElement`
- `SectionElement`
- `TabElement`
### Propridades compartilhadas entre tipos
Para propriedade compartilhadas entre tipos, podem ser utilizadas interfaces, como é feito hoje com o helper.
- Label
- Helper
- Parent?
- Required?
Essas interfaces que definen se um elemento possui ou não uma propriedade.
Ex:
- Interface
```java=
public interface LabeledElement {
String getLabel();
void setLabel(final String label);
}
```
- Elemento
```java=
public class FormulaElement extends InputMetaScreenElement
implements LabeledElement {
private String label;
@Override
public String getLabel() {
return label;
}
@Override
public void setLabel(final String label) {
this.label = label;
}
}
```
### Validação
Hoje existe apenas uma classe de validação para todos os tipos de elementos, poderiamos dividir em varias classes de validação separadas por validações especificas para cada tipo de elemento, ficando assism:
- `MetaScreenElementValidator`
> Utilizado para validações compartilhadas entre todos os elementos e validações das propriedades compartilhadas. Ex: Label
- `ColumnMetaScreenElementValidator`
> Utilizado para validações especificas de colunas
- `InputMetaScreenElementValidator`
> Utilizado para validações especificas de elementos de input
- `WrapperMetaScreenElementValidator`
> Utilizado para validações especificas de wrapper
|validação|sugestão|
|---------|--------|
| validateIfMetaAttributeDoesNotExist | MetaScreenElementValidator |
| validateIfLabelIsNullForSomeTypesOfElements | Bean Validator |
| validateIfOrderIsNegative | MetaScreenElementValidator |
| validateIfNameForMetaScreenElementIsValid | MetaScreenElementValidator |
| validateIfExistsSomeMetaScreenElementWithTheInformedName | MetaScreenElementValidator |
| validateIfObjectOfLinkedAttributeIsTheSameOfLinkedScreen | MetaScreenElementValidator |
| validateIfExistsOtherElementsWithTheSameAttributeForScreen | InputMetaScreenElementValidator ColumnMetaScreenElementValidator |
| validateIfMetaScreenElementRequiresMetaAttribute | Bean Validator |
| validateIfLinkedMetaAttributeIsValidForMetaScreenElement | ColumnMetaScreenElementValidator InputMetaScreenElementValidator ListWithActionsElement |
| validateIfIsStubScreenElement | MetaScreenElementValidator |
| validateIfElementIsValidForMetaScreen | ColumnMetaScreenElementValidator InputMetaScreenElementValidator WrapperMetaScreenElementValidator |
| validateListAndFormMetaAttribute | MetaScreenElementValidator |
| validateAttributeIsChildOfParentElementAttribute | ColumnMetaScreenElementValidator InputMetaScreenElementValidator ListWithActionsElement |
| validateListAndFormParent | |
| validateFormElementParent | |
| validateTabsForStandardScreen | |
| validateTabsParent | |
| validateListWithActionsLevel | |
| validateListAndFormLevel | |
| validateListAndFormUniqueForListWithActions | |
| validateElementLevel | |
| validateRequiredLookupField | |
| validateIfLookupDisplayAttributeIsFound | |
| validateIfLookupDisplayAttributeIsValidType | |
| validateIfLookupDisplayAttributeBelongsToMetaObject | |
| validateElementAndAttributeRequiredValue | |
| validateIfFormElementBeLinkedWithSystemAttribute | |
| validateIfLinkedMetaAttributeStandardIsLinkedToNonStandardFormMetaScreenElement | |
| validateColumnElementParent | |
| validateIfLabelOfListElementInformedIsValidForEnvironmentType | |
| validateIfLookupColumnElementLinkedWithAttributeMultiSelection | |
| validateIfLinkedMetaAttributeStandardIsLinkedToNonStandardListMetaScreenElement | |
| validateIfMetaScreenElementIsStandardForUpdate | |
| validateIfMetaScreenElementBelongsToMetaScreen | |
| validateIfMetaScreenElementNameWasChanged | |
| validateIfMetaScreenElementOrderWasChanged | |
| validateTypeForDelete | |
| validateIfMetaScreenElementIsStandardForDelete | |
| validateIfOrderIsNegative | |
| validateIfOrderCanBeChanged | |
| validateIfOrderExists | |
### Banco de dados
Não será nessessário fazer alteração de estrutura ou de dados na base de dados pois, na coluna `element_type` só persistido o nome das classes concretas.
### Proposta - Validadores com Qualifiers
```java=
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ValidatorForElement {
Class<?> value();
}
```
<sub>Onde o `value` é o tipo de elemento ou interface ao qual será aplicado a validação.</sub>
```java=
public class ValidatorForElementLiteral
extends AnnotationLiteral<ValidatorForElement>
implements ValidatorForElement {
private final Class<?> value;
public ValidatorForElementLiteral(final Class<?> value) {
this.value = value;
}
@Override
public Class<?> value() {
return value;
}
}
```
<sub>Representação literal da annotation para ser utilizado como filtro do CDI</sub>
```java=
@ApplicationScoped
public interface ScreenElementValidator<T extends MetaScreenElement> {
Set<LogicError> insertValidation(final T metaScreenElement);
Set<LogicError> updateValidation(final T metaScreenElement);
Set<LogicError> deleteValidation(final T metaScreenElement);
default Set<LogicError> validate(final T metaScreenElement, final IValidationFilter... filter) {
final Set<LogicError> errors = new HashSet<>();
if (ArrayUtils.contains(filter, EValidationType.INSERT)) {
errors.addAll(insertValidation(metaScreenElement));
}
if (ArrayUtils.contains(filter, EValidationType.UPDATE)) {
errors.addAll(updateValidation(metaScreenElement));
}
if (ArrayUtils.contains(filter, EValidationType.DELETE)) {
errors.addAll(deleteValidation(metaScreenElement));
}
return errors;
}
}
```
<sub>Interface de validação</sub>
```java=
@ValidatorForElement(InputTextElement.class)
public class InputTextValidator implements ScreenElementValidator<InputTextElement> {
@Override
public Set<LogicError> insertValidation(final InputTextElement metaScreenElement) {
return null;
}
@Override
public Set<LogicError> updateValidation(final InputTextElement metaScreenElement) {
return null;
}
@Override
public Set<LogicError> deleteValidation(final InputTextElement metaScreenElement) {
return null;
}
}
```
<sub>Exemplo de validator para InputTextElement</sub>
Tendo os validators devidamente separados, alteramos a `MetaScreenElementValidator` para:
```java=
@Override
protected void validate(final MetaScreenElement metaScreenElement, final IValidationFilter... filter) {
final Set<LogicError> errors = getElementValidators(metaScreenElement.getClass())
.stream()
.map(validator -> validator.validate(metaScreenElement, filter))
.flatMap(Collection::stream)
.collect(Collectors.toSet());
this.addErrors(errors, metaScreenElement);
}
private List<ScreenElementValidation> getElementValidators(final Class<?> metaScreenElementClass) {
return getElementSuperclasses(metaScreenElementClass)
.stream()
.map(ValidatorForElementLiteral::new)
.map(qualifier -> CDI.current().select(ScreenElementValidation.class, qualifier))
.filter(instance -> !instance.isUnsatisfied())
.map(Instance::get)
.collect(Collectors.toList());
}
private Set<Class<?>> getElementSuperclasses(final Class<?> metaScreenElementClass) {
final Set<Class<?>> classes = new HashSet<>();
classes.add(metaScreenElementClass);
classes.addAll(Arrays.asList(metaScreenElementClass.getInterfaces()));
Class<?> superclass = metaScreenElementClass.getSuperclass();
while (MetaScreenElement.class.isAssignableFrom(superclass)) {
classes.add(superclass);
classes.addAll(Arrays.asList(superclass.getInterfaces()));
superclass = superclass.getSuperclass();
}
return classes;
}
```
### Proposta - ValidatorFactory
###### CDI Filter
```java
public interface CdiFilter<T> {
boolean applicable(final T t);
}
```
###### ScreenElementValidator
```java
@ApplicationScoped
public interface ScreenElementValidator<T> extends CdiFilter<MetaScreenElement> {
default Set<LogicError> getInsertErrors(final T element) {
return Collections.emptySet();
}
default Set<LogicError> getUpdateErrors(final T element) {
return Collections.emptySet();
}
default Set<LogicError> getDeleteErrors(final T element) {
return Collections.emptySet();
}
default Set<LogicError> getErrors(final MetaScreenElement metaScreenElement, final IValidationFilter... filters) {
final Set<LogicError> errors = new HashSet<>();
if (ArrayUtils.contains(filters, EValidationType.INSERT)) {
errors.addAll(getInsertErrors((T) metaScreenElement));
}
if (ArrayUtils.contains(filters, EValidationType.UPDATE)) {
errors.addAll(getUpdateErrors((T) metaScreenElement));
}
if (ArrayUtils.contains(filters, EValidationType.DELETE)) {
errors.addAll(getDeleteErrors((T) metaScreenElement));
}
return errors;
}
}
```
###### TabElementValidator
```java
public class TabElementValidator implements ScreenElementValidator<TabElement> {
@Override
public boolean applicable(final MetaScreenElement metaScreenElement) {
return metaScreenElement instanceof TabElement;
}
}
```
###### ScreenElementValidatorFactory
```java
@ApplicationScoped
public class ScreenElementValidatorFactory {
private final Instance<ScreenElementValidator<?>> validatorsBeans;
@Inject
public ScreenElementValidatorFactory(final Instance<ScreenElementValidator<?>> validatorsBeans) {
this.validatorsBeans = validatorsBeans;
}
public List<ScreenElementValidator<?>> getValidators(final MetaScreenElement metaScreenElement) {
final List<ScreenElementValidator<?>> validators = new ArrayList<>();
validatorsBeans.forEach(bean -> {
if (bean.applicable(metaScreenElement)) {
validators.add(bean);
}
});
return validators;
}
}
```
###### MetaScreenElementValidator extends AbstractEntityValidator
```java
protected void validate(final MetaScreenElement metaScreenElement, final IValidationFilter... filter) {
screenElementValidatorFactory
.getValidators(metaScreenElement)
.stream()
.map(validator -> validator.getErrors(metaScreenElement, filter))
.flatMap(Set::stream)
.forEach(error -> addError(error, metaScreenElement));
}
```
### Abstrações
[Fullscreen](https://mermaid-js.github.io/mermaid-live-editor/#/view/eyJjb2RlIjoiY2xhc3NEaWFncmFtXG4gIE1ldGFTY3JlZW5FbGVtZW50IDx8LS0gQWJzdHJhY3RGb3JtTWV0YVNjcmVlbkVsZW1lbnRcbiAgTWV0YVNjcmVlbkVsZW1lbnQgPHwtLSBBYnN0cmFjdExpc3RNZXRhU2NyZWVuRWxlbWVudFxuXG4gIEFic3RyYWN0Rm9ybU1ldGFTY3JlZW5FbGVtZW50IDx8LS0gQWJzdHJhY3RGb3JtTWV0YVNjcmVlblN0dWJFbGVtZW50XG4gIEFic3RyYWN0Rm9ybU1ldGFTY3JlZW5FbGVtZW50IDx8LS0gQWJzdHJhY3RJbnB1dE1ldGFTY3JlZW5FbGVtZW50XG4gIEFic3RyYWN0Rm9ybU1ldGFTY3JlZW5FbGVtZW50IDx8LS0gQWJzdHJhY3RXcmFwcGVyTWV0YVNjcmVlbkVsZW1lbnRcblxuICBBYnN0cmFjdExpc3RNZXRhU2NyZWVuRWxlbWVudCA8fC0tIEFic3RyYWN0Q29sdW1uTWV0YVNjcmVlbkVsZW1lbnRcblxuICBBYnN0cmFjdElucHV0TWV0YVNjcmVlbkVsZW1lbnQgPHwtLSBBYnN0cmFjdElucHV0VGV4dEVsZW1lbnQiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ)
```mermaid
classDiagram
MetaScreenElement <|-- AbstractFormMetaScreenElement
MetaScreenElement <|-- AbstractListMetaScreenElement
AbstractFormMetaScreenElement <|-- AbstractFormMetaScreenStubElement
AbstractFormMetaScreenElement <|-- AbstractInputMetaScreenElement
AbstractFormMetaScreenElement <|-- AbstractWrapperMetaScreenElement
AbstractListMetaScreenElement <|-- AbstractColumnMetaScreenElement
AbstractInputMetaScreenElement <|-- AbstractInputTextElement
```