Dependency Inversion Principle (DIP) é provavelmente o princípio SOLID mais transformador e mais mal aplicado. A versão clássica diz:
Traduzindo: seu código de negócio não deve importar Stripe SDK diretamente. Deve depender de uma interface PaymentGateway. Stripe SDK implementa essa interface.
// UserService.ts
import axios from 'axios';
import { Stripe } from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export class UserService {
async createUser(email: string, password: string) {
const user = await db.users.create({ email, password });
// ❌ Acoplamento direto
await stripe.customers.create({
email: user.email,
name: user.name
});
await axios.post('https://api.sendgrid.com/v3/mail/send', {
to: user.email,
subject: 'Welcome'
});
return user;
}
}
Problemas:
// application/ports/PaymentGateway.ts
export interface PaymentGateway {
createCustomer(data: { email: string; name: string }): Promise<{ id: string }>;
}
// application/ports/EmailService.ts
export interface EmailService {
send(data: { to: string; subject: string; body: string }): Promise<void>;
}
// infrastructure/adapters/StripePaymentGateway.ts
import { Stripe } from 'stripe';
import { PaymentGateway } from '../../application/ports/PaymentGateway';
export class StripePaymentGateway implements PaymentGateway {
constructor(private stripe: Stripe) {}
async createCustomer(data: { email: string; name: string }) {
const customer = await this.stripe.customers.create(data);
return { id: customer.id };
}
}
// infrastructure/adapters/SendGridEmailService.ts
import sgMail from '@sendgrid/mail';
import { EmailService } from '../../application/ports/EmailService';
export class SendGridEmailService implements EmailService {
constructor() {
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
}
async send(data: { to: string; subject: string; body: string }) {
await sgMail.send({ to: data.to, from: 'noreply@app.com', subject: data.subject, html: data.body });
}
}
// application/services/UserService.ts
export class UserService {
constructor(
private userRepository: UserRepository,
private paymentGateway: PaymentGateway,
private emailService: EmailService
) {}
async createUser(data: CreateUserData) {
const user = await this.userRepository.create(data);
await this.paymentGateway.createCustomer({
email: user.email,
name: user.name
});
await this.emailService.send({
to: user.email,
subject: 'Welcome',
body: '<h1>Welcome</h1>'
});
return user;
}
}
// infrastructure/composition.ts
import { UserService } from '../application/services/UserService';
import { PostgresUserRepository } from './adapters/PostgresUserRepository';
import { StripePaymentGateway } from './adapters/StripePaymentGateway';
import { SendGridEmailService } from './adapters/SendGridEmailService';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export const userService = new UserService(
new PostgresUserRepository(),
new StripePaymentGateway(stripe),
new SendGridEmailService()
);
O mesmo vale para componentes. Exemplo:
// application/hooks/useUserProfile.ts
export function useUserProfile(userId: string) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
userService.getProfile(userId).then(setUser);
}, [userId]);
return user;
}
O hook depende de userService (abstração), não de fetch direto. Em testes, userService é mockado.
const UserServiceContext = createContext<UserService | null>(null);
export function UserServiceProvider({ userService, children }: Props) {
return (
<UserServiceContext.Provider value={userService}>
{children}
</UserServiceContext.Provider>
);
}
export function useUserService() {
const service = useContext(UserServiceContext);
if (!service) throw new Error('UserServiceProvider required');
return service;
}
Componentes consomem via useUserService(), recebem implementação no topo da árvore.
DIP não é abstração por abstração. É desacoplamento intencional de boundaries que vão mudar. Pergunte: "o que pode mudar aqui?". Se a resposta for "vou trocar Stripe", crie interface. Se for "nunca vai mudar", não crie.
Produtos gratuitos e pagos para transformar ideias em uma base que você consegue executar.
13 produtos disponíveisContinue explorando tópicos similares
Guia definitivo para aplicar Interface Segregation Principle em React, TypeScript e backend, reduzindo DTOs obesos, props desnecessárias, overfetching e contratos genéricos demais com slices, selectors, fragments e portas específicas.
Descubra o que realmente define um Agente de IA. Explore componentes essenciais como planejamento (ReAct), memória e ferramentas, e aprenda a arquitetar fluxos de trabalho agênticos escaláveis.
A rule, an AI suggestion, and a human agent's decision, for the same synthetic ticket: only one of them can ever change the ticket's state. From that constraint, DeskPilot gains a typed…
Checklist de 47 pontos para encontrar bugs, riscos de segurança e problemas de performance antes do lançamento.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.