Novo projetinho open-source

novembro 2, 2009

Acabo de realizar os primeiros commits no meu novo projeto open-source, chamado Baradur (link aqui). O projeto consiste em um visualizador de grafo de cena para aplicações JavaFX (versão 1.2.1 que é a mais recente no momento em que escrevo este post). O Objetivo a ferramenta é fazer com que o desenvolvedor JavaFX possa visualizar, em tempo de execução, a estrutura do grafo de cena da sua aplicação. Com a ferramenta também é possível exibir na própria cena, a bounding box de cada nodo. Isso pode ser bastante útil quando existem nós opacos na cena. Outra caracteristica bacana da ferramenta é que pode-se visualizar também todo o estado do nodo selecionado na TreeView do grafo de cena. Futuramente pretendo possibilitar ao usuário que altere o valor dos atributos do nó selecionado, porém atualmente os valores são apenas exibidos ao usuário.

Espero que gostem do projeto. Abraços

Integração fácil entre Java e Twitter

julho 24, 2009

Um post rápido hoje, apenas para ter registrado.

Fazendo uma pesquisa rápida no gigante das buscas (Google, é obvio) encontrei uma API muito simples, mas eficiente, para realizar integração de Java com o nosso famoso Twitter. A Java-Twitter trata-se de uma DSL super legível que faz uso do webservice do próprio Twitter, nos livrando do trabalho baixo nivel.

Para utilizá-la é bem simples. Podemos adicioná-la como uma dependência do Maven ou baixar diretamente o seu arquivo JAR. No quadro abaixo consta um exemplo de utilização da API.

Api api = Api.builder().build();
for (Status status : api.publicTimeline().build().get()) {
 System.out.println(String.format("%s wrote '%s'", status.getUser().getName(), status.getText()));
}

No exemplo acima é mostrado como listar os últimos tweets postados pelos usuários. No quadro abaixo está sendo exibido como enviar um novo tweet.

Api api = Api.builder().username("username").password("password").build();
api.updateStatus("This is a test message.").build().post();

Mais informações podem ser obtidas no próprio site do projeto.

Up

julho 18, 2009

Estou a muito tempo sem atualizar o blog. Não me falta vontade, mas sim conteúdo para escrever. A uns dois meses, na empresa onde atualmente trabalho, entrei em um projeto de implantação de um sistema ERP. Esse projeto envolve revisar todos os sistemas internos (desenvolvidos por nós em Oracle Forms&Reports) a fim de “saber o que fica e o que sai” e realizar as alterações e integrações. Acontece que desde o momento que me envolvi com este projeto, não tive mais tempo de desenvolver em Java, parei de ler notícias, etc. Isso me deixou um pouco enferrujado, mas agora a coisa vai voltar ao normal. Essa semana já começei a me inteirar das novidades, e estou bem feliz com o que andei lendo. Então posso garantir que conteúdo aqui para o blog é o que não vai faltar daqui a um tempo.

Aproveitando o post de hoje, vou comentar sobre a FurbSpeech API. Neste final de semana, pretendo implementar uma nova e importante feature para prover geração de arquivo de audio em MP3, atualmente só estou gerando arquivos raw, au, wav, aiff. Essa alteração surgiu da necessidade de uma pessoa que entrou em contato comigo por e-mail, portanto, agradeço o interesse pela API.

[]s

Hibernate: “could not load an entity” causado por SQLException: “Stream já foi fechado”

abril 23, 2009

Hoje, ao colocar uma aplicação que desenvolvi no ambiente de produção (JBoss), deparei-me com uma exceção nada interessante, stack trace abaixo:

DefaultLoadEventListener - Error performing load command <org.hibernate.exception.GenericJDBCException: could not load an entity: &#91;br.furb.ctes.domain.model.EntradaEsterilizacao#32768&#93;>org.hibernate.exception.GenericJDBCException: could not load an entity: [br.furb.ctes.domain.model.EntradaEsterilizacao#32768]
	at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
	at org.hibernate.loader.Loader.loadEntity(Loader.java:1874)
	at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
	at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
	at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3044)
	at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
	at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
	at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
	at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:195)
	at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
	at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
	at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815)
	at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808)
	at br.furb.infra.persistencia.generic.DaoGenerico.retornaById(DaoGenerico.java:58)
	at br.furb.ctes.app.EntradaEsterilizacaoLogic.etiqueta(EntradaEsterilizacaoLogic.java:95)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
