Skip to content

Instantly share code, notes, and snippets.

@octavioturra
Last active December 4, 2025 21:25
Show Gist options
  • Select an option

  • Save octavioturra/b89b65da05b1200c250e6a6e0d4f15a2 to your computer and use it in GitHub Desktop.

Select an option

Save octavioturra/b89b65da05b1200c250e6a6e0d4f15a2 to your computer and use it in GitHub Desktop.

Blueprint do Projeto Android (Kotlin Padrão - MVVM)

Este documento define a arquitetura, as boas práticas de código e os princípios de desenvolvimento a serem seguidos no projeto Android, utilizando Kotlin e priorizando recursos estabelecidos da plataforma.

1. Arquitetura: MVVM (Model-View-ViewModel)

Adotaremos o padrão de arquitetura Model-View-ViewModel (MVVM) para garantir a separação de responsabilidades, testabilidade e manutenção do código.

1.1. Camadas e Responsabilidades

Camada Tecnologias Chave (Kotlin/Android) Responsabilidades
View (Activity/Fragment) Activity, Fragment, Binding (ViewBinding ou DataBinding) Observar o ViewModel, exibir o estado da UI, capturar eventos do usuário (clicks, inputs) e repassá-los ao ViewModel. Não contém lógica de negócios.
ViewModel ViewModel, LiveData ou StateFlow/SharedFlow (para o estado da UI) Manter o estado da UI de forma reativa e independente do ciclo de vida da View. Orquestrar a lógica de negócios, solicitando dados ao Repository.
Repository Interfaces/Classes Repository, Coroutines (para operações assíncronas), Room (SQLite) Abstrair as fontes de dados (local, remota). Gerenciar a lógica de cache, decidir de onde buscar os dados e mapear Entities para Models.
Data Sources Room DAO, Retrofit Service Implementação direta da comunicação com fontes de dados (ex: SQL Queries, chamadas HTTP).
Models data class Abstrações de domínio limpas e independentes de qualquer camada de persistência.

1.2. Comunicação entre Camadas

A comunicação deve ser unidirecional e reativa, garantindo o baixo acoplamento:

  1. View -> ViewModel: A View (Activity/Fragment) chama funções públicas no ViewModel para indicar ações do usuário (ex: viewModel.loadUsers(), viewModel.onUserClick(id)).
  2. ViewModel -> View (Reativo): O ViewModel expõe o estado da UI através de LiveData ou StateFlow. A View observa esses dados e se atualiza automaticamente.
  3. ViewModel -> Repository: O ViewModel chama métodos de uma interface do Repository (ex: repository.getUsers()). O ViewModel não sabe como o dado é obtido.
  4. Repository -> Data Source: O Repository coordena as chamadas a um ou mais Data Sources (ex: localDataSource.queryAll(), remoteDataSource.fetchLatest()).
  5. Data Flow: O fluxo de dados deve ser claro:
    • Entrada de Dados: Data Source (Entity) -> Repository (Mapeamento para Model) -> ViewModel -> View (Estado da UI).
    • Saída de Dados (Persistência): View -> ViewModel (Model) -> Repository (Mapeamento para Entity) -> Data Source.

2. Boas Práticas de Kotlin e Android

2.1. Kotlin Idiomático

  • Imutabilidade (Preferência): Use val sempre que possível. Prefira data class para Models e Entities.
  • Funções de Escopo: Utilize let, run, apply, with e also para melhorar a legibilidade e gerenciar o escopo de objetos nulos ou com configuração.
  • Tipos Nulos (?): Use o operador safe call (?.) e o operador elvis (?:) para lidar com nulidade de forma segura, evitando o operador de asserção não-nula (!!).
  • Coleções: Use tipos de coleção imutáveis por padrão (ex: List<T>), convertendo para mutáveis (MutableList<T>) apenas onde necessário.
  • Coroutines Tradicionais: Utilize suspend fun no Repository e ViewModel para operações assíncronas. Use viewModelScope no ViewModel e injete um CoroutineDispatcher no Repository para controle de threads (ex: Dispatchers.IO).

2.2. Separação de Camadas (Repository e Models)

