Uma jornada prática através dos padrões MVVM, Dependency Injection e Context API para criar apps escaláveis
Desenvolver aplicativos móveis modernos vai muito além de apenas escrever código que funciona. É sobre criar uma arquitetura sólida que seja escalável, testável e mantenível ao longo do tempo. Hoje vamos mergulhar em uma stack completa que combina o melhor do React Native com padrões arquiteturais avançados.
O que você vai aprender nesta série
- Parte I (este post): Fundamentos da arquitetura, MVVM com MobX e organização de pastas
- Parte II: Dependency Injection, Repository Pattern e implementação prática
Vamos usar como base um projeto real que implementa estas tecnologias:
- React Native + Expo para desenvolvimento mobile
- TypeScript para tipagem estática
- MobX para gerenciamento de estado reativo
- Inversify para dependency injection
- RxJS para programação reativa
A Base: Organização de Pastas que Faz Sentido
Antes de falar sobre padrões, vamos entender como organizar um projeto mobile de forma que sua equipe (e você mesmo daqui 6 meses) consiga navegar facilmente:
src/
├── components/ # Componentes reutilizáveis
├── contexts/ # Estados globais com ViewModels
│ ├── auth/ # Autenticação
│ ├── core/ # Funcionalidades centrais
│ └── push/ # Notificações
├── core/ # Infraestrutura do app
│ ├── di/ # Dependency Injection
│ ├── networking/ # API e interceptors
│ └── storage/ # Persistência de dados
├── services/ # Repositórios e lógica de negócio
└── types/ # Definições TypeScript
Por que essa estrutura funciona?
- Separação clara de responsabilidades: Cada pasta tem um propósito específico
- Escalabilidade: Fácil adicionar novos contextos ou serviços
- Facilita testes: Cada camada pode ser testada independentemente
- Time-friendly: Novos desenvolvedores encontram rapidamente o que procuram
MVVM: O Coração da Nossa Arquitetura
O padrão Model-View-ViewModel (MVVM) é perfeito para aplicações React Native porque separa claramente:
- View: Componentes React (UI)
- ViewModel: Lógica de apresentação e estado
- Model: Dados e regras de negócio
Como isso funciona na prática?
Vamos ver um exemplo real do nosso AuthViewModel
:
@injectable()
export class AuthViewModel {
// Estados observáveis do MobX
phone: string = '';
loading?: string;
authenticated: boolean | null = null;
constructor(
@inject(DI_TYPES.AuthRepo) private authRepo: AuthRepo
) {
makeObservable(this, {
phone: observable,
loading: observable,
authenticated: observable,
onPhoneChanged: action,
setLoading: action,
isPhoneValid: computed,
});
}
// Ações que modificam o estado
onPhoneChanged = (phone: string) => {
this.phone = phone;
};
// Estados computados
get isPhoneValid(): boolean {
return this.phone ? isValidPhone(this.phone) : false;
}
// Lógica de negócio
onLoginSubmitted = () => {
this.setLoading('login.request.sendingConfirmation');
this.authRepo.sendOtp$(this.fullPhoneNumber, this.region?.cca2)
.subscribe({
next: (response) => {
this.clearLoading();
// Navegar para próxima tela
},
error: (error) => {
this.setError(error.message);
}
});
};
}
Vantagens do MVVM com MobX
- Reatividade automática: Quando
phone
muda, a UI se atualiza automaticamente - Testabilidade: Você pode testar toda a lógica sem renderizar componentes
- Reutilização: O mesmo ViewModel pode ser usado em diferentes telas
- Debugging: Estados centralizados facilitam o debug
Context API: Distribuindo Estado de Forma Inteligente
O React Context é nossa ponte entre ViewModels e componentes. Vamos ver como implementar:
// core.provider.tsx
const CoreContext = createContext<CoreViewModel>(undefined!);
export const CoreProvider = ({ children }: { children: React.ReactNode }) => {
const vm = useMemo(() => new CoreViewModel(AppState.currentState), []);
useEffect(() => {
return vm.onMount();
}, [vm]);
return <CoreContext.Provider value={vm}>{children}</CoreContext.Provider>;
};
export const useCore = () => {
const context = useContext(CoreContext);
if (!context) {
throw new Error('useCore must be used within a CoreProvider');
}
return context;
};
Hook personalizado para observar estado da app
export const useAppFocus = (effect: (focus: AppFocus) => void) => {
const vm = useCore();
const stableEffect = useCallback(effect, [effect]);
useEffect(() => {
const subscription = vm.observeAppFocus$().subscribe(focus => {
stableEffect(focus);
});
return () => subscription.unsubscribe();
}, [vm, stableEffect]);
};
MobX: Estado Reativo que Simplifica Sua Vida
O MobX é o motor que faz nossa arquitetura MVVM brilhar. Aqui estão os conceitos essenciais:
Observable: Estados que “observam” mudanças
@observable phone: string = '';
@observable loading: boolean = false;
Action: Funções que modificam o estado
@action
setPhone(phone: string) {
this.phone = phone;
}
Computed: Valores derivados do estado
@computed
get isFormValid(): boolean {
return this.isPhoneValid && this.isTermsAccepted;
}
Por que MobX e não Redux?
- Menos boilerplate: Não precisa de actions creators, reducers, etc.
- Mais performático: Atualiza apenas componentes que realmente mudaram
- Mais intuitivo: Trabalha como JavaScript “normal”
- Debugging simpler: Estados são objetos simples
RxJS: Programação Reativa para Eventos Complexos
Para eventos assíncronos e streams de dados, usamos RxJS:
// Observa mudanças de foco da aplicação
observeAppFocus$(): Observable<AppFocus> {
return this.focus$
.asObservable()
.pipe(
filter(isNotNull),
distinctUntilChanged()
);
}
// Escuta atualizações do app
private listenForAppUpdates(): Subscription {
return appIsUpdated$()
.pipe(first())
.subscribe(() => {
runInAction(() => {
this.isAppUpdated = true;
});
});
}
Quando usar RxJS?
- Eventos de tempo: Timers, debounce, throttle
- Combinação de streams: Merge de múltiplas fontes de dados
- Cancelamento: Requests que podem ser cancelados
- Estados complexos: Máquinas de estado reativas
🎨 Layout da Aplicação: Provider Hierarchy
Nossa aplicação está estruturada em camadas de providers:
// app/_layout.tsx
const RootLayout = () => {
return (
<CoreProvider> {/* Estado global da app */}
<SynqProvider> {/* WebSocket connections */}
<QuteProvider> {/* Design system */}
<AuthProvider> {/* Autenticação */}
<PushProvider> {/* Notificações */}
<OrderProvider> {/* Pedidos */}
<Slot /> {/* Páginas do app */}
</OrderProvider>
</PushProvider>
</AuthProvider>
</QuteProvider>
</SynqProvider>
</CoreProvider>
);
};
Hierarchy Design Principles
- Core primeiro: Estados fundamentais ficam no topo
- Dependências claras: Cada provider depende apenas dos superiores
- Lazy loading: Providers carregam apenas quando necessário
- Error boundaries: Falhas isoladas não quebram toda a app
- Vantagens desta Arquitetura
✅ Para o Desenvolvedor
- Tipagem forte: TypeScript previne bugs em tempo de compilação
- Hot reload inteligente: MobX preserva estado durante desenvolvimento
- Debugging avançado: MobX DevTools + RxJS DevTools
- Testes simples: ViewModels são classes JavaScript puras
✅ Para o Negócio
- Time to market: Desenvolvimento mais rápido com menos bugs
- Escalabilidade: Arquitetura suporta crescimento da equipe
- Manutenibilidade: Código organizado facilita mudanças
- Performance: Updates otimizados do MobX
✅ Para a Equipe
- Onboarding: Estrutura clara facilita entrada de novos devs
- Colaboração: Padrões definidos reduzem conflitos
- Conhecimento: Patterns amplamente usados na indústria
🎯 Próximos Passos
Na Parte II desta série, vamos mergulhar em:
- Dependency Injection com Inversify: Como organizar dependências de forma testável
- Repository Pattern: Abstraindo acesso a dados e APIs
- Interceptors: Middleware para autenticação e logging
- Testing Strategy: Como testar cada camada da aplicação
- Performance Optimization: Técnicas para apps que escalam
Livros Indicados
Advanced React Native: Build Powerful Cross-Platform Mobile Apps with React Native

React Native: Desenvolvimento de aplicativos mobile com React

Reflexão Final
Construir uma arquitetura sólida é como construir uma casa: você não vê a fundação, mas ela é o que sustenta tudo. Invista tempo no setup inicial e sua equipe agradecerá nos próximos anos.