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