Caused by: java.sql.SQLException: Stream já foi fechado
	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)
	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)
	at oracle.jdbc.driver.LongAccessor.getCharacterStream(LongAccessor.java:340)
	at oracle.jdbc.driver.OracleResultSetImpl.getCharacterStream(OracleResultSetImpl.java:1519)
	at oracle.jdbc.driver.OracleResultSet.getCharacterStream(OracleResultSet.java:2007)
	at org.jboss.resource.adapter.jdbc.WrappedResultSet.getCharacterStream(WrappedResultSet.java:484)
	at org.hibernate.type.StringClobType.nullSafeGet(StringClobType.java:39)
	at org.hibernate.type.CustomType.nullSafeGet(CustomType.java:105)
	at org.hibernate.type.AbstractType.hydrate(AbstractType.java:81)
	at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2096)
	at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1380)
	at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1308)
	at org.hibernate.loader.Loader.getRow(Loader.java:1206)
	at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:580)
	at org.hibernate.loader.Loader.doQuery(Loader.java:701)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
	at org.hibernate.loader.Loader.loadEntity(Loader.java:1860)
	at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
	at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
	at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3044)
	at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
	at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
	at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
	at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:195)
	at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
	at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
	at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815)
	at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808)
	at br.furb.infra.persistencia.generic.DaoGenerico.retornaById(DaoGenerico.java:58)
	at br.furb.ctes.app.EntradaEsterilizacaoLogic.etiqueta(EntradaEsterilizacaoLogic.java:95)

Apesar de eu achar extramamente improvável, verifiquei meu código para garantir que a sessão do Hibernate estava aberta no momento do session.get. Claro que esse não era o problema, pois eu uso um interceptador do VRaptor para abrir e fechar todos os recursos no momento certo.

No ambiente de desenvolvimento tudo funcionava corretamente. Dessa forma, notei que o problema estava no esquema da base de dados. Para gerar a base do desenvolvimento eu usei os próprios scripts gerados pelo Hibernate. Para a produção eu usei uma ferramenta case da Oracle que temos aqui na empresa. O problema estava nos campos de data, a ferramenta case os gerou como Date, mas no código Java eles estavam como Timestamp. Com alguns ALTER TABLE o problema foi solucionado.

Só para constar aqui no blog o problema. Se fossemos analisá-lo apenas pela exceção levantada seria dificil chegar no erro: “Stream já foi fechado“, em português ainda por cima. Mas essa exceção foi lançada pelo driver do Oracle, portanto, nada de julgar mal o Hibernate! 🙂

[]s

Struts 2 x VRaptor x … x JSF?

março 19, 2009

Mais um longo tempo sem postar aqui no blog. Não por falta de vontade, mas é que está dificil tirar um tempo para escrever.

Mas vamos ao que interessa, hoje vou falar um pouco sobre o meu primeiro contato com um dos mais famosos frameworks MVC para web: o Struts 2. Na empresa onde atualmente trabalho, estamos reestruturando praticamente tudo, desde a metodologia de trabalho até  questões de arquitetura e APIs utilizadas. Se tratando de MVC, alguns frameworks foram elencados para serem estudados, sendo eles: Struts 2 e JSF. Em meus projetos particulares sempre utilizei o VRaptor como framework MVC, considero-o extremamente estável e produtivo, principalmente por seguir a idéia de Convention over Configuration. Porém, como o VRaptor foi (e é) desenvolvido pelos membros da Caelum, muita gente afirma que não é muito seguro utilizá-lo, pois o mesmo pode deixar de ser mantido e ficar obsoleto. Eu particularmente não acredito nisso nem um pouco, o VRaptor é opensource e já vem sendo utilizado por diversas organizações, inclusive de grande porte. A comunidade é ativa através do fórum (em português) e listas de e-mail do SourceForge (em inglês). Entretanto essas justificativas não foram suficientes para a equipe do meu trabalho considerar o VRaptor como opção, portanto começamos a estudar o Struts 2 (nossa primeira opção).

Primeiramente foram levantadas algumas features que considero importante em qualquer framework MVC que se diz “moderno” (e que estão presentes no VRaptor 2 :P), são elas:

  • Configuração. Nenhuma, XML, Properties?
  • Passagem de parâmetros através de Injeção de Dependências
  • Interceptadores
  • Validação
  • View Redirection
  • Outjection
  • Ajax (retorno XML ou JSON)
  • Plugins?

