O que extração significa neste app
Uma regra, e o formato de código que ela produz
O MEMORY.md deste projeto descreve a arquitetura como um Layer Contract. A lógica de negócio fica em services, que devolvem Result = Data.define(...). Perguntas só de leitura, que cruzam models, ficam em query objects, que devolvem o mesmo tipo de Result. Os models guardam validações, scopes e associações, sem lógica de negócio. Os controllers cuidam do HTTP e chamam um service para uma escrita, ou uma ou mais queries para uma leitura. Lido como regra, isso é abstrato. Lido contra os arquivos do app, é um formato de código específico e verificável, e este curso passa as cinco primeiras aulas mostrando esse formato antes de pedir que uma ferramenta de IA o produza.
O padrão, contado
Faça um grep em app/services e app/queries deste app e uma convenção aparece sozinha. 97 dos 124 arquivos Ruby desses dois diretórios definem Result = Data.define(...), muitas vezes na primeira linha, e expõem um método de classe self.call que monta e devolve um. O resto são outros tipos de objeto: policies, pequenos value objects e alguns auxiliares específicos que não precisam de um resultado de sucesso ou falha. Para a maior parte do código, "extração" neste app tem um formato literal:
module SomeNamespace
class SomeService
Result = Data.define(:success, :data, :errors)
def self.call(...)
# a lógica de fato
Result.new(success: true, data: ..., errors: [])
end
end
end
Uma action de controller que chama SomeNamespace::SomeService.call(...), confere result.success e renderiza ou redireciona faz todo o seu trabalho em poucas linhas. A lógica que decidiu sucesso ou falha nunca toca o controller.
Por que um query object é diferente de um service
Os dois compartilham o esqueleto Result/self.call, mas respondem a perguntas diferentes. Um service muda algo, como dar XP, salvar um parceiro ou publicar um comentário, e informa se a escrita deu certo. Um query object responde a uma leitura, muitas vezes em mais de um domínio, sem gravar nada. O Enrollment::LeaderboardQuery deste app é o exemplo mais claro. Ele lê Identity::User e Enrollment::ExperiencePoint no mesmo método para montar as linhas do ranking, uma pergunta entre domínios que não pertence nem a Identity:: nem a um método de model. A aula 5 parte desse arquivo.
O que "extração" quer dizer, na prática
Nem todo controller precisa disso. Um controller que já delega bem não tem nada para extrair, e reconhecer isso é tanto uma habilidade quanto achar algo para tirar. Extração é o que acontece quando uma lógica que decide algo fica fora de um service ou query object. Pode ser uma regra de negócio condicional, um cálculo de várias etapas ou uma leitura entre domínios, solta numa action de controller, num model além do formato dos dados, ou duplicada em dois lugares que deveriam compartilhar um só. A aula 2 procura isso em três controllers deste app.
O que este curso cobre
- Auditar três controllers deste app atrás de candidatos a extração, e relatar o que for encontrado como é.
Partners::FetchSiteIconService, um service de responsabilidade única, lido com atenção.Enrollment::SubmitQuizService, um service que orquestra outros objetos em vez de reimplementar o trabalho deles.Enrollment::LeaderboardQueryeEnrollment::UserGamificationQuery, query objects no namespaceEnrollment, e por que o namespace do primeiro não bate com um model que ele lê.- Pedir a uma ferramenta de IA uma extração nova que siga as convenções deste app em vez de um formato genérico.
- Juntar o fluxo, da auditoria a uma extração revisada e funcionando.
Continue com a aula 2
Auditando controllers deste app atrás de candidatos a extração
Audita três controllers linha a linha e conclui que cada um já delega suas decisões a services.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.