---
# System prepended metadata

title: Proposta de Melhorias para projeto em Vue

---

# Proposta de Melhorias para projeto em Vue
Este documento tem como objetivo demonstrar, após análise do projeto de front da **Maxipas**, melhorias a respeito da arquitetura e recursos do mesmo. Tais melhorias são insights obtidos através de experiências em outros projetos reais com deploys em produção, porém não são absolutas e estão totalmente abertas à sugestões e críticas.

### 1. Devops & CI/CD
Como trata-se de uma aplicação client-side, uma opção com ótima relação custo/benefício seria a distribuição através do CloudFront (AWS) do build armazenado no S3 (AWS). O Processo de CI/CD fica mais simples e a utilização de containers desnecessária.

```yaml
build_prod:
  image: node:14-alpine
  stage: build
  before_script:
    - yarn
    - export AUTH_API_URL=$PROD_AUTH_API_URL
  script:
    - yarn build:prod
  artifacts:
    paths:
      - dist/spa
    expire_in: 20 mins
  environment:
    name: production
  except:
    - schedules
    - triggers
  only:
   - main

deploy_prod:
  stage: deploy
  image:
    name: amazon/aws-cli
    entrypoint: [""]
  script:
    - echo $PROD_S3_BUCKET_NAME
    - echo $PROD_CDN_DISTRIBUTION_ID
    - aws --version
    - aws s3 cp dist/spa s3://$PROD_S3_BUCKET_NAME --recursive
    - aws cloudfront create-invalidation --distribution-id $PROD_CDN_DISTRIBUTION_ID --paths "/*"
  environment:
    name: production
  only:
    - main
```

### 2. TS vs JS

Utilizar TypeScript pode representar um ganho de performance a médio e longo prazo, pois padroniza a comunicação entre componentes e serviços.

Exemplo de declarações de types:

```typescript=
export type User = {
  name: string;
  surname: string;
  email: string;
  permissions: Permission[]
}

export type Permission = {
  name: string;
  modules: Module[];
}

export type Module = {
  name: string;
  description: string;
  path: string;
  position: number;
  active: boolean;
  icon: string;
}
```

### 3. Estrutura e Componentes

Vue é framework progressivo, onde a estrutura de componentes pode ser amplamente utilizada para dividir responsabilidades e multiplicar a reusabilidade.

Exemplo de estrutura de componentes reutilizáveis.


```
> src/
 > components/
  > Forms/
   > GenericFilter.vue
   > Autocomplete.vue
   > InfiniteScroll.vue
  > Tables/
   > GenericTable.vue
   > RowItem.vue
   > TableSkeleton.vue
  > Generic
   > CustomCard.vue
   > CustomAvatar.vue
   > CustomDialog.vue
 > pages/
  > Home.vue
  > Profile.vue
  > Users.vue 
```

### 4.Composition API vs Options API

Composition API torna o código mais simples e reutilizável, facilitando a integração entre componentes. Tem uma melhor relação com o TS e substitui a utilização de Mixins, além de muitos outros benefícios, como injeção de dependências, hooks de ciclo de vida, diminuição no bundle de prod.

https://vuejs.org/guide/extras/composition-api-faq.html

Exemplo de estrutura do Composition API

