# GO: O que raios é "config pattern"?

*Nota: esse artigo foi publicado primeiramente no blog.codeaspiras.dev, que foi encerrado. Transferi para esse blog para que o mesmo não se perdesse.*

Olá, leitores do CodeAspiras! Estou aqui novamente com mais um artigo de **Golang** para disseminar conhecimentos relevantes para a área. Dessa vez, venho com mais um conceito de desenvolvimento que é bom conhecer mas que não deve ser tão utilizado assim, que é o Config Pattern ou "padrão de configuração".

Como o próprio nome diz, trata-se de uma estratégia de estrutura de argumentos e/ou configurações de qualquer coisa. Você consegue até aplicar em outras linguagens, pois o conceito é abrangente o suficiente para produzir em quaisquer linguagens que fornecem alguma ferramenta de captura de argumentos dinâmicos. No caso do Golang, nós usamos o `pack operator`.

> O que raios é isso, Kiko?!

É o oposto de `unpack` ou `spread`. Em outras linguagens, em situações onde você quer quebrar um `array` em uma lista de valores, geralmente você "desempacota" o array com a assinatura `array...`, onde os três pontos no final significam que você vai pegar todos os valores e distribuir na linha onde a sentença está escrita.

No caso do `pack`, é justamente o oposto. Você vai ter uma lista de valores e quer "empacotar" tudo em um único `array`. No caso do Golang, é em um `slice`, ficando assim:

```go
func MyFunc(arguments ...string) {
    // nesse caso, a assinatura final do argumento 'arguments' é []string
}
```

Se a linguagem que você usa fornece algum recurso para capturar uma lista indefinida de argumentos, então você consegue aplicar o `Config Pattern`.

## OK, mas o que é isso?

`Config Pattern` nada mais é que um DTO gerenciado por pequenas funções.

