Hanami vs o mundo

Destaque

Coloque-se na situação em que você precisa desenvolver um software corp enterprise em Ruby. Qual a primeira coisa que pipoca em sua mente neste momento?

Pois é:

Domain-Driven Design

A hype (olá pessoal de FP!) ideia do Domain-Driven Design (DDD pros chegados), é trazer o contexto de negócio juntinho do camarada que faz o software funcionar. Por que isso é bom? O principal argumento é: entendimento.

Poderia resumir todo o blue book (Domain-Driven Design: Tackling Complexity in the Heart of Software – Eric Evans) com a frase acima. Obviamente que seus problemas começam juntamente com seu objetivo de deixar o conhecimento alinhado entre todos envolvidos na solução. É exatamente por isso que o livro é extenso e detalhado, mas isso é assunto pra outro papo.

Qual a outra coisa que você pensa quando se fala enterprise ? Aha!

Patterns of Enterprise Application Architecture

O livro vermelhão do (Martin) Fowler é sem dúvidas um ponto de partida para falarmos daquele papo maroto de Arquitetura. Foi lindo! Todo mundo queria ser Arquiteto de Software, lembra disso? Velhos tempos hahaha!

Passada essa fase, começamos todos a pensar: o que é arquitetura de software? Com isso, veio aquela conversa de Arquitetura Evolucionária, ideia que prega que a arquitetura do seu software deveria emergir de acordo com o passar do tempo e das necessidades do software em questão.

De certa forma isso deveria ter sido falado lá atrás, no começo do epicentro do Domain-Driven Design e da importância por uma Arquitetura de Software definida, por mais tosca que parecesse ser.

Motivo simples: muito bolovo teria sido evitado e o nível de frustação e demissão regulados.

Arquitetura de Software

Enfim, a tal da Arquitetura tem sua utilidade, apesar de eu ver poucas pessoas definindo legal o que significa isso.

Podemos definir arquitetura como uma camada do seu software que vista pela outra camada é percebida como um blackbox, tendo pontos de comunicação externos definidos e padronizados.

Outra definição válida é de que arquitetura é todo design de software que agrupado representa ambos requisitos funcionais e não funcionais de uma parte do software. É assim que Clements P, et al define arquitetura de software:

As Clements P, et al. point out, all architectures are designs but not all designs are architectures. Designs that need to be bound in order for the system to meet its functional and nonfunctional requirements and goals are architectural in nature.

@IBM – Developer Works: Documenting Software Architecture Part 1

Dito isso, poderíamos entrar na Deep Web dos tipos de arquitetura de software, como Layered Architecture, Ports & Adapters, Clean Architecture, Hexagonal Architecture, CQRS.. opa, esse não!

Many people have been getting confused over what CQRS is. They look at CQRS as being an architecture; it is not.

O próprio senhor Greg Young disse que esse papo de que CQRS é arquitetura é infudado. E ele fala mais:

CQRS is a very simple pattern that enables many opportunities for architecture that may otherwise not exist. CQRS is not eventual consistency, it is not eventing, it is not messaging, it is not having separated models for reading and writing, nor is it using event sourcing.

Olé!

e o Hanami?

hanami-logo

O que um framework web Ruby tem a ver com tudo isso?

Tudo, obviamente!

A ideia geral do Hanami é ser desacoplado. Independente. Rebeldia? Jamais! O papo é que o framework por definição é feito para colaborar e não definir sua arquitetura no pior estilo One Size fits All Architecture.

Só por não definir uma arquitetura ele já possibilita ao desenvolvedor maluco definir sua própria arquitetura. E mais: evolui-la de acordo com suas próprias necessidades. Epa! Isso traz a tona o que eu falei lá em cima sobre Arquitetura Evolucionária.

weMor1k.gif

Legal, né?

Ah, e se eu não quiser definir nada? Quero seguir como uma boa ovelha que sou!? Neste caso o Hanami, mesmo não definindo uma arquitetura, sugere um Hexagonal Architecture out of the box. Sem pensar, você terá camadas bem definidas, isolando seu modelo dos seus adapters de apresentação e integração. Boom!

