-
Notifications
You must be signed in to change notification settings - Fork 4
Camada "Domain"
A camada de domínio inclui todas as regras de negócio da aplicação e deve ser independente, não contendo nenhuma dependência de componentes externos. Ela realiza a integração entre a camada de dados e apresentação através de interfaces.
- Base: Pacote onde ficam as classes base que podem ser herdadas por outras classes
- DI: Pacote centralizador das dependências dessa camada.
- Entity: Pacote onde ficam as classes de modelagem para as regras de negócio
- Extensions: Pacote onde ficam as extensões para facilitar a manipulação das Views.
- Repository: Pacote onde ficam as interfaces que estabelecem um contrato de comunicação entre a camada de domain e camada de dados
- UseCase: Pacote onde ficam os casos de uso
Antes de iniciar a construção de um novo caso de uso, alguns conceitos precisam ser apresentados.
Classe base que representa um erro. Todo erro ocorrido que deva ser transportado pela camada de domínio deve ser um BaseErrorData
Classe genérica para mapear status de erro comuns.
Antes de criar um novo status, se questionar:
- Há necessidade de um tratamento específico para o erro?
- Há outro status que passa a mesma 'mensagem' de erro?
Se as respostas forem (1) Sim e (2) Não, então sinta-se livre para criar um novo status. Status já mapeados:
DEFAULT_ERROR(1000)
USER_NOT_LOGGED(1001)
FEATURE_FLAG_ENABLED_WITHOUT_DATA(1002)
NO_NETWORK_CONNECTION(1003)
USER_IS_NOT_IN_FEATURE_SEGMENT(1004)
DATA_NOT_FOUND(1005)
FEATURE_FLAG_DISABLED(1006)
Classe base para a criação de casos de uso que necessitam de algum parâmetro para rodar
Classe base para a criação de casos de uso que não necessitam de algum parâmetro para rodar
ResultWrapper é uma classe que encapsula objetos de erro e sucesso para que a comunicação entre camadas ocorra de forma mais padronizada.
Exemplo de uso:
if (networkStatusManager.hasConnection()) {
if (params.isValid) {
exampleRepository.doAnotherStuff(params.name)
} else {
ResultWrapper(error = BaseErrorStatus.USER_IS_NOT_IN_FEATURE_SEGMENT)
}
} else {
ResultWrapper(error = BaseErrorStatus.NO_NETWORK_CONNECTION)
}
CompleteResultWrapper é uma classe filha de ResultWrapper que adiciona propriedades de uma requisição
Representa os estados de requisição. Utilizado como statusCode no CompleteResultWrapper
Interface genérica para mapear um objeto para outro
São interfaces que estabelecem um contrato de comunicação entre a camada de domain e camada de dados
- Sufixo: todas as classes presentes aqui devem ser o sufixo "Repository"
interface ExampleRepository {
fun doStuff(): FeatureFlagModel<ExampleModel>
fun doAnotherStuff(param: String): ResultWrapper<List<ExampleModel>>
}
São classes de modelagem para as regras de negócio.
- Sufixo: todas as classes presentes aqui devem ser o sufixo "Model"
Cada caso de uso deve conter apenas uma função pública que, por convenção, se chama run(). Para facilitar e padronizar a implementação, pode-se herdar da classe BaseUseCase
ou NoParamsBaseUseCase
para casos de uso que necessitem executar o método run() de maneira assíncrona. Herdando ou não dessas classes, todos os casos de uso devem retornar um ResultWrapper
.
Se for necessário, pode-se criar métodos privados para melhor leitura e entendimento do código. Todo caso de uso deve ter o sufixo "UseCase".
Para realizar a comunicação com a camada de dados, o caso de uso receberá via injeção de dependência todos os repositórios necessário para que o mesmo seja executado. Não há problemas se for necessário chamar dois métodos do mesmo repositório.
O caso de uso não tem conhecimento de modelos externos à camada de domínio. Desta forma, apenas os modelos de entidade podem trafegar nessa camada.
Um caso de uso NUNCA deve conhecer outro caso de uso. Caso sinta necessidade de algum resultado de outro caso de uso, essa informação deverá ser passada via parâmetro.
Exemplo do que pode ser feito:
package com.example.domain.usecase
class ExampleUseCase(
val exampleRepository: ExampleRepository,
val otherExampleRepository: ExampleRepository
): BaseUseCase<ResultWrapper<ExampleResultModel, BaseErrorData<BaseErrorStatus>>, ExampleParam> {
override suspend fun run(params: ExampleParam): ResultWrapper<ExampleResultModel, BaseErrorData<BaseErrorStatus>> {
exampleRepository.doStuff(params)
doSomething()
...
}
private supend fun doSomething() {
otherExampleRepository.doStuff(params)
otherExampleRepository.doThings()
}
}
Exemplo do que não pode ser feito:
package com.example.domain.usecase
class ExampleUseCase(
val exampleRepository: ExampleRepository,
val otherExampleUseCase: OtherExampleUseCase //Dependência de outro caso de uso
): BaseUseCase<ExampleResultResponseModel, ExampleParam> {
override suspend fun run(params: ExampleParam): ExampleResultResponseModel { //Não retorna um ResultWrapper e trafega modelo que não pertence à camada de domínio
exampleRepository.doStuff(params)
}
}