Configuração: O mecanismo de configuração do Struts 2 segue a idéia do WebWork. O principal arquivo é o struts.xml, onde definimos interceptadores, result types, actions, etc. A forma básica de configurar uma nova action no Struts 2 continua sendo inscrevê-la no struts.xml. Porém agora temos a vantagem de poder definir um método como sendo uma action, sem a necessidade de ter somente o método publico execute na classe. No site do Struts 2 encontrei também um plugin chamado Convention, que diz permitir o uso de anotações para configurar as actions, descartando o struts.xml. Porém não obtive sucesso no uso dele, mesmo seguindo exatamento os passos do tutorial.

Passagem de Parâmetros: A passagem de parâmetros para as actions é bem elegante no Struts 2. Ocorre a injeção de dependências por Setter, ou seja, todos os atributos das actions que tiverem setter podem ser acessados. A conversão de tipos ocorre de forma automática, por exemplo no parâmetro ?pessoa.nome=Germano o método setNome do bean Pessoa é invocado, sendo necessário também existir um método setPessoa na classe da action. O Struts 2 permite também a implementação de conversores customizados específicos.

Interceptadores: Os interceptadores do Struts 2 seguem a mesma idéia dos outros frameworks. A vantagem do Struts 2, com relação aos interceptadores, é que podemos definir a ordem que são executados. Podemos definir a ordem inclusive dos interceptadores inclusos na API do Struts, ou seja, podemos indicar um interceptador implementado na nossa aplicação para ser executado antes dos interceptadores padrões do struts, por exemplo. O Struts 2 possui também o conceito de pilha de interceptadores (Stack Interceptors), onde podemos definir a ordem de execução e reutilizá-la na definição de várias actions. O Strus 2 nos prove duas pilhas padrões, a defaultStack e a basicStack. A primeira é comumente usada em actions de submição de formulários, provendo interceptadores para realizar validação, e outras tarefas comuns. Já a basicStack é mais usada em actions de visualização, que são mais simples.

Validação: Baseado no conceito de DRY (Don’t Repeat Yourself), inicialmente fui atrás de um plugin do Struts 2 para integrar com o Hibernate Validation, porém não encontrei nenhuma solução oficial. A forma básica e padrão de definir validação no Struts 2 é através de XML (oh que novidade!) no struts.xml. Porém ele prove também uma forma de especificar as regras de validação em anotações a nivel de métodos (nas actions). Caso ocorra algum erro de validação, o Struts retorna ‘input’ como result type da action, onde através de view redirection (próximo tópico) podemos encaminhar para o formulário com todos os parâmetros enviados a action.

View Redirection: No Struts 2, todo método de action retorna uma String. Essa informação é usada para determinar qual a ação deve ser tomada após o término da action. Normalmente o fluxo é passado à uma página JSP para que seja gerado o conteudo HTML para retornar ao browser. Porém podemos definir um redirecionamento como resultado da action. O Struts permite tanto o redirecionamento no cliente (browser fazer uma nova requisição) quanto no servidor (na mesma requisição, mantendo todos os parâmetros da mesma). Para o redirecionamento no cliente deve ser usado o result type “redirect”, indicando a nova action a ser executada. Essa action é enviada ao browser para que seja feita uma nova requisição. Para o redirecionamento no servidor deve ser usado o result type “chain”, também indicando o nome da action a ser executada.

Outjection: Todos os métodos get contidos na classe da action são usados para colocar os valores no escopo da requisição e serem usados na View (via Expression Language na página JSP por exemplo). O Struts 2 permite também que façamos outjection para os escopos de sessão e aplicação, porém não através dos métodos get. Para realizar essa tarefa devemos acessar o método estático getContext da classe ActionContext e colocar os valores diretamente dentro da sessão ou aplicação, que são objetos do tipo Map.

Ajax: O Struts 2 possui suporte completo a Ajax. Tanto no lado do controlador quanto no lado da View (através de taglibs). Nativamente ele prove um result type chamado “xlst” que é responsável por serializar todos os atributos outjected no formato XML no OutputStream. Além disso, existe um plugin chamado JSON que prove um result type chamado “json” onde os atributos outjected são serializados nesse formato. Usando esse tipo de formato, torna-se muito prático o uso de chamadas remotas via javascript, que consome JSON nativamente.

Plugins: A estrutura de plugins do Struts 2 é bem robusta e prática, para ativar um plugin basta colocar o respectivo .jar na pasta lib do projeto Web. Existem diversos plugins já implementados (pela comunidade) e que servem para propósitos variados.

De forma geral, o Struts 2 parece bem produtivo, principalmente se formos comparar com a versão 1 do Struts. Agora vamos começar o estudo do JSF e tentar bolar um comparativo de produtividade. Espero que tenhamos sucesso na escolha!