*Não sabe o que é DTO? Dá uma lida*[*nesse outro artigo que expliquei direitinho*](https://telegra.ph/Data-Transfer-Object---DTO-02-15)*(escrevi via Telegraph, numa época obscura que eu publicava coisas para só uma pessoa ler... Eu deveria republicar aqui no CodeAspiras? Comenta aí).*

A principal diferença seria sobre qual lado da função retém a responsabilidade de alimentar os dados do objeto que carrega informação. O DTO é alimentado por quem chama a função, antes de chamá-la. Já o Config Pattern, é alimentado dentro da função. Por isso você cria funções que servem para injetar valores no objeto de configuração.

![Exemplo visual descrevendo os cenários DTO versus Config Pattern, onde a função em DTO recebe um objeto construído e a função em Config Pattern recebe vários valores para construir um objeto.](https://cdn.hashnode.com/res/hashnode/image/upload/v1706582282826/3aa887fa-7a6f-4a0e-860f-df1b780856c5.png align="center")

> E pra que serve isso tudo, Kiko?!

Para ter um controle rígido do que pode e não pode ser inserido. Por exemplo, no Golang, você pode desenvolver uma configuração 100% privada, expondo somente algumas possibilidades pra quem vai chamar sua função.

Digamos que estamos desenvolvendo um `Logger` e queremos que o desenvolvedor que irá usá-lo possa configurar somente prefixo ou sufixo. Se você implementar em DTO, você teria algo mais ou menos assim:

```go
package mylog // logger.go

type LoggerConfigDTO struct {
    Prefix string
    Suffix string
}

type Logger struct {
    config LoggerConfigDTO
}

func New(config LoggerConfigDTO) *Logger {
    return &Logger{config: config}
}
```

E aí, quem fosse inicializar seu `Logger` poderia fazer algo assim:

```go
mylog.New(mylog.LoggerConfigDTO{
    Prefix: "$timestamp [$level]",
    Suffix: "/n",
})
```

Só tem um problema nisso. Lá no enunciado, eu disse que era para ter somente prefixo **ou** sufixo, **não ambos simultaneamente**, correto? Então essa implementação não está adequada.

Uma possível solução seria somente aplicar o prefixo se o sufixo estiver vazio e/ou vice-versa, porém isso iria impactar o tempo de escrita de logs, adicionando uma condição para o momento de execução. Outra solução seria, já na hora de inicializar a struct, verificar se os dois estão preenchidos e apagar um arbitrariamente.

Em ambas as soluções, nós estamos decidindo algo e gerando um **comportamento obscuro** na nossa aplicação. O desenvolvedor que usasse seu `Logger` só iria perceber esse comportamento quando já estivesse vendo os logs em produção com alguma informação faltando. Claro, você também pode deixar um aviso gigante na documentação... Enfim...

## Em busca do Config Pattern

![Personagem de O Hobbit correndo com a legenda em inglês "I'm going on an adventure!" (Estou indo em uma aventura!)](https://media.giphy.com/media/HVr4gFHYIqeti/giphy.gif?cid=790b76111pdkaz7v01fjry0zolodromomr76xbclrrjttci6&ep=v1_gifs_search&rid=giphy.gif&ct=g align="center")

Antes de prosseguir, deixe-me alertá-lo(a) novamente: só use isso se você precisar de controle rígido sobre configurações, pois a implementação pode deixar o código bem massante. Outro ponto: embora você possa implementar a configuração de forma privada, sempre que possível, faça-a pública. Assim você facilita a escrita de testes de outros desenvolvedores, beleza? Tendo uma interface de configuração pública, eles conseguem mockar o setup e fazer seu código ter um comportamento específico nos testes. Mas chega de papo e vamos lá.

O primeiro passo que você precisa para transformar seu `DTO` em um `Config Pattern` é criar uma assinatura para funções de manipulação dos dados. No caso, eu vou chamar de `LoggerOption`:

```go
package mylog // logger_config.go

type LoggerOption func(c *LoggerConfigDTO)
```

Nessa assinatura, estou dizendo que um `LoggerOption` é uma função que recebe uma referência/ponteiro de `LoggerConfigDTO` e não retorna nada. Basicamente, essa função deve alterar a referência que recebe no argumento e nada mais.

Tendo essa definição, você pode criar as funções que manipulam os dados do DTO. Essas funções recebem como argumento o dado a ser inserido na configuração e retornam um `LoggerOption`, ou seja, retornam a função de injeção do dado no DTO:

```go
package mylog // logger_config.go

type LoggerOption func(c *LoggerConfigDTO)

// WithPrefix defines the prefix to be injected in all logs. It erases the suffix, if it's filled.
func WithPrefix(prefix string) LoggerOption {
    return func(c *LoggerConfigDTO) {
        c.Prefix = prefix // aqui aplicamos o prefixo na configuração
        c.Suffix = "" // aqui limpamos o sufixo para não ter coexistência
    }
}

// WithSuffix defines the suffix to be injected in all logs. It erases the prefix, if it's filled.
func WithSuffix(suffix string) LoggerOption {
    return func(c *LoggerConfigDTO) {
        c.Suffix = suffix // aqui aplicamos o sufixo na configuração
        c.Prefix = "" // aqui limpamos o prefixo para não ter coexistência
    }
}
```

Note que adicionei dois comentários indicando o comportamento de cada configuração. Esses comentários são chamados de `godoc` e são suportados por quase toda IDE que podemos usar para desenvolver Golang. Dito isso, ao invocar a função, o desenvolvedor já vai saber dos efeitos obscuros - se ele ler, claro. E isso não é exclusivo de Config Pattern. Por favor, use `godoc` em tudo, até em propriedades, rs.

E por último mas não menos importante, agora podemos modificar a função de inicialização para receber um pack de `LoggerOption` ao invés de um DTO preenchido, **migrando a responsabilidade de construção do DTO** para dentro da função:

```go
package mylog // logger.go

type LoggerConfigDTO struct { // isso pode permanecer assim
    Prefix string
    Suffix string
}

type Logger struct {
    config LoggerConfigDTO // aqui também
}

func New(options ...LoggerOption) *Logger {
    config := LoggerConfigDTO{} // construimos o DTO
    for index := 0; index < len(options); index++ {
        options[index](&config) // executamos cada LoggerOption na referência do DTO
    }

    return &Logger{config: config}
}
```

E com isso, ao invés de inicializar um DTO, agora o desenvolvedor poderá inicializar o `Logger` com várias chamadas de `LoggerOption`:

```go
mylog.New(
    mylog.WithPrefix("$timestamp [$level]"),
    // mylog.WithSuffix("\n"),
)
```

Fim. Você rapidamente migrou o padrão do seu projeto de DTO para Config Pattern. Caso mantenha a assinatura das opções e do DTO como públicas, você dará ao desenvolvedor a possibilidade de criar novos controles de configuração, mas ele só vai poder manipular o que for público.

Então você realmente vai ter muito mais domínio sobre o código e a escrita vai ficar mais semântica. Às vezes temos de lidar com campos com nomes esquisitos e, com Config Pattern, temos a possibilidade de escrever funções com nomenclaturas mais claras sobre o que estamos configurando.

A documentação em `godoc` está disponível tanto para DTO quanto para Config Pattern, então isso não é exatamente vantagem pra ninguém. Porém, quando se trata de sobreescrever configurações padrões, o Config Pattern é a melhor escolha.

> Como assim, Kiko?

Digamos que, por padrão, eu que o prefixo seja sempre `"$timestamp [$level]"`. Com o pattern, é bem simples:

```go
package mylog // logger.go

type LoggerConfigDTO struct {
    Prefix string
    Suffix string
}

type Logger struct {
    config LoggerConfigDTO
}

func New(options ...LoggerOption) *Logger {
    config := LoggerConfigDTO{
        Prefix: "$timestamp [$level]", // valor padrão
    }
    for index := 0; index < len(options); index++ {
        options[index](&config)
    }

    return &Logger{config: config}
}
```

Basta instanciar o DTO com o valor inicial de cada opção, certo? Mas se você não é responsável pela inicialização dele, como você vai garantir esse valor padrão? Você teria de partir para solução de mesclagem de structs e isso não é exatamente necessário se você usa Config Pattern, dado que você é quem inicializa o DTO.

É nesses casos que esse padrão ganha força e se torna prático.

![Body builder feminina exibindo seus bíceps definidos, como analogia à "força" do Config Pattern.](https://media.giphy.com/media/l0Ex4MZdAVJPHxjvG/giphy.gif?cid=790b7611aoiesw8pkr3y58ndl8l1vn2jm0xh9qf18yltq9f9&ep=v1_gifs_search&rid=giphy.gif&ct=g align="center")

> Alguém já usou isso antes, Kiko?

Com certeza! Eu já cheguei a dar palpite num Config Pattern do [Tracer do Datadog](https://github.com/DataDog/dd-trace-go/blob/d7ed3ea75ae8cc691d34f405debe585874778df1/ddtrace/tracer/tracer.go#L135). Eles fizeram as configurações privadas e eu queria modificar o logger deles em outro pacote. Você pode ver a issue que abri aqui: [https://github.com/DataDog/dd-trace-go/issues/1331](https://github.com/DataDog/dd-trace-go/issues/1331) . No final das contas, eles perceberam que centralizaram a configuração de logger de todos os produtos no Tracer e resolveram refatorar essa lógica.

Outra biblioteca bem famosa que usa o Config Pattern é o [GORM](https://github.com/go-gorm/gorm/blob/418ee3fc1939d87a05bbb8ac6d7c7223e2c4571f/gorm.go#L121).

Se você procurar direitinho, você vai perceber que muitas bibliotecas usam essa estratégia para estabelecer um controle mais rígido sobre os argumentos. Então sim, muita gente usa.

---

Curtiu? Comenta e compartilha! <s>E lembrando: esse blog é nosso! Se tiver algo que queira publicar por aqui, fala comigo que te convido como colaborador do blog. Pode ser sobre qualquer coisa da área de tecnologia! Algo que acabou de estudar e tal. Antes de publicar, nós revisamos o conteúdo, assim podemos te corrigir se tiver aprendido algo errado e reforçamos nossos conhecimentos juntos</s>. Bora?

E por hoje é só!

Inté.
