2. Um pouco sobre mim
● Atuo 10 anos com Desenvolvimento Web
● Atuo com React desde 2016. Com Next.js e TypeScript desde 2018.
● Já atuei em Portais e E-commerces com milhões de visitas diárias
● Atuo com foco em melhoria de código e performance de aplicações
● 31k pessoas leram minhas respostas no Stack Overflow
● 35k de leituras nos artigos do Medium e dev.to
● Já Atuei em Projetos de 5 Países, com pessoas de mais de 70 países.
3. O que você vai ver
nessa apresentação
▪ Quem deve Escalar?
▪ O que é Escalabilidade?
▪ Problemas Comuns
▪ Desafios
▪ Mantenabilidade
Escalabilidade
01.
▪ GIT
▪ Configurando um projeto
▪ Estrutura de Projetos
▪ Monitoramento
▪ Testes
Arquitetura
02.
▪ React + TS
▪ Boas Práticas de React e
TypeScript
▪ Solid com React e
TypeScript
React + TS
03.
4. Quem deve pensar em Escalar?
Qualquer pessoa que queira atuar em um
projeto que tenha um crescimento muito rápido
em sua base de código, e uma alta frequência
de atualização de seus co-criadores.
5. Quais problemas evitamos em
um projeto escalável?
- Custos de Infraestrutura.
- Alta curva de aprendizado de novos integrantes
- Performance
- Manutenabilidade
- Falta de Agilidade e Problemas de Entrega de
novas features
6. Como sabemos que um projeto
é escalável?
- O projeto consegue aumentar recursos de infra-estrutura sem aumentar
absurdamente os custos
- Estrutura do Projeto é simples e intuitiva
- Complexidade e Número de dependências não impede o crescimento do
projeto
- Aprendizado sobre o produto e base de código de novos integrantes ocorre
de forma orgânica e sem maiores dificuldades
- Projeto raramente têm bloqueios na parte técnica
- Projeto consegue atender uma base de usuários enorme, sem maiores
problemas.
- Projeto necessita de poucas refatorações para ser o mais performático
possível
7. Problemas comuns
de Escalabilidade
- Cada Desenvolvedor quer implementar seu estilo de código ou paradigma de
programação ( Programação Funcional x POO)
- Mudanças frequentes na estrutura de projeto ou estilo de código (ex: JSX para TSX
- Complexidade e Número de dependências que impede o crescimento do projeto
- Código que fere o princípio de Single Responsibility, e que se alterado pode quebrar
muitas partes do projeto.
- Código sem nenhuma documentação
- As Pessoas que fizeram a maior parte do projeto e tinham conhecimento sobre ele, já
deixaram a equipe.
- Projeto engessado com dependências muito antigas (ex: só compativel com IE)
8. Você define a cada decisão
se seu projeto vai ser
escalável ou não
9. Como saber se meu código
é escalavel?
Faça a si mesmo as seguintes perguntas:
- Meu código é legível, de fácil entendimento, performático (faz mais com menos
código), tem vazamento de memória?
- Já existe algum componente, função, método, serviço na aplicação que faz o que quero
fazer?
- Essa é a melhor forma de criar a estrutura de arquivos do meu código?
- Os custos para o navegador, usuário e servidor são grandes demais?
- Meu código implementa alguma melhoria, ou introduz bugs?
- O tipo de dados e objetos são coerentes com o fluxo da aplicação e não existe a
possibilidade de quebrar a mesma?
- Só eu entendo a lógica escrita?
- Consigo explicar esse código facilmente para qualquer novato na equipe?
10. Arquitetura:
Estruturando seu projeto
Existem diversas formas de estruturar um projeto React.
Você pode utilizar Atomic Design para fazer um projeto escalável.
Como pode utilizar uma estrutura de pastas muito semelhante ao CRA
( Create React App ).
Mas caso sua aplicação esteja ficando muito complexa, ou com
Componentes globais demais, talvez seja o momento de adotar uma
arquitetura Micro-frontend.
Ou criar uma lib para seus componentes de layout.
Ou dividir sua pasta em mais subpastas.
11. Arquitetura:
Estruturando seu projeto
Você pode descobrir se a arquitetura do seu projeto está ruim, se para
renderizar um componente em uma página, você depende de um
número muito grande de arquivos e dependências para isso.
O contrário também é válido, ter um componente gigantesco de mais
de 1000 linhas, em que o código fique gigantesco e difícil de debugar,
é um problema bem grande para escalabilidade.
12. Arquitetura: GIT
Na hora de decidir como sua equipe vai atuar em um projeto, pode-se usar
tanto um repositório por projeto, como mono-repos.
Dependendo do tamanho do seu projeto, mono-repos não é uma solução
viável, devido a performance baixa do Git para lidar com projetos grandes
em monorepos, e a problemas de segurança que um projeto pode estar
exposto ao estar em um mono-repo.
Como padrão de branches,recomendo o Gitflow.
Como padrão de commits, recomendo usar Conventional Commits.
Para fazer releases: Semantic Release.
13. Configuração:
Paths
Quando estamos configurando um Projeto React + TS, algumas práticas que
considero vitais para que um app seja escalável:
- Configurar os paths no arquivo tsconfig.json, por exemplo:
15. Configuração:
Utilizando Husky
Podemos também utilizar o Husky para configurar scripts que vão verificar
se nosso código está com lint válido, se passa nos testes unitários, se não
possui erros de código.
Com Husky conseguimos criar scripts, que antes de um commit ou um push
roda outros scripts para verificar se o código, está no padrão esperado.
Também conseguimos configurar o Husky para aceitar apenas um padrão de
mensagens de commit, junto com packages como commitizen ou
commitlint, o que pode facilitar muito o log da nossa aplicação.
16. Configuração:
Gerando Releases
Depois de configurar como nosso projeto e tratar os paths da aplicação,
como ele valida o código antes de comitarmos, um terceiro passo é controlar
como geramos os releases do projeto.
Os releases podem ser gerados semanalmente,mensalmente, ou
diariamente tudo depende de como a equipe se organiza e qual a prioridade
de features,bugs,hotfix, etc.. que a equipe quer enviar para produção.
Utilizando o package semantic-release, podemos gerar CHANGELOGS
automáticos a cada versão de release.
17. Um exemplo de estrutura de projeto
.
Aqui um exemplo ao lado de estrutura que pode ser escalável sem bem
utilizada: (mais a frente vamos explicar as principais pastas)
assets: Pasta para assets como imagens,svgs, js terceiros
components: Pasta para os componentes do projeto
core: Pasta onde a lógica principal de funcionamento da aplicação fica
pages: A pasta com as páginas finais das rotas renderizadas.
tests: pasta para testes unitários, simula a estrutura da pasta src, para
organizar os testes por tipos de arquivo.
18. Um exemplo de estrutura de projeto:
Components
.
Categorizei esse exemplo de uma pasta components:
elements: Pasta com os componentes mais básicos do DOM da sua
aplicação e que vão ser re-utilizados por diversas vezes.
layout: Componentes globais de layout como footer,header,menus.
pages: componentes de página, aqui as páginas são construídas, tem sua
lógica e componentes específicos de cada página
templates: pasta para componentes de templates/temas específicos que
podem ser utilizados em diversas situações/tipos de
usuário/localidades/línguas
widgets: Elementos maiores compostos de vários elementos, mas que não
são únicos de uma página
19. Um exemplo de estrutura de projeto:
core
.
Na pasta core:
config: variavéis e objetos de configuração do projeto, como os Routes da
aplicação
context: Contexts da aplicação
helpers: Funcões que retornem uma formatação, validações, cálculos
hooks: Hooks da aplicação
middleware: Caso nossa aplicação utilize algum middleware
services: Os serviços da nossa aplicação
types: Onde armazenamos os tipos e interfaces da aplicação
20. Monitorando o projeto
Depois que temos o projeto está em produção, é bom monitorar alguns fatores como:
Erros no código: Sentry, é uma ferramenta de monitoramento de erros, que aceita diversas plataformas e
tecnologias.
Segurança: Existem ferramentas que pegam falhas de segurança como Sqreen e LGTM
Performance e Usuário: LightHouse & SpeedCurve, com essas duas ferramentas conseguimos monitorar
fatores importantes como tempo de carregamento de uma página, quantos usuários abandonam a página
sem navegar pelo resto do projeto, qual a velocidade
SonarQube: Na plataforma do SonarCloud.com você consegue linkar com seu projeto no github, e pegar
parametros como Manutenibilidade de código, Coverage de testes, code smells, duplicação de código etc.
21. Testes
Quando pensamos em escalar uma aplicação uma forma de evitar que ela quebre, e que se comporte como
esperamos trabalhamos com testes:
Testes Unitários: O recomendado é fazer testes unitários em todos serviços e pelo menos nos componentes
principais da aplicação e que lidam com dados sensíveis do usuário.
Com TypeScript conseguimos usar nossos tipos e interfaces para fazer mocks de respostas de serviços. No
React utilizamos React Testing Library para testar componente visuais e Jest para testar funções e serviços.
Testes de Integração: Usamos os testes de Integração para verificar se a renderização de componentes, ou
integração de serviços está ocorrendo de forma como esperada.
Testes E2E: Os Testes E2E são importantes para garantir que o comportamento em nossa aplicação vai ser o
esperado. Utilizamos essa técnica para verificar se o fluxo que desejamos está ocorrendo como deveria.
Cypress é a lib mais utilizada do mercado.
23. 1 - Ótima documentação, muitos recursos de aprendizado,
Framework com maior base de conhecimento disponível na
internet
2 - Lib mais utilizada do mercado.
3 - React foi implementado em apps com alto volume de
dados e tráfego. O mais conhecido deles sendo o
Facebook,criador do React, mas também os maiores
e-commerces, portais, redes sociais ,e plataformas utilizam
React.
4 - Baixa curva de aprendizado
25. 1 - TypeScript torna fácil o entendimento do tipo de dados e
objetos de uma API, ou de uma aplicação
2 - Garante a consistência dos dados, evitando exceções e
erros.
3 - Impoem um guia de código
4 - Ajuda na arquitetura e estruturação do código
5 - Melhora a documentação e velocidade de entrega do
projeto
6 - Checagem de erros em tempo de compilação
7 - Ferramentas de debug nativas no VSCode
28. React + TypeScript
1 - TypeScript + @types/react juntos tem mais de 35 milhões
de downloads diários.
2 - O package @types/react, tem mais downloads do que
qualquer outro package de tipagem para React.
3 - TypeScript tem uma comunidade maior e mais conteúdo
se comparado a seus concorrentes.
4 - PropTypes que vem em segundo lugar nos packages para
tipagem de projetos React, teve sua última atualização em
Fevereiro de 2019.E apenas checa os tipos em Runtime.
5 - Flow que foi feito pelo Facebook, tem uma adoção muito
baixa e não chega a ser uma linguagem mas apenas um
checker de tipos estáticos.
29. Boas Práticas de React e TS
1 - Saiba que condicional usar
Em algumas situações operadores short
circuit && , podem retornar um valor 0 na sua
tela.
Isso é porque o React não trata o 0 como
falso, e para isso podemos usar um ternário
que é um pouco mais verboso, mas resolve
nosso problema, ou a dupla negação que
converte o 0 para false, e aí não renderiza o
0 na tela.
30. Boas Práticas de React e TS
2 - Use uma lib externa para Data Fetching
É sempre bom utilizar as Apis nativas do JavaScript como fetch, porém existem libs que
podem ser usadas no React como React Query ou SWR que utilizam Hooks para fazer essa
comunicação com o servidor.
Eles já vem com features prontas de Tratamento de Erros, Cacheamento, Hidratação de
componentes, Gerenciamento de Estados e etc. que se fossemos fazer do zero, iríamos
perder muito tempo reinventando a roda.
Caso trabalhe com APIS GraphQL, existem clientes como o Apollo, que já possui o
conceito de estado pronto.
31. Boas Práticas de React e TS
3 - Se o seu APP é grande o suficiente:
Use uma lib para gerenciar o estado
Em diversas situações o Context API nativo do React não vai funcionar como um gerenciador de estado da sua
aplicação.
Utilizar o Context API em situações como quando só precisamos passar dados de um componente para outro, ou
para compartilhar dados em baixo volume, é a solução ideal para o que precisamos
Mas em outras situações o Context API é apenas um injetor de dependências e não serve para tratar gerenciamento
de estados complexos, ou que sejam atualizados com alta frequência.
Para isso possuímos os Redux com Redux Toolkit, ou Mobx ou Recoil.
Escolher uma lib para gerenciar o estado da nossa aplicação pode ser uma decisão acertada, pois geralmente o
estado processa dados importantes da aplicação e se for arquitetado e manipulado de forma errada pode causar
grande perda de performance ou até quebrar nossa aplicação.
32. Boas Práticas de React e TS
4 - Não escreva código Hardcoded.
Utilize um objeto de config para isso
Um exemplo é esse código ao lado com um
arquivo de configuração de rotas.
Ele vai ser interpretado por um AppRoutes
que vai mapear cada objeto desse array e vai
renderizar uma rota da nossa aplicação
33. Boas Práticas de React e TS
4 - Não escreva código Hardcoded.
Utilize um objeto de config para isso
Ao lado temos o componente AppRoutes que possui
apenas a lógica de renderização das Rotas, não tendo
as rotas em si.
Separando a responsabilidade de cada componente,
um para os dados, e um para a interpretação dos
dados.
34. Boas Práticas de React e TS
5 - Faça a tipagem correta dos seus objetos/props e funções
Tipar nossos objetos com o tipo errado, ou com any, pode ocasionar problemas na hora de renderizar
nossa aplicação.
Especialmente em casos em que não fazemos nullcheck nos nossos dados.
um exemplo comum de componente com tipagem incorreta:
35. Boas Práticas de React e TS
5 - Faça a tipagem correta dos seus objetos/props
Qual o problema com o componente anterior?
1- Estamos usando any como tipo do prop data, aceitando qualquer valor que venha no componente.
2 - caso esse prop data seja nulo ou undefined, ele vai quebrar a renderização do componente.
3 - Não estamos forçando o tipo de retorno do nosso Componente. na teoria ele pode retornar qualquer coisa.
4 - Para isso corrigimos nosso componente dessa forma:
36. Boas Práticas de React e TS
5 - Faça a tipagem correta dos seus objetos/props
No exemplo do slide anterior, estamos especificando uma interface para a props
data, que recebemos no nosso componente, e que ele é um objeto de um array (por
isso fazemos map dele),
e antes de fazermos esse map, realizamos o nullcheck, pra saber se essa prop data
existe e não é nem null e nem undefined.
Caso seja retorna null e não quebra a nossa aplicação.
37. Boas Práticas de React e TS
6 - Centralize componentes externos
Caso você utilize componentes de outras libs e packages,é uma boa prática, você centralizar em uma pasta
shared todos os imports dos seus componentes, pois quando você importar nos seus componentes locais,
os mesmos não precisam saber da onde vem esses componentes externos.
Facilitando inclusive a troca de libs caso seja necessário.
Assim você tem apenas uma fonte de informação que é facilmente substituível, ao invés de ter que substituir
em sua aplicação inteira.
38. Boas Práticas de React e TS
7 - Utilize Hooks
Hooks veio para ajudar no re-uso de lógica e compartilhamento de estado entre componentes da nossa
aplicação.
Antes dos Hooks, a forma de fazer isso era através de High Order Components ou Render Props.
O ruim de usar HOC, é que os valores que precisamos vem dentro de props, que não temos uma idéia
clara se vem do componente pai, ou do componente que estamos adicionando no HOC.
com Hooks conseguimos separar a parte lógica do componente, e reutilizá la onde quisermos em nossa
aplicação.
Além de deixar nosso componente muito mais legível.
39. Boas Práticas de React e TS
8 - Preste Atenção nos Re-Renders:
useMemo() x useCallback()
Quando atualizamos as props de nosso componente ele causa re-render em todos sua estrutura,
seja o filho ou o próprio componente pai, inclusive em partes/estruturas do componente que não
foram atualizadas.
Para evitar isso existem dois Hooks que nos ajudam a não causar Re-renders desnecessários:
useMemo() x useCallback()
useMemo(), serve para guardamos valores em cache, e só atualizar quando o valor da dependência
que especificamos no hook mudar.
useCallBack(), serve para guardar um método em cache ,que só vai ser chamado/atualizado se as
suas dependências forem atualizadas.
A importância de utilizarmos esses Hooks, é especialmente porque ele evita muitos problemas de
performance para o usuário.
40. Boas Práticas de React e TS
9 - Utilize Object Literals ao invés de switch
Muitas vezes quando precisamos fazer algum código condicional como muitos if/else,utilizamos
switch/case, mas o problema do switch case é que ele não escala muito bem. Object literal é mais
simples. podemos por exemplo usar para instanciar um componente:
41. Boas Práticas de React e TS
9 - Utilize Object Literals ao invés de switch
Nesse exemplo, temos no slider anterior um Componente de Página que centraliza todos os componentes
de página, e que abstrai para outro componente a lógica e UI da página,e abaixo seria o componente
principal que apenas instancia esse componente centralizador com o "type" do componente de que
queremos.
42. Boas Práticas de React e TS
9 - Utilize Object Literals ao invés de switch
Qual a vantagem de usar essa abordagem para Componentes, lógica de código, casos de exceção etc?
Seu código fica mais organizado. Mais intuitivo. Caso você queira trocar um componente, remover, ou até
testar seu código ele fica muito mais simples para atingir esses resultados.
E escalavel.
Porque nesse exemplo anterior, sabemos onde está sendo instanciado todos nossos componentes de
páginas, e assim podemos a partir do Object Literal instanciar mais componentes, que separando a parte
lógica de um componente de Rota ( como o do slider anterior ), podemos não só organizar melhor nosso
código, mas re-utiliza-lo também.
43. Boas Práticas de React e TS
10 - Abstraia Serviços
Uma boa prática para tornar sua
aplicação escalável, é ter uma função
que retorna os mesmos tipos de
componentes, ou serviços por exemplo.
Vamos dar um exemplo de abstração de
um serviço:
Primeiro criamos um serviço genérico de
Fetch que vai ser utilizado em todos os
nossos serviços:
44. 10 - Abstraia Serviços
No nosso serviço específico de busca de
artigos, apenas passamos o parâmetro
da URL que queremos para nosso serviço
de fetch genérico.
Boas Práticas de React e TS
45. 10 - Abstraia Serviços
agora podemos fazer um import mais limpo e usar nosso serviço dessa maneira:
Boas Práticas de React e TS
46. 10 - Abstraia Serviços
Encapsulando nosso serviços em um só lugar, fica mais fácil de substituir eles depois:
Boas Práticas de React e TS
48. SOLID é um acronimo para 5 principios
de software que tem como objetivo
definir práticas que mantém o software
mais intuitivo, flexível e manutenível.
49. Conceitos de SOLID para React
SOLID , um conceito muito difundido na programação orientada a objetos pode ser usada
em programação funcional? vamos ver o que o Uncle Bob tem a dizer sobre isso:
Tweet traduzido de: https://twitter.com/unclebobmartin/status/1164511654618550273
50. Quais são os conceitos Solid?
S de Single Responsibility Principle:
Um Componente ou função só deve fazer uma
coisa e fazê-la direito.
51. Um componente comum em projetos React:
Aqui um componente de listagem de gatos: ( continua no próximo slide)
53. Single Responsibility Principle no React/TS
Qual o problema com esse componente? Vamos lá:
1 - Ele define um estado inicial
2 - Depois ele define uma função redutora para gerenciar esse estado
3 - Ele define uma interface que vai ser o tipo principal do objeto que é a
razão desse componente
4 - Ele define um Componente, que faz 1 - Um serviço de chamada de API e
uma filtragem desse resultado de API
5- Ele instância esse objeto resultado da chamada da API, e então retorna os
dados dentro de um componente HTML.
54. Single Responsibility Principle no React/TS
Como resolver os problemas definidos no slide anterior
respeitando o princípio de Single Responsibility?
1 - Podemos abstrair o código do useReducer() para uma função
redutora separada para tratar o state da nossa função.
2 - Podemos abstrair o código dos hooks useEffect:
1 - passando o fetch para um serviço genérico HTTP.
2 - Criando um hook específico para lidar com a resposta desse
serviço e fazer o filtro que esperamos do objeto. ex: useCatHook()
3 - o hook useCatHook(), voltaria 2 propriedades , a resposta = cats, e o
isLoading para trazer se a resposta já foi obtida ou não.
4 - Fariamos um componente UI, que faria um map no objeto cats e
imprimiria nossa lista com dados dos gatos.
3 - No final iriamos só renderizar o componente final que organiza toda
a lógica que precisamos para renderizar a lista de gatos :)
55. Open Closed Principle no React/TS
“Robert C. Martin (Uncle Bob) considerou este princípio como o‘ princípio mais importante do design
orientado a objetos ’.
Mas ele não foi o primeiro a defini-lo. Bertrand Meyer escreveu sobre isso em 1988 em seu livro
Object-Oriented Software Construction.
Ele explicou o princípio Open/Closed como:
‘Entidades de software (classes, módulos, funções, etc.) devem ser abertas para extensão, mas fechadas
para modificação.’ ”
56. Open Closed Principle no React/TS
Usando Open / Closed Principle:
Assim como já demonstramos com a Abstração de Serviços e Object Literals, no componente abaixo mostramos
mais um exemplo de Open Closed Principle, onde não importa quantos tipos de gato podemos criar, não alteramos
o código do componente de gatos principal.
58. Liskov Substitution Principle no React/TS
O princípio de Liskov Substitution nos diz que:
“Os componentes devem obedecer a
algum tipo de contrato.”
Basicamente, isso significa que deve haver algum tipo de contrato entre os componentes. Portanto, sempre que um
componente usa outro componente, ele não deve interromper sua funcionalidade (ou criar algum efeito colateral
indesejado).
59. Liskov Substitution Principle no React/TS
Imagine que temos um componente que recebe um dado
via props e passa para seu componente filho:
E aí renderizamos esse componente no nosso App,
passando um objeto key: "value"
60. Liskov Substitution Principle no React/TS
Eis que estoura um erro no console
Então TypeScript vem para nos salvar!
Quando tipamos o prop data do nosso
componente estamos atribuindo a ele um
tipo único que caso não seja obedecido, vai
estourar um erro no nosso Vs Code:
61. Liskov Substitution Principle no React/TS
Agora quando tentamos renderizar nosso componente em
outro, ele vai acusar erro, caso não respeitamos o
contrato entre componentes:
62. Liskov Substitution Principle no React/TS
Se substituirmos a propriedade do objeto pela
propriedade correta, ele para de acusar o Erro e
conseguimos respeitar o princípio de Liskov Substitution
63. Interface Segregation Principle no React/TS
O princípio de Interface Segragation nos diz que:
“Os componentes não devem depender
de coisas de que não precisam.”
64. Interface Segregation Principle no React/TS
Imagine que temos nosso componente de gatos pai, que renderiza um Componente com detalhes do gato, e um com
imagens do gato, e em ambos passamos nosso objeto cat:
65. Interface Segregation Principle no React/TS
Qual o problema com o código do slide anterior?
1 - Ele está passando props desnecessárias para nossos componentes filhos, esse tipo de situação
é muito comum quando retornamos a resposta de uma API sem tratar os dados devidos, ou não
tipos e passamos os parâmetros certos para nossos componentes
2- Basta passar o objeto correto para nossos componentes filhos e então não violamos mais o
princípio de Interface Segregation.
66. Dependency Inversion Principle no React/TS
O princípio de Dependency Inversion nos diz que:
“Nenhum componente ou função deve se preocupar com a forma como
uma determinada coisa é feita.”
67. Dependency Inversion Principle no React/TS
Imaginamos que temos um componente que precise fazer o fetch de dados de uma base de
cachorros como no código abaixo:
68. Dependency Inversion Principle no React/TS
Qual o problema desse código?
1 - Ele Depende de alguns dados remotos que são buscados diretamente no componente.
2- A principal responsabilidade do nosso componente Dogs é processar e renderizar os dados.
3 - Ele não deve se preocupar com a forma como os dados são buscados ou de onde os dados
vêm.
Este componente sabe demais - e isso é um problema.
Imagine que:
1 - Você precise fazer fetch de dados em mais de 50 componentes.
2 - Seu chefe resolve trocar fetch, por axios por exemplo, e você tenha que trocar nos 50
componentes.
3 - Depois ele pede que você faça o cacheamento dos dados.
69. Dependency Inversion Principle no React/TS
Como arrumar meu código para não ferir o princípio de Dependency Inversion?
1 - Podemos abstrair toda lógica de fetch de dados para um hook customizável: useFetch() hook
por exemplo.
2- Nesse hook podemos usar o axios ou lib que quisermos além de implementar o cacheamento
dos dados.
3 - Assim não precisamos implementar o mesmo código em mais de 50 componentes, evitando a
possibilidade de criar Bugs desnecessários, economizando tempo e manutenção.
Como sei se estou quebrando o princípio de Dependency Inversion?
1 - Na maioria dos casos se você está quebrando o princípio de Single Responsibility, você
também está quebrando o princípio de Dependency Inversion.
2 - Olhe nos imports do seu componente. Se você não está importando apenas componentes
que são responsáveis por renderização no UI (um Modal por Exemplo), você pode estar violando
o princípio de Dependency Inversion.