FurbSpeech open-source

janeiro 28, 2009

Primeiro post do ano aqui no blog e cá estou para dar uma boa notícia. A biblioteca FurbSpeech foi liberada como um projeto open-source. No momento encontra-se hospedada no code.google sob a licensa GNU GPL v3. Espero que venha a ser útil para outras pessoas, sendo que até o momento havia uma grande complexidade para realizar a técnica de text-to-speech na lingua portuguesa.

Ainda é necessário implementar mais testes da biblioteca, principalmente em ambientes Linux e Mac OS X. Mas conforme a biblioteca for crescendo vou notificando aqui no blog as melhorias e correções de possíveis bugs.

[]’s

Ansioso para meu primeiro Sprint Scrum…

dezembro 14, 2008

Após assistir a palestra (versão eletrônica) do Danilo Bardusco entitulada “Scrum na Globo.com: Derrubando Mitos” que ocorreu no Falando em Agile 2009 da Caelum e ler o livro “Scrum e XP direto das Trincheiras” de Henrik Kniberg, sinto-me altamente motivado a pensar em Agile pra valer, mesmo sem o apoio da maioria das pessoas com quem trabalho. Na palestra, Danilo comenta que um amigo dele, ao realizar uma entrevista de emprego para desenvolvimento com algum candidato, a primeira pessoa que ficaria sem chances de obter a vaga seria alguem que falasse algo como: “Eu gostaria de usar Agile, mas a empresa que trabalho não dá espaço…”. Danilo afirma que a idéia de usar Agile deve partir do desenvolvedor, mesmo que aos poucos. Achei tudo isso muito semelhante a minha situação, e me fez mudar!

Antes de começar a pensar Agile, estou estudando de fato o que tudo envolve. Compreender a essência o Manifesto Ágil em si. Essa semana baixei a versão PDF original do livro “Scrum e XP direto das Trincheiras” de Henrik Kniberg e já terminei de ler. Livro excelente, pois descreve o “como nós fazemos Scrum” (inclusive é o subtítulo do livro).

Com as informações da palestra e do primeiro livro de Agile lido, já estou me preparando para o primeiro sprint em um projeto novo que vou desenvolver na empresa onde trabalho. Quinta-feira dessa semana, com a grande ajuda do meu amigo João de Deus, fizemos um Task Board para registrar o andamento do sprint. Ficou muito bom, créditos ao João que além de ser desenvolvedor, tem a marcenaria como hobby. Também já elaborei e detalhei o Product Backlog. Como é o primeiro sprint e o PO ainda não conhece Scrum, eu mesmo adicionei as estórias no Product Backlog, mas claro vou deixar ele priorizar cada uma delas. Terça-feira pretendo realizar a reunião de planejamento do sprint junto com o PO e alinhar a idéia geral do Scrum.

Vamos que vamos, quando tiver iniciado o sprint e o quadro de tarefas estiver com uma certa “bagunça”, faço um novo relato.

[]’s

Text-to-speech (TTS)

dezembro 8, 2008

Recentemente, em um projeto de pesquisa aqui da Furb no qual participo, desenvolvemos uma API de conversão texto-fala (text-to-speech, ou somente TTS) para o idioma português.  Antes de iniciar o desenvolvimento, fizemos uma pesquisa para encontrar alguma solução de text-to-speech pronta para utilizar em uma aplicação Java. Encontramos alguns softwares de text-to-speech, porém isso não nos interessava, precisavamos de uma API e que tratasse o idioma português do Brasil. Nessa pesquisa, encontramos a JSR-113 – Java Speech API. Parecia ser o melhor caminho a seguir, porém verificamos que existiam poucas implementações dessa especificação freeware e opensource. Analisamos a implementação chamada de FreeTTS desenvolvida em Java, porém notamos uma complexidade exceciva ao montar um simples “Hello World”, sendo que também não tratava o idioma português.

Pensamos em uma API simples, prática e principalmente bem testada. Algo que pudessemos dizer: “Quero que você (a API) converta um texto T em fala.” e ela gerasse um arquivo de audio (.wav por exemplo) contendo a respectiva fala. Dessa forma, a implementação da nossa API (chamada de FurbSpeech) possibilita uma chamada assim:

File audio = new FurbSpeech().text("Rua antônio da veiga").to().speech();