Arquitetura, Rails e o ecossistema Ruby

Rails acredito que seja a primeira palavra a vir à cabeça quando se fala em Ruby. A coisa é tão intensa, que não é difícil encontrar vagas e programadores entitulando-se: Rails Developer. Nem Engenheiro de Software; Nem (Back|Front)end Developer, tão pouco Ruby Developer.

Que o Rails é o framework mais conhecido dentro (e até fora) do mundo Ruby isso é um fato. Fato também é o quão simplificado o desenvolvimento utilizando ele é; Mas, já parou para pensar no trade-off existente aí?

Architecture: The Lost Years

Keynote apresentado pelo Unclebob lá no distante 2011, sobre o quão ofuscados ficamos com o Rails – e o que isso trouxe como consequência. Não é um ataque direto ao Rails, mas sim um: hey, vamos acordar pra vida e utilizar o Rails de uma forma um pouco mais decente ?

Se você nunca viu esta palestra. Pare de ler agora e veja até o final. Depois, continue lendo (pois vou assumir que você viu ao keynote)

Obviamente que ele não é o único a pensar assim. A apresentação dele gerou diversas threads na Internet sobre como fazer um aplicativo Rails desacoplado e mais sob o controle do desenvolvedor do que do framework.

Acredito que o desejo de muitos seria ter uma especie de Symfony Framework + Doctrine 2.x para Ruby. Desacoplamento. O desenvolvedor escolher as peças; ou como o Unclebob disse na palestra: acessar o diretório do projeto e pensar: “Ah, isso é um software de X; ao invés de: Aaah, isso é um app Rails.”

Moldar o Rails para algo mais Domain-Driven Design

Resolvi apostar. Aposta simples, silenciosa. Aproveitei a (maior) modularidade do Rails 4 para começar a extrair algumas coisas e definir uma estrutura nova de diretórios. Preferir transparência no domínio às convenções do Rails. Movi tudo para o /domain/(modulo)/.

  • O Rails convenciona que Models devem estar dentro de app/models, caso contrário, o ActiveModel não funciona corretamente as relações de ORM.

Ao topar com este empecilho, não quis me alongar nisso e preferi manter todos os “Models” dentro do diretório que se é convencionado. Já viu a sopa de diretórios que isso ficou, né ? /domain; /app/models.

  • O ActiveRecord possui features intrigantes, como por exemplo os scopes, porém os Contras são maiores do que os Prós.

O Avdi Grimm no livro Objects on Rails mostrou passo a passo como ele construiu um software Rails-based postergando relacionar suas entities ao ActiveRecord. No final, ele preciso modificar bastante coisa para tê-las in place. Alguns testes precisarão ser de integração (scopes, olho para vocês) – pois teste de Unidade Comportamental (Unit-Testing Behavioral) não garantirá que o scope está correto mesmo.

Pode parecer xiitismo, mas o ActiveRecord é pesado. E esse peso aparece ao rodar os testes de unidade. Mesmo utilizando Test Double, só de precisar subir toda aquele estrutura do ActiveRecord, o processamento já fica mais lento do que se as entidades fossem livres do meio de persistência.

  • Convention Over Configuration em um framework Arquitetural (One Size Fits All) é nocivo para adotar meios e métodos alternativos.

Seguir o caminho sem o Rails

Optar por deixar o Rails de lado, utilizando somente o que você precisa e quando precisa é uma das alternativas dos Rubistas (outside Brasil) atualmente. Isso explica a popularidade que o Sinatra ganhou nos últimos tempos. Sinatra pois ele fornece uma interface simples entre o Rack e sua aplicação web. Sinatra, pois ele é somente isso, deixando todas as demais decisões para você. Com isto em mente, vale lembrar de que precisará criar suas próprias coisas.

Algumas muito simples outras nem tanto:

  • Criar e configurar seu config.ru para que o Rack o leia e suba um stack;
  • Configurar seu spec_helper.rb ou test_helper.rb para Unit Testing com RSpec ou MiniTest;
  • Criar seu Environment Manager, para conseguir distinguir Development, Testing e Production modes – Dica: ENV['RACK_ENV'] pode ser usado pra isto;
  • Configurar seu Rakefile para manipular Rake Tasks;
  • Definir um Autoloader para ler sua estrutura de diretórios. /domain e /controllers, como é o meu, por exemplo.
  • Migration, Validation, ORM, etc.

