---
# System prepended metadata

title: Untitled

---

Pessoal, puxei o Raul pra conversar sobre os pontos que ele levantou, acho pertinente compartilhar aqui, e talvez até alterar a decisão tomada anteriormente. Vai ficar um post meio grande, mas vamo nessa.

## Com a decisão atual (passando pelo construtor), teriamos uma classe no seguinte estilo:
``` Ruby=
class DecidePendencyTypeService
  def initialize(author:, pendency_type: nil)
    @author = author
    @pendency_type = pendency_type
  end

  def call
    return @pendency_type if pendency_type_known?

    define_pendency_by_role
  end

  private def define_pendency_by_role
    return 'evaluation' if @author.roles.map(&:to_s).include?('evaluator')
    return 'operation' if @author.roles.map(&:to_s).include?('operator')
    return 'antifraud' if @author.roles.map(&:to_s).include?('analyst')
  end

  private def pendency_type_known?
    ProposalNote.all_pendency_type.map(&:to_s).include?(@pendency_type)
  end
end
```
Nesse caso temos uma classe bem estruturada e até simples, porém tem alguns pontos que levantaram atenção:
1) Não estamos incentivando o uso de funções puras.
2) A existência do initialize pode gerar ambiguidade sobre onde chamar as funções (no call ou no próprio initialize), mesmo que tenhamos o controle disso no review de PRs e ADRs, o simples fato de existir o initialize definido já é uma brecha.

## Caso optemos por passar todos os parâmetros no call teríamos a mesma classe mais ou menos assim:

``` Ruby=
class DecidePendencyTypeService
  def call(author:, pendency_type: nil)
    return pendency_type if pendency_type_known?(pendency_type)

    define_pendency_by_role(author)
  end

  private def define_pendency_by_role(author)
    return 'evaluation' if author.roles.map(&:to_s).include?('evaluator')
    return 'operation' if author.roles.map(&:to_s).include?('operator')
    return 'antifraud' if author.roles.map(&:to_s).include?('analyst')
  end

  private def pendency_type_known?(pendency_type)
    ProposalNote.all_pendency_type.map(&:to_s).include?(pendency_type)
  end
end
```
Nesse caso o código pra mim me pareceu um pouco mais limpo.
1) Incentiva o uso de funcoes puras
2) Remove a possibilidade de ter comportamento espalhado (initialize e call)

Essas melhorias já seriam bons pontos que favoreceriam a adoção desse padrão. Mas olhando mais a fundo a gente ainda conseguiu encontrar algumas melhorias com o uso de mocks nos testes.

## Como isso ajuda na hora de fazer testes?

Se quisermos testar quem interage com `DecidePendencyTypeService` e precisarmos saber mockar os retornos para que se torne um teste unitario, e garantir que o teste seja paralelizável. 

No case dos parâmetros enviados via construtor vamos precisar testar da seguinte forma:

```ruby=
    instance_of_service = InstanceDouble(DecidePendencyTypeService)
    allow(DecidePendencyTypeService)
        .to receive(:new)
        .with(author: author)
        .and_return(instance_of_service)

    allow(instance_of_service)
        .to receive(:call)
        .and_return(pendency_type)
```

Nesse caso tivemos que definir a instancia que o `new` retornara e dar um allow nessa instancia a retornar o valor que queremos para o metodo call.

```ruby=
    allow_any_instance_of(DecidePendencyTypeService)
        .to receive(:call)
        .with(author: author)
        .and_return(pendency_type)
```
Acabamos mockando menos e deixando o setup mais simples e objetivo. 