O papel da FurbSpeech é gerar arquivos de texto contendo uma série de símbolos fonéticos, juntamente com sua frequência, duração e amplitude. Um exemplo de arquivos gerado pela nossa biblioteca pode ser visto a seguir:

_ 300
r 120 100 100.0
u 120 100 100.49480791850904
a 105 100 100.95885107720841
am 105 100 101.36327752004667
t 120 100 101.6829419696158
o 120 100 101.89796923871117
n 105 100 101.9949899732081
i 105 100 101.96797189374787
o 105 100 101.81859485365136
d 120 100 101.55614639377585
a 120 100 101.19694428820792
v 120 100 100.76332198410466
e 120 100 100.28224001611973
i 120 100 99.78360973093979
g 105 100 99.29843354462076
a 105 100 98.8568773625153
_ 300
_ 300
_ 300

Para transformar esse arquivos de fonemas em som propriamente dito, utilizamos um sintetizador chamado MBROLA. Ele recebe como entrada um arquivo de fonemas e gera um arquivo de audio no formato WAV.

Ainda não disponibilizamos o código-fonte da API para download, pois ainda não está completamente finalizada e testada devidamente. Porém, se houver interesse, fique a vontade para entrar em contato.

Até a próxima.

Um domínio mais limpo…

novembro 14, 2008

A abordagem DomainDrivenDesign (ou apenas DDD), dentre várias boas características, tem contribuido para resgatar os originais valores das linguagens orientadas a objetos. Porém, isso só é possível graças aos “novos” frameworks baseados em classes POJOs, onde não somos obrigados a extender classes que não conhecemos. Agora as classes são baseadas em metadados através de anotações no próprio código Java, provendo maior flexibilidade devido ao desacoplamento com classes de frameworks.
Dentre todas as classes que compõem um domínio que faz uso de um mecanismo de persistência como a JPA, destacam-se as classes de entidades (Entity). Essas classes, que devem ser um espelho (orientado a objetos) da estrutura das entidades do banco de dados relacional, podem conter métodos de negócio que alteram ou acessam informações da base de dados. Mas para que esse acesso à base de dados dentro das classes de entidades possa ser feito, é necessário que todos os seus atributos estejam mapeados, para que quando instâncias dessas classes forem obtidas através do contexto de persistência (EntityManager ou mesmo uma Session do Hibernate Core), seus atributos também estejam no estado gerenciado (managed state). Se as classes de entidades forem implementadas dessa forma, não é necessário poluirmos elas com acesso à repositórios (implementados como DAOs, por exemplo). A Listagem 1, exemplifica a implementação de uma classe de entidade com todos os atributos mapeados.

@Entity
public class Curso {
	@Id
	@GeneratedValue
	private Long id;

	@ManyToMany(cascade=CascadeType.ALL)
	@JoinTable(name="Matricula",
			joinColumns=@JoinColumn(name = "idCurso"),
			inverseJoinColumns=@JoinColumn(name = "idAluno"))
	private List<Pessoa> alunos;

	public Long getId() {
		return this.id;
	}

	/** retorna uma lista read-only */
	public List<Pessoa> getAlunos() {
		return Collections.unmodifiableList(this.alunos);
	}

