Arquivo da Categoria “domain driven design”

Tenho conversado com alguns amigos sobre os projetos que tenho participado no emprego novo e uma das dúvidas que surge é como utilizar repositórios em entidades, para tornar o domínio um pouco mais rico.

Obviamente, este POST não tem o intuito de levantar nenhuma discussão sobre como você deve tratar sua camada de persistência e o seu domínio, mas sim, mostrar como é possível injetar repositórios dentro de entidades de maneira transparente usando Spring.

Para saber mais a respeito do conceito por trás desta solução, leia este POST

O problema

Ao recuperar uma entidade (user, por exemplo) de um repositório, as dependências desta entidade devem ser injetadas manualmente dentro do método find.

Exemplo:

public User findById(Long id) {
    User user = entityManager.find(User.class, id);
    user.setUserRepository(this);
    user.setRoleRepository(...);
    user.setXXXRepository(...);
}

Você talvez queira injetar só um repositório, mas talvez não. Imagine, se ao invés de todos estes setters eu apenas usasse a annotation @ResolveDependencies no método e todas as dependências (no caso, repositórios) fossem injetadas automaticamente na entidade. Um outro problema é que a cada nova dependência (não que eu pretenda ter muitas), mais um set você tem que efetuar no método find e isso daria um pouco mais de trabalho. Além disso, muitas vezes seus repositórios já estão sendo gerenciados pelo container de injeção de dependências e você não gostaria de criar uma nova instância a cada find que efetuar.

A solução

A classe ApplicationContext do Spring possui recursos para resolver as dependências de um objeto não gerenciado pelo Spring. Desta forma, se a minha entidade User possuir um atributo chamado userRepository anotado pela annotation @Autowired (Spring) ou @Inject (JSR-330), através da chamada do método ctx.getAutowireCapableBeanFactory().autowireBean(user), todas as dependências serão injetadas. Onde, ctx é uma instância de ApplicationContext, user é uma instância de User e userRepository é uma instância de UserDAO gerenciada pelo Spring.

A seguir, as classes citadas:

UserRepository.java

public interface UserRepository {
    public User findById(Long id);
    public void save(User user);
}

UserDAO.java

@Named("userRepository")
public class UserDAO implements UserRepository {
 
    @Override
    @ResolveDependencies
    public User findById(Long id) {
        return new User(id);
    }
 
    @Override
    public void save(User user) {
        System.out.println("salvando user id=[" + user.getId() + "]");
    }
 
}

A classe UserDAO, foi anotada pela annotation @Named que faz parte da JSR-330 (Dependency Injection) suportada pelo Spring, o parâmetro passado (userRepository) é a String que identifica o DAO dentro do container de injeção de dependência (no caso, Spring). Toda classe anotada por esta anotação, será gerenciada pelo Spring.

O método save, simplemente imprime uma mensagem com o id do user.

O método findById, foi anotado pela a annotation @ResolveDependencies, que utilizaremos para indicar que a entidade retornada por este método deve ter todas as suas dependências resolvidas (injetadas).

A mágica

Como foi dito anteriormente, o Spring tem condições de resolver dependências de objetos não gerenciados pelo mesmo, desta forma, utilizaremos este recurso para o exemplo acima:

SpringUtils.java

public abstract class SpringUtils {
 
    public static void autowire(Object obj, ApplicationContext ctx) {
       ctx.getAutowireCapableBeanFactory().autowireBean(obj);
    }
 
}

Esta classe abstrata possui um método chamado autowire que, simplesmente, injeta as dependências (contidas no container, no caso, ApplicationContext) em um dado objeto (obj).

Para juntarmos tudo, utilizaremos AOP para interceptarmos os métodos anotados por @ResolveDependencies e, através da classe SpringUtils, resolvermos as dependências do objeto de retorno, conforme demostra a classe a seguir:

DetachedSpringBeanDependencyResolver.java

@Aspect
@Named
public class DetachedSpringBeanDependencyResolver {
 
    @Inject
    @Named("applicationContext")
    private ApplicationContext applicationContext;
 
    @AfterReturning(value="@annotation(br.com.submundojava.ResolveDependencies)", returning="retVal")
    public void resolve(Object retVal) throws Throwable {
 
        if(retVal != null)
            SpringUtils.autowire(retVal, applicationContext);
 
    }
 
}