Particularmente quando fiz isso pela primeira vez me senti perdido. Não é cuspindo no prato não, mas o Rails o faz criar manias e uma certa dependência nele.

Importante resaltar que você não precisará das coisas da mesma forma que o Rails criou. A vantagem é que você cria as coisas on demand, voltadas às suas necessidades. Por exemplo, meu Environment Manager é muito mais simples do que o do Rails, entretanto, consigo com ele diferenciar os Envs e subir coisas diferentes.

Outra vantagem é que construo um stack muito simples, rápido e customizado. Precisei apenas de 2 dias para ter um sandbox com Sinatra funcionando e meus testes de unidade com RSpec ficam na casa dos 0.00xx sec.

O tempo “gasto” estudando como fazer certas coisas vale muito a pena, pois você entende melhor como funciona a arquitetura por debaixo do Rack. Você no controle!

Desta forma, até agora não poluí meu código com ORM, Validations, etc. Quando realmente preciso de alguma coisa, vou lá e pontualmente a configuro/instalo.

Próximos steps: Virtus gem para Entity Modeling; ActiveModel::Validations ou o ActiveValidators; Sequel ou Rom-rb; Asset Pipeline – Sim, nada me impede de utilizá-lo standalone numa arquitetura onde eu consigo ter o controle 😉

Aos poucos pretendo ir comentando minhas aventuras nessa área. O resultado tem sido bem satisfatório até o momento. Recomendo tentar você também!

Concluindo

Não me entenda errado: o Rails possui seus pontos positivos. Particularmente, gosto do Asset Pipeline e dos Validators (externos à classe, não aqueles validates :field). O propósito aqui é fazer o que muitos developers lá fora já fazem: abrir o olho da comunidade de que há um mundo imenso fora do Rails e que você precisa pensar: eu preciso do Rails ou estou apenas com medo/preguiça de seguir outra solução?

1 – Foto da arquitetura da Ponte de Londres.

Ruby e as variáveis de classe

Recentemente percebi algo muito peculiar no comportamento do Ruby quando trata-se de variáveis de classe. Sabemos que:

class Foo
  @@bar = 'baz'

  def self.bar
    @@bar
  end
end

p Foo.bar #baz

Porém, certa vez, aconteceu um caso peculiar.

class User < ActiveRecord::Base
    @@default_user_locale = :en

    def default_user_locale
         # …
    end
end

p User.first.default_user_locale

Isso, conforme o esperado, deveria retornar en, mas acredite, retornou outra coisa.

O fato

Até não ter ideia do que ocorrera, resolvi pesquisar. Encontrei pessoas afirmando que: "@@default_user_locale era uma palavra reservada do Rails" =S
Pode ser qualquer coisa, menos isto, afinal estamos falando de uma classe!

Daí, lendo o livro Eloquent Ruby, me deparei com o tal problema. Não é culpa do Rails (ou POG do Valim), e sim, um comportamento maluco do Ruby. O dito cujo, trata variáveis de classe como variáveis globais. Logo, se você possuir uma herança, onde em ambas classes a constante for definida, o valor da última classe a ser carregada definirá o valor da variável da classe.

Em termos práticos:

foo.rb

class Foo
    @@size = :small

    def self.size
     @@size
    end
end

bar.rb

class Bar  small

A solução

A sugestão para sanar este problema, de acordo com o autor do livro é utilizar variáveis de instância com métodos de acesso estáticos. Desta forma, o valor nunca será sobreescrito caso uma classe pai tenha definido a mesma variável internamente. Ficaria assim:

class Foo
    @size = :small

    def self.size
        @size
    end
end

Fica o alerta para evitar uma noite perdida 😉

Traits em PHP. Herança Horizontal

Post revisitado em junho/2014. Tudo que está aí funciona na versão atual PHP 5.5.x. Enjoy 😉