	public boolean existeOuJaExistiuAluno(Pessoa p) {
		for(Pessoa aluno : this.alunos) {
			if (p.equals(aluno) { // por seguir o pattern Entity, compara pelo ID.
				return true;
			}
		}

		return false;
	}

	public void matriculaPessoa(Pessoa p) {
		this.alunos.add(p);
	}

	public void cancelaMatriculaAluno(Pessoa p) {
		this.alunos.remove(p);
	}
}

Nesta classe, é possivel notar que não a poluimos com getters e setters desnecessários. Para alterar o estado desse objeto, utilizamos os métodos de negócio nela contidos. A Listagem 2 mostra a implementação de um método que possui uma lista de pessoas e precisa verificar quais estão matriculadas em determinados cursos.

Setor s = repositorioSetor.getById(2L);
List<Pessoa> alunosDosCursosDoSetor = new ArrayList<Pessoa>();
for(Pessoa p : pessoas) {
	for (Curso c : s.getCursos()) {
		if (c.existeOuJaExistiuAluno(p) {
			alunosDosCursosDoSetor.add(p);
		}
	}
}

Esta implementação é muito mais OO do que a implementação mostrada na Listagem 3.

Setor s = repositorioSetor.getById(2L);
List<Pessoa> alunosDosCursosDoSetor = new ArrayList<Pessoa>();
for(Pessoa p : pessoas) {
	for (Curso c : s.getCursos()) {
		if (existeOuJaExistouAluno(c, p) {
			alunosDosCursosDoSetor.add(p);
		}
	}
}

O método existeOuJaExistiuAluno (que tem mais aparência de função do que de método) teria que acessar o repositório de curso ou de pessoa e fazer um select para verificar se a pessoa está ou já foi matriculada no curso em questão. Isso é procedural demais!!

A idéia de ter um modelo mais orientado a objetos é que podemos ter um código muito mais próximo do negócio a ser tratado, facilitando o entendimento do mesmo tanto pelos técnicos (desenvolvedores) quanto pelos especialistas do negócio.

Arquiteturas…

julho 31, 2008

Ao iniciar o desenvolvimento de qualquer software, uma das primeiras atividades desempenhadas pelos desenvolvedores (especialistas em computação) é a definição da arquitetura do mesmo. Essa tarefa custuma ocorrer logo após uma definição, ou melhor, do levantamento dos requisitos do negócio a ser tratado, sendo assim, o que for definido nesta etapa deverá ser considerado durante todo o tempo de vida do projeto. Acontece que essa não é uma atividade trivial. Com tantos padrões, técnicas, modas, conceitos mal entendidos, frameworks entre outros parâmetros, muitas vezes ficamos em dúvida do que de fato utilizar. Uma coisa é clara, desejamos que o código-fonte dos nossos softwares estejam cada vez mais próximo do domínio do problema, fazendo com que a comunicação entre desenvolvedores e especialistas do negócio utilize uma linguagem comum. Como uma tentativa de atingir esse nível de qualidade do código e ter uma maior aproximação dos envolvidos no projeto, o paradigma de Domain-Driven-Design (DDD) apresenta-se como uma abordagem extremamente útil e prática a ser considerada. Através do DDD, várias diretrizes podem ser seguidas para a elaboração de uma arquitetura transparente e mais próxima do domínio como desejamos. Essas diretrizes não são relacionadas apenas com o código-fonte em si, como é o caso do uso de determinados padrões de projeto, tal como o tão falado e confundido Repository. O DDD contempla também a elaboração de uma linguagem comum (chamada de Ubiquitous Language) a ser usada entre os envolvidos no projeto. Isso significa que os desenvolvedores devem nomear seus artefatos (classes, métodos, atributos, etc) de acordo com o negócio tratado. Isso também não quer dizer que os desenvolvedores não possam usar palavras do seu próprio jargão, por exemplo, o conceito de repositórios (pattern Repository) é comumente usado nessa comunicação, mas seu conceito deve estar bem claro para ambos os lados.
Outra discussão que segue é em relação às camadas da arquitetura. A tradicional dúvida, “Onde devo colocar as regras de negócio?” A tradicional separação em classes do tipo Business Object, Value Object, Data Transfer Object, Classes de controle, entre outras, nos perguntamos onde se encaixa a orientação a objetos, essa forma tradicional é muito estruturada. Esses dias conversando com um amigo no Google Talk ele me perguntou: “me diz onde eu deveria criar meu objeto ‘Notícias’ que vai puxar os dados no BD, usar uma DAO? e as validações, regras de negócio?”. Para responder a pergunta, abaixo consta um pequeno diagrama de classes envolvidas na solução.
A classe News é a classe de domínio. Nessa solução, o padrão Repository está sendo implementado como uma DAO, porém perceba que a classe Client (classe que exemplifica o uso dessa estrutura criada, poderia ser uma action do Struts ou lógica do VRaptor por exemplo) tem associação apenas com a interface NewsRepository, isso é importante para não termos um forte acoplamento com a implementação. A classe concreta NewsDao então implementa a interface NewsRepository para conter de fato a implementação da interação dessa entidade com o banco de dados. Perceba também que a classe News não possui nenhum método de negócio digamos, mas caso existisse algum método que alterasse o estado dos atributos este deveria ser criado nessa classe, e não em uma NewsBO. Quando temos uma classe que contém os métodos de negócio e os atributos dizemos que ela possui um alto nível de coesão, que é um ponto forte dessa implementação. Quanto a questão de validação do estado dos atributos, fazendo uso de um framework como o HibernateValidator (que inclusive serviu de inspiração para a criação da JSR-303 – Bean Validator) a tarefa torna-se simples e declarativa.

Para mais informações a respeito de DDD, peço que chequem os artigos contidos na InfoQ. Nesse espaço pode ser encontrado muito conteúdo bom sobre Java, arquitetutura, entre outros.

[]s