Apps crescem. O que começa como um projeto de duas telas acumula regras de negócio, integrações, estados e edge cases. Sem estrutura, o código vira uma teia onde mudar uma coisa quebra outra.
Clean Architecture, proposta por Robert C. Martin, separa o código em camadas com responsabilidades bem definidas. A regra central: dependências só apontam para dentro. A camada de domínio não sabe que existe SwiftUI, nem banco de dados, nem API.
As três camadas
Domínio
É o coração do app. Contém:
- Entities: as estruturas de dados do negócio (
Transacao,Veiculo,Projeto) - Use Cases: as ações possíveis (
CriarTransacao,BuscarVeiculoPorId) - Repository interfaces: protocolos Swift que definem como os dados são obtidos
Nenhuma dependência de framework. Swift puro. Testável sem mocks de UIKit ou SwiftUI.
Dados
Implementa os protocolos do domínio:
- Repository implementations: chamam APIs ou persistência local
- Data sources:
RemoteDataSource(URLSession/REST) eLocalDataSource(SwiftData/CoreData) - Models: structs serializáveis com
Codable
Apresentação
A camada SwiftUI de fato:
- Views: telas e componentes reutilizáveis
- ViewModels:
@ObservableouObservableObjectque expõem estado para as views - Coordinators: gerenciam navegação sem acoplar as views entre si
Só depende de use cases. Não faz chamadas de rede diretamente.
Na prática: um exemplo
Para o FinPessoal, o fluxo de registro de uma transação é:
- O usuário preenche o formulário → view chama
CriarTransacaoUseCase - O use case valida a entidade e invoca
TransacaoRepository.salvar() - O repository delega para
LocalDataSource.inserir(transacaoModel) - Sucesso ou erro retorna pelo mesmo caminho via
async throws
Nenhuma dessas camadas conhece a implementação interna das outras — só os protocolos.
O ganho nos testes
Com essa separação, testar um use case é direto:
func testDeveRejeitarTransacaoComValorZero() async throws {
let useCase = CriarTransacaoUseCase(
repository: MockTransacaoRepository()
)
await #expect(throws: ValorInvalidoError.self) {
try await useCase.executar(
Transacao(valor: 0, descricao: "Teste")
)
}
}
Sem simulador. Sem banco de dados real. Só lógica de negócio.
O mesmo vale para os data sources: você testa a serialização Codable sem depender de rede real. E as views são testadas sem depender de regra de negócio.
Quando aplicar
Clean Architecture não precisa ser aplicada de forma rígida em apps pequenos. Em projetos com menos de dez telas e sem lógica de negócio complexa, uma separação mais simples (MVVM direto com @Observable) pode ser suficiente.
A arquitetura em camadas se paga quando:
- O projeto vai crescer (novas features planejadas)
- Há múltiplos desenvolvedores no time
- Existe necessidade de testes automatizados sérios
- A lógica de negócio é complexa ou vai mudar
Conclusão
Clean Architecture não é overhead para apps pequenos — é o que mantém apps pequenos enquanto eles crescem. A separação de camadas é o que nos permite adicionar uma nova feature sem medo de quebrar o que já funciona.
É o padrão que adotamos em todos os projetos do werkStadtG, e que recomendamos para qualquer app iOS que pretende durar.