Aqui complica um pouco, mas é bem simples:

@Aspect, sucintamente informa que esta classe utilizará AOP.

@Named, um objeto gerenciado pelo Spring.

@Inject + @Named(”applicationContext”), como esta classe também será gerenciada pelo Spring e no próprio Spring existe uma instância de ApplicationContext (obtida pelo nome applicationContext) podemos então injetar o applicationContext dentro do aspecto, pois o SpringUtils precisa do applicationContext para obter as instâncias que serão injetadas no objeto retornado.

@AfterReturning, indica que após o retorno do método anotado pela annotation @ResolveDependencies e antes de retornar ao cliente, o aplicativo deverá passar pelo método resolve.

Método resolve(retVal), acho que fica óbvio que retVal é o valor retornado pelo método anotado por @ResolveDependencies. No caso o retorno é uma instância de User.

SpringUtils.autowire, resolve todas as dependências contidas em applicationContext, do objeto retornado.

Para finalizar, a entidade User, a anotação @ResolveDependencies e o application-context.xml

User.java

public class User {
 
    private Long id;
 
    @Inject
    @Named("userRepository")
    private UserRepository userRepository;
 
    public User(Long id) {
        this.id = id;
    }
 
    public Long getId() {
        return id;
    }
 
    public void save() {
 
        if(userRepository == null)
            throw new IllegalStateException("userRepository == null");
 
        userRepository.save(this);
 
    }
 
}

