Skip to content

SOLID

SOLID é um acrônimo para cinco princípios de design de software que visam tornar o código mais compreensível, flexível e sustentável. Embora originados na POO, sua aplicação no ecossistema React/TypeScript é vital para o nosso projeto.


1. S - Single Responsibility (Responsabilidade Única)

Definição: Um componente ou função deve ter apenas uma razão para mudar.

❌ Como não fazer

Um componente que busca dados, gerencia estado, trata erros e renderiza a UI. Se a API mudar, o componente muda. Se o design mudar, o componente muda.

tsx
function UserProfile() {
  const [data, setData] = useState();
  useEffect(() => { /* fetch logic */ }, []);
  if (!data) return <Loading />;
  return <div>{data.name}</div>;
}

✅ Como fazer

Separe a lógica de busca em um Hook e a renderização no Componente.

tsx
// Hook (Responsável apenas pela lógica de dados)
function useUser() {
  return useQuery(['user'], fetchUser);
}

// Componente (Responsável apenas pela UI)
function UserProfile() {
  const { data, isLoading } = useUser();
  if (isLoading) return <Loading />;
  return <UserDisplay name={data.name} />;
}

2. O - Open/Closed (Aberto/Fechado)

Definição: Entidades devem estar abertas para extensão, mas fechadas para modificação.

❌ Como não fazer

Criar um botão que precisa de um if interno toda vez que um novo ícone ou comportamento é adicionado.

tsx
function Button({ type }) {
  return (
    <button>
      {type === 'add' && <AddIcon />}
      {type === 'remove' && <RemoveIcon />}
      Texto
    </button>
  );
}

✅ Como fazer

Use Composição e Slots. O componente está "fechado" (você não muda o código do Button), mas "aberto" para extensão (você injeta o que quiser no slot).

tsx
function Button({ children, leadingSlot }) {
  return (
    <button>
      {leadingSlot}
      {children}
    </button>
  );
}

// Extensão sem modificar o original
<Button leadingSlot={<AddIcon />}>Adicionar</Button>

3. L - Liskov Substitution (Substituição de Liskov)

Definição: Subclasses devem poder substituir suas classes base sem quebrar o sistema.

No React/TS

Se um componente Input estende as propriedades de um HTMLInputElement, ele deve se comportar como um, aceitando todas as props padrão sem comportamentos inesperados.

tsx
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label: string;
}

function CustomInput({ label, ...rest }: InputProps) {
  return (
    <div>
      <label>{label}</label>
      <input {...rest} /> {/* Deve aceitar onChange, value, disabled, etc */}
    </div>
  );
}

4. Interface Segregation (Segregação de Interface)

Definição: Clientes não devem ser forçados a depender de interfaces que não utilizam.

❌ Como não fazer

Passar o objeto User inteiro para um componente que precisa apenas do nome.

tsx
function UserAvatar({ user }: { user: UserModel }) {
  return <img src={user.avatarUrl} />;
}

✅ Como fazer

Passe apenas o que o componente realmente usa. Isso torna o componente mais fácil de testar e reutilizar.

tsx
function UserAvatar({ url }: { url: string }) {
  return <img src={url} />;
}

5. Dependency Inversion (Inversão de Dependência)

Definição: Dependa de abstrações, não de implementações concretas.

No nosso projeto

Componentes de tela não devem depender diretamente de chamadas de API (Axios/Fetch). Eles dependem de uma abstração (uma Action ou um Hook de Feature).

tsx
// ❌ Ruim: Dependência direta de implementação (Axios)
function Screen() {
  const save = () => axios.post('/user', data);
}

// ✅ Bom: Dependência de uma abstração (Action)
function Screen() {
  const { mutate } = useUserMutation(); // O componente não sabe se é Axios, Fetch ou Mock
  const save = () => mutate(data);
}

Por que aplicar SOLID?

O SOLID não é uma regra burocrática, é uma estratégia de sobrevivência para projetos grandes. Ele garante que, quando precisarmos mudar uma regra de negócio ou uma ferramenta (ex: trocar Redux por TanStack), o impacto seja localizado e não quebre o sistema inteiro.