Olá, como vai você?
As polêmicas
Do lado de cá, continuo me metendo em polêmicas e mais polêmicas nos grupos de discussões nacionais, e nesse final de semana decidi sair desses grupos. A discussão por aqui não é muito produtiva, leva-se tempo para que as pessoas foquem no foco de uma discussão enquanto, outros fazem agressões descabidas, ou mesmo falem muita besteira. Chato isso, mas de fato, há muita gente achando que sabe, o que não sabe!
As dúvidas
A dinâmica começou com meus questionamentos sobre DDD, e a intenção é simples: Não uso DDD, nunca tentei usar, estudei superficialmente para saber o que é, mas não me chamou a atenção por diversos pontos. Foi uma euforia só no DNA, em 2010, e ao mesmo tempo o que se tinha como resultado é que cada um tinha uma visão diferente da coisa. Resolvi esperar e questionar:
Dúvidas de 1 milhão de dolares:
* Após alguns anos usando DDD o que acharam, qual é o feedback?
* Quais foram os pontos positivos e negativos?
* Usaram à risca, ou pedaços (pílulas) de DDD em seus projetos?
* Após alguns anos, DDD permanece consistente? Ou tende a desmoronar, como o SOA?
Antes que me ataque, faça uma pesquisa no google com o termo “SOA IS DEAD“, o material geralmente diz que o SOA, como framework com seus N padrões morre, mas individualmente, seus padrões sobrevivem e sobrevivem bem.
Bom, a discussão se prendeu à terminologia de Framework, onde a única visão limitada de muitos era considerar framework como biblioteca, mas enfim, um dito cujo me fez o questionamento sobre o que eu uso em meus projetos. Acho que é relevante discutir isso e esse post segue exatamente com essa temática. O que eu uso nos meus projetos?! Achei pertinente, respondi para o rapaz que me questionou, mas em PVT e resolvi colocar aqui no blog para poder ajudar no entendimento da forma como trabalho.
Então, o que eu uso?
Embora o título pareça uma menção a uma canção do Barão Vermelho, a intenção é mostrar o que uso nos meus projetos!
Estrutura
Bom, primeiro vamos falar da solução. A solução geralmente contem no mínimo 2 projetos: Services e Contracts.
- Projeto Services
- Business
- Serviços (exemplo)
- Data
- Data Process ou DAL ou DALC (exemplo)
- Data Mappings (exemplo)
- Business
- Projeto Contracts (normalmente chamado de Domain)
- API
- Contratos dos Serviços (exemplo)
- Mensagens dos Serviços (exemplo)
- Business
- Entidades geradas a partir do banco (exemplo)
- Exceptions
- API
O projeto Services contém toda a regra de negócio, é segmentado em Serviços, Business Process, Data Process e Mappings, obviamente isolados por namespaces. Para acesso a dados, uso NHibernate, com FluentNhibernate. Uso geralmente DatabaseFirst, detalho os motivos na sessão argumentos. O código de mapeamento, as entidades e os DAL’s são gerados pelo gerador de código do Oragon Architecture, também detalho depois. Entidades e os contratos dos serviços ficam no projeto Contracts, assim como Mensagens de serviços e as interfaces dos serviços. Todos os serviços precisam de interfaces.
AOP
Diferente dos projetos comuns, onde você vê uma série de códigos não funcionais misturados com os comportamentos de negócio, nos projetos que construo com essa arquitetura você não vai encontrar:
- Tratamento de Exceção e Logging
- Manipulação e gerenciamento de Filas
- Abertura de Transações e Sessões
- Necessidade de manipulação da arquitetura, nos códigos de negócio.
O resultado é um código clean. Somente com o código de negócio, sem a necessidade de manipulação de uma série de questões arquiteturais. Mas todos os itens citados existem, mas são abstraídos pela arquitetura. Eles estão nos aspectos que são implementados no Oragon Architecture. Isso facilita o desenvolvimento e a gestão da solução.
Mas como isso funciona? AOP! Quando você usa AOP, você geralmente define quais classes podem ou não ter aspectos, você pode definir, classe-a-classe ou usando padrões e convenções. Eu uso padrões, que definem que toda a camada Services, possui AOP ativo. Ter AOP ativo garante que a infraestrutura do Spring.Net vai criar um proxy para você, nas classes que atenderem à regra. Exceto o aspecto de Exception Handling que usa configurações opcionais, todos os demais aspectos precisam necessariamente de atributos nos respectivos métodos para executarem suas tarefas. Assim, mesmo que a infraestrutura esteja ativa, você precisa marcar cada método da camada mais elevada, que no meu caso são os services, com atributos. Abaixo eu mostro um exemplo bem interessante.
Esses aspectos, fazem o trabalho sujo de garantir todos esses tratamentos listados acima. Isso elimina drasticamente a necessidade de programar tratamentos arquiteturais em camadas de negócio, sujando seu código.
Se você olhou os exemplos, deve ter tido curiosidade para entender o que significam os atributos no método Ingest da classe IngestionServices:
namespace xxxxxxx.Enterprise.Services.Ingestion { public class IngestionService : IIngestionService { ... [MongoDBContext("MongoBackOffice")] [RedisContext("BackOfficeCache")] [NHContext(contextKey: "MetaContext", creationStrategy: NHContextCreationStrategy.CreateNew, transactionMode: NHContextTransactionMode.Transactioned)] [NHContext(contextKey: "ImportacaoContext", creationStrategy: NHContextCreationStrategy.CreateNew, transactionMode: NHContextTransactionMode.None)] public void Ingest(PackageMessage message) { ... } ... }
Como disse, os aspectos que faço, geralmente dependem de atributos, exceto o de Exception Handling. Esses aspectos analisam a existência desses atributos para realizar operações de infraestrutura de aplicação, como a abertura de transações e sessões do NHibernate, MongoDB e Redis, além de nativamente serem programados para estarem em contextos ThreadSafe.
Toda a infraestrutura do Oragon Architecture colabora para o desenvolvimento bem desacoplado, baseado em diretivas de marcação que apontam para configurações, essas diretivas são os atributos que usamos. O resultado imediato é que: Durante a execução do método que possui o aspecto, seu contexto está ativo, garantindo todos os recursos e gerenciamento centralizado. Na medida que o método acaba sua execução ou mesmo lança uma exceção, a infraestrutura de aspectos cuida do tratamento adequado, evitando leaks de memória em todos os aspectos. O último aspecto,de exception handling, cuida do último tratamento que pode ser logging ou simplesmente ignorar exceptions (para ignorar exceptions é necessário usar um atributo específico).
Service Abstractions
O mais interessante dessa arquitetura, é a flexibilidade: Serviço A, depende do Serviço B. Podemos trabalhar essa relação, apenas com configurações possibilitando as mais variadas formas de deploy dessa associação.
Associação direta local (referência direta)
- Comunicação Via WCF, Remoting, Enterprise Services ou WebServices
- Comunicação via RabbitMQ (com callback)
- Workflow baseado em pipeline de filas
Essa flexibilidade permite um desenvolvimento completamente independente do deploy. E quando você está em um processo evolutivo, ter flexibilidade te dá segurança e confiança de que é possível, escalar e horizontalizar suas aplicações e serviços com pouco esforço. Hoje nesses desenhos, o esforço é de configuração.
Host Process
Outra parte relevante da arquitetura é o modelo de host de processos. Bom, se você já torceu o nariz, com o que leu até aqui, se prepare, será mais doloroso ainda. Se gostou, é possível que torça o nariz agora. Mas não esquente, preste atenção pois esse modelo já está se tornando bem mais comum do que você imagina, e em breve será padrão.
Como venho trabalhando há anos construindo serviços há anos, optei, e já faz alguns anos a não hospedar mais meus WCF’s no IIS. Pelo menos em 2011 eu já não fazia mais isso, por diversos motivos, que não vem ao caso. Com um utilitário do Visual Studio, o WcfSvcHost.exe, percebi que era possível fazer isso programaticamente então estudei a respeito e comecei a usar hosts baseados em Windows Services. A gestão ficou excepcional. O ruim desse modelo é a necessidade de criar Service Based Applications, cada vez que tinha a necessidade de segmentar o deploy e isso foi começando a mexer comigo, pois era o mesmo código, replicado. Em 2011 eu fiz o primeiro host de processos do Oragon Architecture. Ele tinha embarcado algoritmos para hospedagem via Windows Services e Console, permitindo que seja desenvolvido, debugado e testado via console, enquanto o deploy, em produção usava a infraestrutura de Windows Services. Pesquisei um pouco mais e encontrei o TopShelf que me permitiu eliminar as abstrações para deploy via windows services, e ganhei diversas features com essa escolha.
O modelo citado é o modelo atual, que será substituído em breve (você poderá encontrar nas tags Oragon Architecture Application Hosting e Oragon Architecture Application Server). O modelo acima permite que um único executável, possa ser copiado e com base em 2 XMLs de configuração, hospede qualquer serviço dessa infraestrutura que citei para vocês ao longo do post. O primeiro XML refere-se ao deploy do executável como um Windows Services, o outro XML é seu arquivo .config que contém todo o setup do AppDomain do EntryPoint, incluindo as referências para o Spring.Net e suas dependências.
Argumentação
Vamos aos motivos de cada coisa:
Por que Database First?
O primeiro ponto é a dificuldade de encontrarmos um modelo eficiente de gestão compartilhada para Code First.
O segundo ponto está relacionado a sempre encontrar cenários onde o banco já está modelado, ou 60% dos bancos que vou usar já estão modelados. Opto por não ter duas formas de fazer a mesma coisa. Geralmente quem precisa de Database First, são aqueles que esbarram em bancos de dados de gestão de DBAs ou sem gestão alguma, onde todo mundo mete a mão a qualquer hora. E quando há muito achismo sobre o que afeta ou não afeta alguma aplicação. O dia-a-dia de empresas grandes não é tão mais difícil que o dia-a-dia de empresas pequenas, nada é fácil.
A angústia dos modelos de classes tortos, gerados a partir do banco, não são problema para o Gerador de Código do Oragon Architecture. Nesse exemplo abaixo, de 2011, tínhamos um impasse com os DBAs. Queríamos uma tabela com o nome Grupo, mas por se tratar de controle de acesso, não conseguimos a aprovação do nome que fazia sentido para o negócio. Os DBA`s escolheram um nome, e nós, outro. Abaixo temos o resultado do mapeamento. Olhe as relações entre as colunas e as entidades, e lembre-se. Esse é um mapeamento automático! Sem nenhuma intervenção humana.
public partial class GrupoMapper : ClassMap<Grupo> { partial void CompleteMappings(); public GrupoMapper() { Table("[GrupoAcesso]"); Id(it => it.CodGrupo, "CodGrupoAcesso").GeneratedBy.Identity(); HasManyToMany(x => x.Acoes) .ParentKeyColumns.Add("[CodGrupoAcesso]") .Table("[PermissaoGrupoAcesso]") .ChildKeyColumns.Add("[CodAcaoSistema]"); HasMany(x => x.ProfissionaisGrupo) .KeyColumns.Add("[CodGrupoAcesso]") .ForeignKeyConstraintName("FK_PRGA_GRAC"); Map(it => it.Nome, "NomGrupoAcesso").Not.Nullable().CustomSqlType("varchar(100)").Length(100); Map(it => it.GrupoAdministrativo, "FlgGrupoAdministrativo").Not.Nullable().CustomSqlType("bit"); this.CompleteMappings(); } } public partial class Grupo { public virtual IList<Acao> Acoes { get; set; } public virtual IList<ProfissionalGrupo> ProfissionaisGrupo { get; set; } public virtual short CodGrupo { get; set; } public virtual string Nome { get; set; } public virtual bool GrupoAdministrativo { get; set; } }
Dos projetos que compõem o Oragon Architecture, o gerador de código é o que se mantém mais estável. Diferente de todos os modelos de geração de código que conheço, e uso geração de código pelo menos desde 2005, o nosso gerador de código usa dois níveis de meta-modelos, e o segundo nível garante uma redução drástica na complexidade da geração de código. Esse meta-modelo possui representações para entidades, propriedades e relações, suportando NxN, 1xN, Nx1, e propriedades comuns. Esse modelo oferece suporte a plugins de resolução de nomenclaturas dos mais variados tipos:
- Plugin para resolução de convenções de nomenclatura de entidades e propriedades
- Plugins prontos para pluralização idiomática com regras para portugês brasileiro e inglês americano.
- Plugin baseado em dicionário de cacófatos para resolução de problemas de case em bases geradas sem utilização de pascal case ou cammel case.
- Plugin para replace de nomes de Entidades, Relações e Propriedades.
- Você pode construir plugins!
Todos esses plugins afetam o 2° meta-modelo, o modelo de entidades. Esse é o diferencial do projeto. Há mais de dez anos, vejo templates baseados em tabelas, gerando classes diretamente a partir de tabelas, obrigando quem constrói os templates, reimplementar diversas vezes as mesmas regras,para gerar um código diferente. O impacto é que os templates naturalmente são densos e complexos, cheios de regras. Aqui, mostro um template, o responsável pelo mapeamento do FluentNHibernate, com suas míseras 288 linhas de código, onde pelo menos 30 delas são de setup do template e nunca mudam.
Esse modelo de geração de código, quanto ao OO só perde a herança. Mas os templates são externos e você poderia fazer um template que suportasse herança. Que tal?
A geração de código não tem nenhuma relação com a arquitetura, embora esteja no mesmo projeto. E sempre está ao meu alcance.
Para atender a um amigo que estava com um projeto atrasado por conta de uma demanda contratual, criei novos templates que suportaram mais de 200 CRUD’s completos para um projeto do Itaú. Sua equipe só teve o trabalho de customizar o código gerado, o que não é uma boa prática. Nessa empreitada, a geração de código se estendeu à UI gerando controlers e views para todos os cruds, enfim toda a cadeia, o resultado eram páginas funcionais.
Ageração de código me permitiu, em 2011, remover toda a camada de acesso a dados de um grande sistema de análise financeira de uma consultoria do Rio de Janeiro. Todo o mapeamento era baseado em HBMs que estavam gerando um consumo excessivo de banco. Toda a troca foi feita com base no gerador de código, implantando também os templates de geração de código com suporta a AOP. Reduzimos o custo de refatoração do projeto de 6 meses para 8 semanas.
Por que não OO, porque não DDD?
Bom, você já viu a quantidade de facilitadores que possuo. Abandoná-los não é algo fácil porque ganho muito tempo em desenvolvimento, com esses mecanismos e frameworks. Embora seja possível aplicar mais OO e DDD, ainda não estou certo se efetivamente ganho produtividade se comparado ao modelo que uso. Estou certo de que não perco o modelo atual, não peca em qualidade. Esse modelo é bem escalável, verticalmente e horizontalmente, a gosto do freguês. Do ponto de vista da organização do negócio, ela está clara nos serviços e processos de negócio.
Por que o Spring.Net?
Antes de mais nada, o Spring.Net é um excelente projeto, visionário em suas abstrações e muito robusto. Embora eu não use todas as features do projeto, seus mecanismos de IoC, DI e AOP são excelentes. Do ponto de vista de ser visionário, o pacote Services, serviu de inspiração para o WCF da Microsoft, que em 2006, já contava com total abstração de depoy, e pasme! Sem a necessidade dos infernais atributos nas entidades e contratos.
Outro ponto relevante do Spring.Net é o fato de não usar código para o mapeamento da configuração. Embora seja mais chato de trabalhar e configurar, a flexibilidade é absurda. Ele impõe o foco na inversão de controle que dificilmente encontramos e outros containers. Diferente da maioria das abordagens que se dizem fazendo DI/IoC, mas na verdade usam meramente o anti-pattern Service Locator. O Spring.Net ajuda no seu design, pois como usa poucas convenções, permite que você desenha classes altamente reaproveitáveis, e com configurações mais completas, permitindo que sejam injetadas com configurações diferentes em locais diferentes, maximizando o reaproveitamento e oferecendo uma rica gama de possibilidades.
Mais um ponto relevante para o Spring.Net é favorecer concretamente a inversão de controle, ao ponto de ser possível injetar abstrações inimagináveis. Como por exemplo, linkar via configuração um método de negócio a uma fila do RabbitMQ. Basta você definir, na configuração do Spring.Net, um listener para o endpoint e a fila desejada, associando à instância/método que processará as mensagens. Seu método agora recebe mensagens oriundas de uma fila, da mesma forma como uma aplicação UI poderia chamá-lo. E os dois cenários podem coexistir pois seus métodos não são dependentes do modelo de deploy, apenas realizam suas tarefas, enquanto a infraestrutura cuida do papel de orquestrar essas chamadas.
Por que AOP?
Embora AOP tenha entrado na moda em 2012, uso desde 2006. O principal ponto do AOP é viabilizar abstrações de forma segura, reduzindo a necessidade de implementação de código repetitivo. Você consegue ter total foco no negócio, sem abdicar dos controles e tratamentos relevantes a um código estável e seguro, você apenas centraliza e move essas implementações, deixando o código mais limpo e coeso.
Por que FluentNHibernate?
De fato eu tenho de mudar e usar o code mappings do NH. Mas não parei para rever isso ainda. Na versão 7 do Oragon Architecture, a dependência com FluentNHibernate é opcional, mas continua sendo a única implementação para mappings. Está no roadmap a construção da infraestrutura para code mappings, mas ainda não há prazo.
Por que NHibernate e não EntityFramework?
Orabolas, toda essa infraestrutura foi evoluindo e é estável antes mesmo do lançamento do Entity Framework. As primeiras versões do Entity Framework não convenceram, embora o Oragon já tenha dado suporte ao Entity Framework nas primeiras releases dele. De qualquer forma está no roadmap construir as abstrações para o Entity Framework também. Estou certo de que com algum tempo a Microsoft possa ter efetivamente acertado com o Entity Framework, no entanto ainda não dei foco nesse desenvolvimento, que sabe você não faz um pedido de feature e eu atendo?
Por que não citou UI?
Simples, não tenho feito nada de UI, embora admita que sou apaixonado pelo ExtJS. Mas é um caso a se repensar sempre. Minha última experiência foi positiva do ponto de vista do negócio, da entrega de comportamento e funcionalidade. Mas do ponto de vista da equipe, foi um inferno. Talvez eu usasse mais, em projetos pessoais, mas de fato a UI do ExtJS já está se tornando antiquada.
Bom, por enquanto é só pe-pe-pessoal!
0 comentários