```typescript=

// Importação manual de Hooks

import {
  defineComponent,
  ref,
  Ref,
  onMounted,
  defineAsyncComponent,
  computed,
} from 'vue';

export default defineComponent({
  name: 'QuizCreate',
  components: {
    // Importação assíncrona de componentes
    // permite um carregamente inicial mais rápido
    ObjectiveBody: defineAsyncComponent(() => {
      return import(
        /* webpackPrefrech: true */ '../objectives/ObjectiveBody.vue'
      );
    }),
  },
  props: {
    id: {
      type: String,
    },
  },
  setup(props) {
    // Instâncias de variáveis
    // com a utilização de TS e refs
    const noDialog = ref(false);
    const questionList = ref<Objective[]>([]);
    const historyList = ref<ObjectiveHistoryWhere[]>([]);


    // Lifecycle Hooks
    onMounted(async () => {
      // Uma variável ref dentro do composition api expõe seu 
      // conteúdo através do value
      loading.value = true;
      await fetchQuestions();
      loading.value = false;
    });

    const fetchQuestions = async (): Promise<Question[]> => {
      try {
        const { data } = await Objectives.fetch({
          where: {
            qid: {
              $in: quiz.value?.questions,
            },
          },
        });
        if (data?._items) {
          questionList.value = response.data._items.sort(sortByQid);
        }
      } catch (error) {
        // Utilização de uma classe própria para
        // logs de erros, facilitando o controle de logs próprios
        // para ambientes de desenvolvimento e produção
        logger.log('error', Messages.QUIZ_FETCH_QUESTIONS_ERROR, error);
        await fastExit(Messages.QUIZ_FETCH_QUESTIONS_ERROR);
      }
    };

    return {
      refClock,
      setObjectiveAnswer,
      saveHistoryDialog,
    };
  },
});
```

### 5. Classe de Log de Erros
Criar uma classe própria de log de erros aumenta o controle e rastreabilidade de falhas no código, além de possibilitar a integração, de forma mais prática de soluções externas, como o Sentry.


Exemplo de classe de log utilizando Sendgrid.

```typescript=
import * as Sentry from '@sentry/browser';
const LOGGER = process.env.LOGGER === 'true';

if (LOGGER) {
  console.log('%c Logging error is enabled.', 'color: orange');
}

export type Level = 'error' | 'warn' | 'info' | 'debug' | 'trace';

export class Logger {
  private module: string;

  constructor(module: string) {
    this.module = module;
  }

  public log(logLevel: Level, message: string, ...args: unknown[]): void {
    const logColor = {
      error: 'red',
      warn: 'yellow',
      info: 'green',
      debug: 'cyan',
      trace: 'gray',
    };
    if (LOGGER) {
      console.log(
        `%c[${this.module}] %c${message}`,
        'color: orange',
        `color: ${logColor[logLevel]}`
      );
      console[logLevel](`%c [${this.module}]`, 'color: orange', ...args);
    }
    Sentry.captureMessage(message);
  }

  public error(message: string, ...args: unknown[]): void {
    this.log('error', message, ...args);
  }

  public warn(message: string, ...args: unknown[]): void {
    this.log('warn', message, ...args);
  }

  public info(message: string, ...args: unknown[]): void {
    this.log('info', message, ...args);
  }

  public debug(message: string, ...args: unknown[]): void {
    this.log('debug', message, ...args);
  }

  public trace(message: string, ...args: unknown[]): void {
    this.log('trace', message, ...args);
  }
}

```

Além da classe, é importante alterar as configurações do Lint para alertar a respeito de toda instância do console nativo em arquivos que não sejam o da própria classe.

### 7. Variáveis de Ambiente
"Como definir Variáveis de ambiente" é sempre uma questão importante, pois este é o elo de configuração entre o host e a aplicação. É muito importante também manter tais dados fora do versionamento do projeto, por isso é fortemente recomendado a utilização de exemplos de arquivos .env (quando utilizados) com dados fake.

### 8. Pinia vs Vuex
Pinia é um gerenciador de máquina de estados feito para o VueJs, mais especificamente para a versão 3.

Criando a Store com Pinia
```typescript=
// stores/todo.js
import { defineStore } from 'pinia'

export const useTodoStore = defineStore({
  id: 'todo',
  state: () => ({ count: 0, title: "Cook noodles", done:false })
})
```
Usando Pinia em um componente
```typescript=
export default defineComponent({
  setup() {
    const todo = useTodoStore()

    return {
      // gives access only to specific state
      state: computed(() => todo.title),
    }
  },
})
```