Muitas vezes, em um projeto em Rails, nos vemos encorajados a escrever parte da lógica da aplicação em nossos controllers. Talvez isso acontença, principalmente, pelo fato da grande maioria dos exemplos existentes na internet e literatura (de Rails) não mostrar preocupação com a arquitetura da aplicação (até pelo foco nestes casos ser a tecnologia, em si).
Acho que essa falta de precupação com design mais rico se justifica na maioria dos casos, onde a aplicação é pequena, o escopo do projeto é bem limitado ou existe uma necessidade imediata de lançar um produto ao público (como acontece em muitas startups - e nada impede um ‘refactoring’ posterior, mas isto é outra estória…). Existem, porém, outros casos… Aplicações grandes, complexas ou onde a criticidade das informações é elevada, requerem cuidados maiores com o design da solução (ou não… dependendo de quão suicida o time for!).
Após trabalhar em algumas aplicações grandes escritas em Rails (apps com mais de 15.000 linhas de código), muitas coisas que passariam despercebidas em alguns casos, tornam-se evidentes, principalmente quando modificações são necessárias: implementação de nova funcionalidade, refactoring ou correção de bugs…
Abaixo segue um exemplo de extração de objetos e lógica de domínio do controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
O principal problema existente neste controller é que ele, enquanto camada de parte da camada de visualização, têm conhecimento de como aplicar uma regra de negócio (além de outros problemas de design, como testabilidade, violação SOLID, enfim, este não é o foco no post).
A ‘regra de negócio’, enquanto parte de maior importância no sistema, devia estar isolada, independente de como é implementada sua parte “visual”.
Imagine, neste cenário, que será necessário fornecer esta mesma funcionalidade via WebService SOAP. É fácil copiar e colar este código no “handle” do request via SOAP, não?!? É fácil até que exista um novo requerimento ou que seja necessário uma adição/modificação do que existe. Por exemplo, surge a necessidade de que pedidos cancelados devem notificar o departamento de vendas… 2 lugares para modificar, maiores as chances de que algo seja esquecido no caminho, fora os outros problemas anteriormente descritos…
Então, para evitar estes problemas, podemos seguir a seguinte abordagem:
1) Extraímos a lógica de cancelamento e notificação para um objeto (“Domain Service”, se quisermos definir assim, segundo concepções do DDD):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
2) Adicionamos a manipulação dos atributos internos relativos ao cancelamento de pedidos ao próprio pedido, que deveria ter este conhecimento.:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
3) Por último, modificamos nosso controller:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Acho que a idéia é mais ou menos essa… Com esse pequeno (e fácil!) refactoring do controller, explicitamos o caso de uso mencioando e nosso controller passa a ser somente um consumidor de nossa API para cancelamento de pedidos!!! Parece razoável (apesar de ainda ser possível melhorar isso)…
O livro do Eric Evans - “Domain Driven Design - tackling complexity in the heart of software” é, na minha opinião, um livro um pouco difícil de ser lido e “digerido”, porém consegue fornecer um insumo suficiente para desenvolver software com um “mindset” que prima a representação em código do negócio de nossos clientes.