ResolveDependencies.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ResolveDependencies {
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
    <!-- Procura por classes anotadas nos sub-pacotes do base-package -->
    <context:component-scan base-package="br.com.submundojava" />
 
    <!-- Ativa suporte a aspectos -->
    <aop:aspectj-autoproxy />
 
</beans>

Executando

public static void main(String argz[]) {
 
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:application-context.xml");
    UserRepository repository = ctx.getBean("userRepository", UserRepository.class);
    User user = repository.findById(1L);
    user.save();
 
}

A saída deverá ser algo parecido com:

salvando user id=[1]

Conclusão

O que tentei demonstrar aqui é uma maneira simples de resolver dependências de objetos que não fazem parte do container de injeção de dependências. Esta abordagem está sendo utilizada atualmente nos meus projetos e tem solucionado uma porção de problemas.

Para ter uma melhor visualização você pode abaixar o projeto aqui. Certifique-se de ter java 5+ e maven 2 instalados.

Dúvidas, críticas ou sugestões. Deixe um comentário.

Abraços.

Comments 5 comentários »

Na semana passada, Martin Fowler escreveu um artigo demonstrando as práticas que ele costuma utilizar ao trabalhar com expressões regulares. Neste artigo, o senhor Fowler propõe uma maneira de separar uma expressão regular em partes menores, recompondo tal expressão posteriormente para, entre outra coisas, dar maior legibilidade ao código. Ao acabar de ler o artigo a primeira coisa que veio em minha cabeça foi Fluent Interfaces. Na atualização de seu artigo, Martin Fowler cita este assunto (inclusive expôe sua preferência por não utilizar Fluent Interfaces) e sugere uma alternativa fluente criada por Joshua Flanagan para C#.

Este post não tem como  objetivo discutir se utilizar Fluent Interfaces, ou não, é a melhor maneira de resolver problemas relacionados a expressões regulares, mas sim, propor um exercício a fim de criar uma possível API fluente e, ao final do artigo, demonstrar uma pequena porção de código Java que pode solucionar alguns problemas relacionados a expressões regulares utilizando esta abordagem.

O problema

O grande problema de quando trabalhamos com expressões regulares é conhecer todos os recursos disponíveis (metacaracteres, quantificadores gananciosos, possessivos, relutantes, etc) para validarmos determinada String e posteriormente recuperarmos os grupos que nos interessa para aplicarmos a lógica que necessitamos. Além disso, quanto maior for a expressão mais difícil será de interpretá-la, por isso, utilizar a abordagem “Divisão e Conquista” é uma boa prática a ser seguida.

A solução

Criar uma API para abstrair os recursos (metacaracteres, quantificadores, etc) e disponibilizar ao usuário, métodos mais legíveis e simples de escrever. Para chegar a este fim, me propus a analisar as expressões regulares que mais utilizo no dia-a-dia, “mapeá-las” para o português estruturado e, então, escrever uma porção de código que reflita a interpretação do português estruturado o mais fluente possível.

Interpretando Expressões Regulares

Analisando uma expressão regular que tem como objetivo validar se Strings representam uma data em um formato dd/mm/aaaa (em java dd/MM/yyyy), temos:

Expressão Regular: \\d{2}/\\d{2}/\\d{4}

Português estruturado: A String em questão: Inicia com dois dígitos (numéricos), seguido de uma barra, seguido de dois dígitos (numéricos), seguido de uma barra e finaliza com quatro dígitos (númericos).

Código Java:

RegexComposer composer = RegexComposer.getInstance();
composer
    .startsWith(exactly(2, digit()))
    .followedBy(exactly(1, literal("/")))
    .followedBy(exactly(2, digit()))
    .followedBy(exactly(1, literal("/")))
    .finishedWith(exactly(4, digit()));

Parece bacana, mas não está ao contrário? Porque o quantificador vem antes do dígito no código Java e na expressão regular não? Pois ao interpretarmos a expressão regular em português estruturado, também passamos o quantificador a frente do dígito e é essa a diferença, pois, ao interpretarmos uma expressão como: \\d{2} nós não escrevemos “Esta expressão representa digítos (numéricos) dois“, e sim “Esta expressão representa dois dígitos (numéricos)”.

Este foi o meu principal questionamento quando analisei a API do senhor Joshua Flanagan, pois acredito que ao utilizarmos a abordagem fluente, devemos valorizar a maneira  como escreveríamos em uma língua (português/inglês) e não como é a sintaxe de uma determinada linguagem (de programação).

Delimitadores

Uma ocorrência constante (inclusive citado no artigo do Martin Fowler) é a utilização de delimitadores para separarmos os dados em Strings (tokens). São exemplos disto, os arquivos CSV (valores separados por vírgula) e também a data do exemplo anterior (separados por barra “/”). Desta forma, seria conveniente se pudessemos configurar a API para interpretar delimitadores. A seguir, demostro um exemplo de como isto poderia ser feito:

Expressão Regular: \\d{2}/\\d{2}/\\d{4}

Português estruturado: A String em questão: Inicia com dois dígitos (numéricos), seguido de dois dígitos (numéricos), finaliza com quatro dígitos (númericos) e é delimitada por uma barra.

Código Java:

RegexComposer composer = RegexComposer.getInstance();
composer
    .startsWith(exactly(2, digit()))
    .followedBy(exactly(2, digit()))
    .finishedWith(exactly(4, digit()))
    .delimitedBy(exactly(1, literal("/")));

Tá começando a melhorar, mas existe um recurso muito importante que deve ser considerado, os agrupamentos.

Agrupamentos

Os agrupamentos em Java são efetuados através inserção da expressão em parênteses, isto possibilita recuperar a String de determinado grupo uma vez que foi validado que a String se encontra no padrão requerido. A seguir demonstro uma maneira que, inicialmente, acreditei ser adequada para este fim.

Imagine que, após validar uma determinada data, você queira recuperar o dia, mês e ano separadamente para efetuar alguma lógica, então, teríamos algo como:

Expressão Regular: (\\d{2})/(\\d{2})/(\\d{4})

Português estruturado: A String em questão: Inicia com dois dígitos (numéricos) agrupados, seguido de dois dígitos (numéricos) agrupados, finaliza com quatro dígitos (númericos) agrupados e é delimitada por uma barra.

Código Java:

RegexComposer composer = RegexComposer.getInstance();

Pattern p = composer
                 .startsWith(exactly(2, digit()).grouped())
                 .followedBy(exactly(2, digit()).grouped())
                 .finishedWith(exactly(4, digit()).grouped())
                 .delimitedBy(exactly(1, literal("/")))
                 .compile();

Matcher matcher = p.matcher("05/01/1979");

if(matcher.matches()) {

    System.out.println("Dia: " + matcher.group(1));
    System.out.println("Mes: " + matcher.group(2));
    System.out.println("Ano: " + matcher.group(3));

} else {

     System.out.println("Formato invalido");

}

Legal, ao menos à primeira vista, entretanto eu ainda precisaria saber em qual grupo está o dia (matcher.group(1)), o mês (matcher.group(2)) e o ano (matcher.group(3)) e isto não é muito bacana. Creio que o ideal seria criarmos um alias para cada grupo e depois recuperarmos o número do grupo através do alias. Seria algo como .startsWith(exactly(2, digit()).grouped(usingAlias(”dia”))) e depois recuperarmos o grupo usando matcher.group(composer.getAliasGroup(”dia”)). Uma outra questão é que há outra interpretação para expressões agrupadas, por exemplo, “A String em questão: Inicia com um grupo de dois dígitos …”. Isto quer dizer que escreveriamos algo como: .startsWith(groupOf(exactly(2, digit())).usingAlias(”dia”)).

Embora eu considere estas opções adequadas, não escrevi nada disso ainda. No exemplo, a seguir, irei demonstrar um pouco mais de detalhes do que já implementei.

Exemplo prático

Utilizando o exemplo que o Martin Fowler deu em seu artigo, irei validar se a String “score 400 for 2 nights at Minas Tirith Airport” se encontra no padrão correto e ,após isto, recuperar os números 400 e 2.

Código Java:

import br.com.submundojava.regexcomposer.RegexComposer;
import static br.com.submundojava.regexcomposer.quantifier.Quantifiers.*;
import static br.com.submundojava.regexcomposer.value.MetaCharacters.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

/**
 *
 * @author Henrique Lima
 */
public class Test {

    public static void main(String argz[]) {

        RegexComposer composer = RegexComposer.getInstance();

        Pattern pattern = composer
                        .startsWith(exactly(1, literal("score")))
                        .followedBy(oneOrMore(digit()).grouped())
                        .followedBy(exactly(1, literal("for")))
                        .followedBy(oneOrMore(digit()).grouped())
                        .followedBy(exactly(1, literal("night")))
                        .followedBy(zeroOrOne(literal("s")))
                        .followedBy(exactly(1, literal("at")))
                        .delimitedBy(zeroOrMore(whiteSpace()))
                        .finishedWith(oneOrMore(any()))
                        .compile();

        Matcher matcher = pattern.matcher("score 400 for 2 nights at Minas Tirith Airport");

        if (matcher.matches()) {

            System.out.println(matcher.group(1));
            System.out.println(matcher.group(2));

        } else {
            System.out.println("Formato inválido");
        }

    }

}

Muito simples! Veja que não utilizamos nenhum metacaracter, nenhum quantificador, agrupamos os dados necessários e ainda utilizamos um delimitador. Uma pessoa nem precisaria conhecer de expressões regulares para validar a String (embora seja desejável).

Perceba que novas coisas aconteceram e utilizamos import static do Java 5 para disponibilizarmos os métodos necessários à nossa classe. Além disso, utilizamos novos métodos quantificadores (não regex) que são: zeroOrMore(), para zero ou mais ocorrências (análogo a *), oneOrMore(), para uma ou mais ocorrências (análogo a +), zeroOrOne() para zero ou uma ocorrência (análogo a ?) e por final utilizamos o método compile() para obtermos um objeto do tipo java.util.regex.Pattern para podermos validar a String.

Código fonte

Abaixo segue o projeto que criei para efetuar este post.

Para netbeans: download
Para eclipse: download

Considerações Finais

Procurei demonstrar uma maneira fluente de trabalhar com expressões regulares, abstraindo seus recursos e disponibilizando métodos de alto nível. O exemplo que escrevi ainda está muito “verde”, com nomes esquisitos para algumas classes, métodos e pacotes. Na verdade, em alguns casos, nem sei se o inglês está correto.

Além disso, existem muitas limitações, por exemplo, se eu quiser usar um delimitador e ao mesmo tempo validar um dos tokens com um outro RegexComposer, não é possível. Seria desejável poder aninhar RegexComposer’s para este fim. Outro detalhe é que os métodos da classe RegexComposer recebem sempre uma instância de Quantifier e deveriam receber uma instância de uma classe com um nome mais adequado, como Expression ou ExpressionGroup. Detalhes a serem resolvidos.

Aguardo seus comentários e quem quiser entrar em contato, meu twitter é hgflima

Um abraço.

Comments 5 comentários »