---
# System prepended metadata

title: Refactor the elements backend
tags: [análise BTB]

---

---
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
```