Vamos a quarta parte do artigo de arquitetura para desenvolvimento em camadas, para quem ainda não viu os artigos anteriores desta série, veja os links abaixo:
- Implementando a camada de modelo – Model
- Implementando a camada DAO
- Implementando uma arquitetura em camadas
Pode dar uma lida também nesse post de referência:
Uma visão sobre arquitetura de softwares.
Conforme falamos nos artigos anteriores, vamos mais uma vez apresentar de forma prática a implementação de uma metodologia de desenvolvimento de softwares em camada.
Estamos estudando agora a camada de controle, essa camada é muito importante para o desenvolvimento da aplicação, já que é ela que conversa proximamente com a camada de visualização. Lembrando que temos ainda uma camada de ajuda, chamada de ViewHelper, mas esta camada serve para implementação de lógica e não de ponte para a camada de controle. Resumindo, podemos ter casos onde utilizamos a camada de helper antes de chamar o controller, mas pode ter momentos que não precisamos utilizar um ViewHelper.
Bom, a camada de controle é muito importante por ter a responsabilidade de controlar as solicitações de recursos lógicos das camadas de dados, através da camada de modelo é claro.
Mas como as outras camadas, essa camada também não é somente uma ponte, se fosse para ser uma ponte, não havia sentido em ter uma camada a mais na aplicação. Como a camada de modelo, não é responsável por manipulação de dados do cliente, tratamentos, validações básicas. O Controller assume esse papel, em métodos que recebem DTOs, você irá notar que o Controller é a única camada que não receberá dados nesse formato, e sim uma coleção que populará um DTO para ser passado para as demais camadas. Isso é trabalho para a camada de controle, controlar quais dados chegará a camada de modelo.
Tratamentos com resultados, devem ser implementados dentro da camada de controle, um exemplo seria a necessidade de um array contendo dados diferentes de uma mesma tabela, talvez para gerar uma estatística ou um relatório dos mesmos dados, detalhados de fora diferente. Podem ser consultados e separados na camada de controle, gerando um array contendo os dos resultados diferentes, sem ter que fazer essa separação em outra camada.
Na teoria, essa divisão de responsabilidade é determinada por um bom senso. Quando estamos implementando uma classe mais específica, por exemplo uma classe chamada Pessoa, onde temos explicitamente definidas todas as suas responsabilidades.
Então tenha calma e pense bem antes de começar a implementar suas lógicas em qualquer lugar, conversar com a equipe de desenvolvimento e até mesmo consultar a equipe de arquitetura pode ajuda e evitar de implementações de difícil entendimento.
Agora que falamos bem sobre o que é e para que serve uma camada de controle, vamos a implementação da mesma de forma que se integre com as demais camadas que já implementamos nos artigos anteriores.
Vamos começar implementando a classe base para a camada controle.
ControllerBase.cfc
<cfcomponent> <!--- * Função: Construtor * @name init * @return void ---> <cffunction name="init" access="package" returntype="void"> <cfscript> this.instance = Application.instance.Factory; </cfscript> </cffunction> </cfcomponent>
Usuarios.cfc
<cfcomponent displayname="Usuarios" hint="Objeto Controller de Usuarios" output="false" extends="ControllerBase"> <!--- * Função: Construtor * @name init * @return void ---> <cffunction name="init" access="public" output="false" returntype="Usuarios"> <cfscript> super.init(); this.model = this.instance.getModel("Usuarios"); this.dto = this.instance.getDTO("Usuarios"); return this; </cfscript> </cffunction> <!--- * Função: lista os dados * @name listar * @return Query ---> <cffunction name="listar" access="public" output="false" returntype="Query"> <cfargument name="id" type="numeric" required="false" default="0" /> <cfscript> return this.model.listar( id : arguments.id ); </cfscript> </cffunction> <!--- * Função: salva os dados * @name salvar * @return void ---> <cffunction name="salvar" access="public" output="false" returntype="string"> <cfargument name="dados" type="Any" required="true" /> <cfscript> var retorno = ""; if( isQuery( "arguments.dados" ) == true OR StructKeyExists( arguments.dados , "fieldnames" == true ) ) { retorno = this.dto.popular( argumentsCollection : dados ); if( Len( retorno ) == 0 ) { try{ this.model.salvar( dto : this.dto ); }catch(e){ return e.message; } } } return retorno; </cfscript> </cffunction> <!--- * Função: deleta os dados * @name delete * @return void ---> <cffunction name="delete" access="public" output="false" returntype="void"> <cfargument name="id" type="numeric" required="true" /> <cfscript> try{ this.model.delete( id : arguments.id ); }catch(e){ throw(e.message); } </cfscript> </cffunction> <!--- * Função: autenticar usuários * @name autenticar * @return query ---> <cffunction name="autenticar" access="public" output="false" returntype="Void"> <cfargument name="dados" type="Any" displayname="dados" required="true" default="" /> <cfscript> var retorno = ""; if( isQuery( "arguments.dados" ) == true OR StructKeyExists( arguments.dados , "fieldnames" == true ) ) { retorno = this.dto.popular( argumentsCollection : dados ); if( Len( retorno ) == 0 ) { try{ this.dto.popular( this.model.autenticar( usuario : this.dto ) ); SESSION['UsuarioLogado'] = this.dto; }catch(e){ throw(e.message); } } } </cfscript> </cffunction> <!--- * Função: ativar usuários * @name ativarUsuario * @return boolean ---> <cffunction name="ativarUsuario" access="public" output="false" returntype="boolean"> <cfargument name="dados" type="Any" displayname="dados" required="true" /> <cfscript> var retorno = ""; if( isQuery( "arguments.dados" ) == true OR StructKeyExists( arguments.dados , "fieldnames" == true ) ) { retorno = this.dto.popular( argumentsCollection : dados ); if( Len( retorno ) == 0 ) { try{ return this.model.ativarUsuario( usuario : this.dto ); }catch(e){ throw(e.message); } } } </cfscript> </cffunction> <!--- * Função: listar usuários * @name listarUsuarios * @return Query ---> <cffunction name="listarUsuarios" access="public" output="false" returntype="query"> <cfargument name="dados" type="any" displayname="dados" required="false" default="0" /> <cfargument name="ativos" type="boolean" displayname="ativos" required="false" default="true" /> <cfargument name="inativos" type="boolean" displayname="inativos" required="false" default="true" /> <cfscript> var retorno = ""; var usuario = ""; if( isQuery( "arguments.dados" ) == true OR StructKeyExists( arguments.dados , "fieldnames" == true ) ) { retorno = this.dto.popular( argumentsCollection : dados ); if( Len( retorno ) == 0 ) { try{ usuario = this.dto; }catch(e){ usuario = arguments.dados; } } } return this.model.listarUsuarios( usuario : usuario , ativos : arguments.ativos , inativos : arguments.inativos ); </cfscript> </cffunction> </cfcomponent>
Como estamos vendo acima, implementamos de forma bem simplificada a classe de controle Usuarios, fiz validações básicas e tratamentos básicos para não alongar muito esses exemplos.
Dessa forma, você consegue criar o seu padrão de controller e implementar mais uma camada da sua arquitetura.
Gostaria de lembrar, que minha ideia com esses artigos não é dizer qual padrão é certo ou errado, e sim dar uma base para que desenvolvedores que não estão acostumados a trabalhar com arquitetura, possam criar seus próprios padrões e criar aplicações bem estruturadas e de fácil manutenção.
Fico por aqui, na próxima parte desse artigo, chegamos ao final do artigo e falaremos sobre ViewHelper.
Até a próxima.
Paulo Teixeira
Post Original