Antevejo que no futuro, o PHP como uma linguagem iterativa e incremental que é, adicionará ao seu núcleo um recurso muito valioso na categoria orientação à objetos.Antevejo ele, os Traits. E mais, consigo prever que será algo assim:

ola()} trait !";
}
}

$ola = new Mundo();
$ola->world();
// Olá trait !

Ok, não sou bidu. Realmente os Traits estão na lista do possível-futuro PHP 5.4 e eu, baixei a build de 31/03/2011 para testar.

Traits

O PHP não suporta herença multipla – amém – e por este motivo, às vezes ficamos limitados em algumas decisões de projeto (design). O maior problema neste ponto é que toda herença até agora (PHP 5.3.x) é vertical, ou seja, se eu precisar de um nível de abstração diferente no meio do processo, ou precisarei adicionar a nova abstração e reescrever tudo abaixo dela, ou ainda, terei que duplicar o código pois não conseguirei satisfazer a herença.

É nesse ponto que os Traits aparecem. Diferentemente da herença (vertical), os Traits possibilitam-nos criar herenças horizontais.

Exemplo, dada a necessidade: haverão Funcionários, Supervisores, Gestores e Diretores no sistema e estes possui em comum o fato de receber salário. (capitalismo). Uma possibilidade seria:

Pessoa > Funcionario
Pessoa > Supervisores
Pessoa > Gestores
Pessoa > Diretores

Tudo perfeito. Porém, chega outra necessidade: Gestores e Diretores tem a capacidade de demitir. Gestores precisam gerenciar cronogramas, assim como os Supervisores fazem.

Pessoa.demitir(pessoa) não faz sentido, pois afetiria todos.
Gestor.demitir(pessoa) e Diretor.demitir(pessoa) atende, mas duplicaria o código.

Pessoa > Supervisor > Gestor
Pessoa > Diretor

Shi, apareceu outro nível hierarquico. Traits auxilia exatamente neste ponto ! Vejamos:

Trait Cronograma

Trait Evil (que contém recursos para demitir uma pessoa)

Pessoa > Funcionario

Pessoa > Supervisor : Cronograma

Pessoa > Gestor : Cronograma : Evil

Pessoa > Diretor : Evil

Note que o Pessoa > Supervisor : Cronograma – os dois pontos para indicar o Trait são de minha autoria – era o Pessoa > Supervisor antigo. Isso porque as atribuições para gerenciar cronogramas foram passadas para o Trait.

Neste pequeno exemplo, o Trait nos auxiliou com um problema de domínio. Acredito que ele também aplique-se e muito bem para problemas onde temos domínios transversais, pois, nele ficariam as atividades do outro domínio pode o domínio principal teria acesso aos recursos sem quebrar a SRP.

Traits em PHP

Como disse no começo, o futuro-provável PHP 5.4 virá com o Trait. Por enquanto essa implementação funcionaria:

definirPressao(self::$ATRASADO);
        $this->youTube();
    }

    private function youTube() {

    }
}

class Funcionario extends Pessoa {
}

class Supervisor extends Pessoa {
    use Cronograma;
}

$colaborador = new Funcionario();
$controlador = new Supervisor();
$controlador->cobrarDe($colaborador);

Os Traits possuem boas capacidades com métodos de instância e estáticos. Vamos explorar um pouco mais:

sayHello();
$view->sayWorld();
// exibe: Olá Mundo

Pegando informações da Classe

eu();
// exibe: Foo

Usando Variáveis de instância da classe:

hello} world";
    }
}

class Hello {
    private $hello = "Hello";
    use World;
}

$hello = new Hello();
echo $hello->help();
// exibe: Hello world

Modificadores de acesso dos traits na classe

ola();
// Fatal Error....

Modificadores de acesso com alias para nome de método

aloha();
// Olá mundo

Há outros vários recursos como métodos abstratos nos Traits, precedência, métodos estáticos, late static bindings, métodos mágicos, alias para nome de método do Trait ao usá-lo na classe. Para Rubistas ou Scalistas (?) as possibilidades são as mesmas dos Mixins e Traits de Ruby e Scala respectivamente.

RFC do PHP Trait
PHP doc do Trait. PHP >= 5.4