É crucial separar as abstrações de domínio da lógica de persistência:

  • Models (data class): Representam os objetos de domínio que a View e o ViewModel consomem. Eles não devem ter anotações de banco de dados (ex: @Entity do Room).
    // Model usado pelo ViewModel e View
    data class User(val id: Long, val name: String, val email: String)

  • Entities (data class): Representam a estrutura da tabela no SQLite (Room).
    // Entity usado pelo Room e DAO
    @Entity(tableName = "user_table")
    data class UserEntity(
    @PrimaryKey val entityId: Long,
    val entityName: String,
    val entityEmail: String
    )

  • Mappers (Obrigatório): O Repository é responsável por mapear (converter) Entity para Model na leitura, e Model para Entity na escrita.
    // Lógica dentro do Repository
    fun UserEntity.toDomainModel(): User = User(id = this.entityId, name = this.entityName, email = this.entityEmail)

3. Princípios de Código Limpo e Métodos

3.1. Reforçando YAGNI, DRY e KISS

  • YAGNI (You Ain't Gonna Need It - Você Não Vai Precisar Disso):
    • Regra: Não adicione funcionalidade, bibliotecas ou complexidade que não sejam explicitamente necessárias para o requisito atual.
    • Prática: Evite arquiteturas super-abrangentes no início. Adicione injeção de dependência (ex: Koin ou Hilt) somente quando a complexidade das dependências justificar.
  • DRY (Don't Repeat Yourself - Não Se Repita):
    • Regra: Qualquer pedaço de lógica ou informação deve ter uma única, inequívoca e autoritária representação dentro do sistema.
    • Prática: Use funções e classes utilitárias para lógica comum (ex: formatação de datas, validação de inputs). Defina constantes em um único local.
  • KISS (Keep It Simple, Stupid - Mantenha Simples, Estúpido):
    • Regra: A simplicidade deve ser um objetivo chave no design.
    • Prática: Prefira métodos pequenos e focados. Evite aninhamento profundo de condicionais (if/else ou when). Se um método tem mais de 20 linhas, ele provavelmente precisa ser refatorado.

3.2. Boas Práticas em Métodos (Funções)

  1. Nomeclatura Explícita: O nome da função deve descrever exatamente o que ela faz. Use verbos (ex: calculateTotal(), fetchUser(), formatDate()).
  2. Funções Focadas (SRP - Single Responsibility Principle): Cada função deve fazer apenas uma coisa e fazê-la bem.
  3. Argumentos Mínimos: Prefira funções com zero, um ou dois argumentos. Evite mais de três argumentos. Se precisar de muitos, agrupe-os em uma data class ou use argumentos nomeados.
  4. Retorno Único: Uma função deve ter um único ponto de saída (retorno). Use return no início apenas para guarda (early exit) de casos de erro ou pré-condição.
  5. Evitar Efeitos Colaterais: Funções devem, preferencialmente, ser puras (retornar o mesmo resultado para os mesmos inputs e não modificar estados externos). Se houver efeito colateral (ex: salvar no banco de dados), deixe-o explícito no nome da função (ex: saveChanges()).

4. Estrutura de Diretórios (Exemplo)

.
├── ui/
│ ├── activity/
│ │ └── MainActivity.kt (View)
│ ├── viewmodel/
│ │ └── MainViewModel.kt (ViewModel)
│ └── adapter/
│ └── UserAdapter.kt
├── data/
│ ├── model/ (Abstrações de Domínio - Consumidas pelo VM)
│ │ └── User.kt
│ ├── repository/ (Interfaces de Contrato)
│ │ └── UserRepository.kt
│ ├── repository/impl/ (Implementação do Repositório)
│ │ └── UserRepositoryImpl.kt
│ ├── local/
│ │ ├── database/
│ │ │ └── AppDatabase.kt
│ │ ├── entity/ (Estrutura da Tabela - Usado pelo Room)
│ │ │ └── UserEntity.kt
│ │ └── dao/
│ │ └── UserDao.kt (Data Source Local)
│ └── remote/
│ └── service/
│ └── UserApiService.kt (Data Source Remoto)
└── util/
└── extensions/
└── Mappers.kt (Conversão entre Model e Entity)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment