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.
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.
// 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.
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).
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.
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.
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.
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).
// ❌ 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.