Diminua suas dependências com os eventos do CDI

Já falamos de CDI aqui no blog da Caelum, tanto numa introdução pra você começar a usar o CDI quanto em tópicos mais avançados. E recentemente até abordei o tema em uma palestra sobre CDI no JavaOne, além de sempre falarmos dele no nosso curso de JSF e CDI.

Fato é que o CDI tem muitas vantagens e, por isso, recomendo que você use no seu próximo projeto Java. É uma solução completa de injeção e gerenciamento de dependências, é uma especificação oficial, é muito fácil e produtivo de usar, e tem muitos recursos interessantes.

Esse post trata de um recurso avançado do CDI pra diminuir bastante o acoplamento dos seus objetos. Injeção de dependências por si só já é uma baita diminuição de acoplamento: a gente passa a receber os objetos magicamente sem nos preocuparmos com sua criação ou seu ciclo de vida. Mas, se pensar bem, ainda estamos bem acoplados com a dependência que recebemos.

Alto acoplamento

Uma classe FinalizaCompra, por exemplo, que recebe uma dependência injetada do tipo SistemaPagamento, está acoplada com esse objeto. Se o processo de finalizar uma compra for um pouco mais elaborado, é fácil ver que várias dependências, mesmo injetadas, geram uma grande complexidade:

public class FinalizaCompra {

	@Inject private SistemaPagamento pagamento;
	@Inject private Estoque estoque;
	@Inject private Notificacoes notificacoes;
	
	public void finalizaCompra() {
		pagamento.efetuaCobranca();
		estoque.reservaMercadoria();
		notificacoes.notificaCompra();
	}
}

Nesse exemplo, criei um processo que finaliza a compra de um usuário na loja virtual. Esse processo exige a execução de diversas tarefas diferentes e não relacionadas, e a classe FinalizaCompra foi encarregada de coordenar tudo.

Usamos @Inject pro CDI injetar os 3 objetos, o que já ajudou a flexibilizar nosso código. Mas a classe FinalizaCompra tem dependências demais. Pior, se o processo mudar e exigir mais um passo, teremos que editá-la e acrescentar mais uma dependência. Essa classe tem a difícil responsabilidade de coordenar vários objetos bem diferentes.

E se conseguíssemos flexibilizar nosso design para que a classe FinalizaCompra ficasse independente das etapas individuais do processo? Adicionar novas etapas não exigiria mudanças na FinalizaCompra e ela não teria mais aquele acoplamento com tantas dependências diferentes.

Conseguimos isso com os eventos do CDI.

O evento

A API de eventos do CDI nos permite implementar um mecanismo onde um componente do sistema dispara um aviso quando certo acontecimento ocorrer, e depois outros componentes podem escutar esse aviso e executar suas respectivas atividades. O evento do qual falamos, é esse acontecimento que será avisado pra todo o sistema.

No nosso exemplo do processo de finalização de compra, o evento em si, o acontecimento que ocorre naquele instante, é simplesmente o fato do usuário ter feito uma compra. A compra é nosso evento. E o primeiro passo é criar uma classe simples pra representar esse evento, uma classe Compra.

A ideia é, então, criar um objeto Compra toda vez que a compra for efetuada. No caso, dentro da nossa classe FinalizaCompra de antes:

public class FinalizaCompra {
	public void finalizaCompra() {
		Compra compra = new Compra(produto, cliente);
	}
}

Essa classe Compra seria bem simples; no exemplo, fiz com que recebesse o produto da compra e qual cliente estava recebendo, apenas pra ilustrar.

Disparando eventos no CDI

Próximo passo: usar o CDI pra anunciar pra todo mundo que uma compra foi efetuada naquele instante na classe FinalizaCompra. Usamos a classe Event que é parametrizada com o tipo do evento que estamos lidando. Ficaria então um simples Event<Compra>. E, como tudo no CDI, vamos injetar esse objeto pra não precisarmos criá-lo:

@Inject @Any Event<Compra> eventoCompra;

Esse objeto tem uma utilidade principal: disparar o evento no momento certo e anunciar o objeto do evento pra todo mundo que estiver escutando. É uma simples chamada:

eventoCompra.fire(compra);

Pra ficar mais claro, vamos juntar tudo na nossa classe FinalizaCompra, agora terminada:

public class FinalizaCompra {
	@Inject @Any private Event<Compra> eventoCompra;
	public void finalizaCompra() {
		Compra compra = new Compra(produto, cliente);
		eventoCompra.fire(compra);
	}
}

Nesse ponto, o que temos é nossa classe anunciando pra todo lado que o evento de Compra acontece quando alguém chega no método finalizaCompra.

Escutando eventos no CDI

Bom, mas a parte difícil era que, ao acontecer uma compra, precisávamos disparar 3 processos: o pagamento, a reserva da mercadoria no estoque, e uma notificação por email pro usuário. Antes, essas 3 chamadas ficavam dentro da FinalizaCompra; agora, vamos inverter isso.

A FinalizaCompra apenas avisa que o evento de Compra aconteceu. E cada uma das 3 classes (Pagamento, Estoque e Notificacoes) fica responsável por observar se o evento de compra aconteceu ou não.

Com CDI, isso é muito fácil. Basta cada uma das classes receber o objeto do evento como argumento e usar a anotação @Observes. A classe Estoque, por exemplo, ficaria:

public class Estoque {
	public void reservaMercadoria(@Observes Compra compra) {
		// ... reserva produto no estoque ...
	}
}

O papel do CDI aqui é coletar o evento disparado na nossa classe FinalizaCompra e disparar todos os métodos marcados com @Observes recebendo aquele evento específico.

Podemos implementar o @Observes em quantas classes quisermos (no nosso caso, seriam as 3 citadas) e o CDI vai disparar todas. Precisa de mais um participante no processo? Sem problemas, só colocar @Observes nele e nada mais precisa ser mudado no sistema.

Acoplamento praticamente zero

Repare como o processo fica completamente desacoplado. A classe FinalizaCompra nem conhece mais quais são as etapas do processamento de uma Compra. Pra ela, podem ser 3 passos ou mil passos, não importa. Seu trabalho é simplesmente disparar o evento na hora certa.

Conseguimos flexibilidade e simplicidade importantes em qualquer sistema um pouco mais complexo. Separamos bem as responsabilidades, e diminuímos o papel da FinalizaCompra que antes era de centralizar toda a coordenação do processo de compra.

E esse mecanismo do CDI é, na verdade, uma implementação bastante flexível e fácil de usar de um design pattern famoso chamado Observer que está descrito no GoF. A gente fala mais desse pattern (e outros) no curso online de Design Patterns da Caelum.

Se você gosta do CDI, pode se interessar também pelos slides da palestra do JavaOne que mostram esse mecanismo de eventos e ainda outras ideias bacanas:

46 Comentários

  1. Rafael 11/12/2012 at 11:15 #

    Ótimo artigo!
    Ter algo assim no VRaptor seria extremamente útil para justamente evitar controladores inchados de dependências como o caso de um FinalizaVenda

  2. José Dal Prá Junior 11/12/2012 at 11:21 #

    Ótimo post! Como sempre.

    – Existe alguma outra implementação do CDI além do Weld?
    – Tem uma previsão para migração do VRaptor para o CDI?
    – Vocês já pensaram em criar algum framework para construção mais otimizada de aplicações para dispositivos móveis?

    Valeu.

  3. Jairo Ricarte 11/12/2012 at 11:24 #

    Lembrando que o @Observer deveria ser utilizado em Classes que implementam o Event.

    Parabéns pelo Post e Apresentação na JavaOne.

  4. Guilherme Balde 11/12/2012 at 11:33 #

    Bom dia,
    Eu acho muito bom esse desacoplamento do CDI. No meu último projeto tentei usar os listeners, mas tive um problema:

    Eu tenho minhas classes, a partir delas eu tenho um conjunto A de instâncias e um outro conjunto B de instâncias diferentes dessas mesmas classes.

    Eu precisaria que os eventos ficassem restritos à cada conjunto desse, de forma que os observers do conjunto B não ouçam eventos lançados do conjunto A.

    Todas as classes envolvidas estavam sendo injetadas, mas não consigo fechar essas conjuntos em escopos separados. Alguém tem alguma idéia? Eu até cheguei em uma solução usando o escopo default, que é o dependente, mas os objetos eram recriados ao receber os eventos, e eu não posso perdê-los uma vez que são criados!

    Abraço.

    Balde.

  5. Frederico Maia Arantes 11/12/2012 at 11:36 #

    CDI é fantástico. Já vi um vídeo de vocês falando sobre as vantagens que seriam adicionadas ao VRaptor se usasse o CDI para DI, com eventos e a extensibilidade que ganharia. Alguma previsão pra isso? Hoje é possível substituir o Spring e no lugar dele usar o CDI com VRaptor?

    Parabéns pelo post.

  6. Lucas 11/12/2012 at 11:46 #

    Muito legal, Sérgio! Apenas uma questão, nem tão relacionada com o CDI: as classes FinalizaCompra, SistemaPagamento, Estoque e Notificacoes, seriam o que? Facades?
    Valeu, abs.

  7. Rafael Ponte 11/12/2012 at 11:55 #

    Excelente post, Sérgio.

    Trabalhar com o padrão Observer é prático e simples quando a ordem de execução dos observadores não importa. Mas e se a ordem dos observadores importar para o negócio, como o CDI trata dessa ordem?

    Talvez implementando outra interface ou utilizando algum anotação?

  8. Mauricio Aniche 11/12/2012 at 12:15 #

    Oi Rafael,

    No VRaptor, vc pode receber um List, que ele vai te injetar todas as implementações dela!

    Coisa linda esse Observer! 🙂

    Um abraço,
    Mauricio

  9. Sérgio Lopes 11/12/2012 at 12:17 #

    @José Dal Pra

    1) Existem outras implementações sim, mas a Weld é a implementacao de referencia oficial. Mas tem a OpenWebBeans, tem uma da Caucho, outros vendors etc.

    2) Sem previsão de data. Mas tem um pessoal trabalhando na implementacao do CDI no VRaptor 3.x ainda.

    3) Nada em mente por enquanto 🙂

  10. Sérgio Lopes 11/12/2012 at 12:19 #

    @Guilherme Balde

    Se entendi seu problema, o que você precisa é de Qualifiers misturados com Eventos. Dá pra fazer sim, veja mais aqui:

    http://docs.jboss.org/weld/reference/latest/en-US/html/events.html#d0e4016

  11. Sérgio Lopes 11/12/2012 at 12:21 #

    @Frederico Maia

    Tem um pessoal trabalhando nesse exato momento em suportar CDI como alternativa ao Spring no VRaptor 3.x. Deve sair em breve.

    Além disso, existe outra iniciativa, de um possível novo VRaptor 4 que usaria CDI internamente e exploraria melhor os recursos da spec (é o vídeo que você viu). Esse tá um pouco parado ainda pois exige uma reestruturação maior, e ainda estamos ajeitando as ideias novas 🙂

  12. Sérgio Lopes 11/12/2012 at 12:23 #

    @Lucas

    As classes são beans normais, simples, sem nenhuma característica especial. Num sistema web, eu imagino a FinalizaCompra sendo um controller do seu framework predileto (JSF, VRaptor etc) ou até mesmo uma Servlet simples.

    As outras são classes de negócio simples que implementam alguma funcionalidade específica do sistema (Pagamento, Estoque etc).

  13. Guilherme Balde 11/12/2012 at 12:34 #

    @Sérgio Lopes

    Muito obrigado pela referência. Eu considerei os qualifiers, mas o problema é que os dois conjuntos de instâncias não são de classes distintas, eu teria que ter um “qualifier” resolvido em tempo de execução, pra eu poder lançar um evento X para instancias com o “Group ID = 1234”, e eu não queria receber o evento e começar com um “if (groupId == …” =P. Fora isso tem o problema de ele não manter em memória as mesmas instâncias.

    Eu considerei também criar um escopo programaticamente, mas começou a virar quase que um framework separado pra resolver um problema muito simples.

    Parabéns pelo post!

  14. Sérgio Lopes 11/12/2012 at 12:35 #

    @Rafael Ponte

    Não existe ordem pré-determinada. A ideia dos eventos é justo pra cenários onde a ordem não importa muito. Há até uma discussão um tanto acalorada sobre possíveis mudanças à spec pra acomodar algum mecanismo de ordem, mas até onde sei não há nada fechado:

    https://issues.jboss.org/browse/CDI-4

    Mas eu acho que a beleza dos eventos do CDI é justo esse desacoplamento que ele gera entre o produtos e o(s) consumidor(es). Colocar alguma ordem lá seria voltar um pouquinho atrás e gerar mais um acoplamento. Eu gosto dessa ideia de não conseguir prever a ordem de execução 🙂

    Sei, contudo, que há cenários que você precisa de ordem. Eu acho que boa parte desses cenários poderiam ser resolvidos com múltiplos eventos disparados na sequência certa. Algo como:

    eventoCompra.fire(compra);
    eventoFinalizacao.fire(finalizacao);
    

    Eventos distintos que mantém a ordem entre si por serem síncronos, mas sem manter a ordem internamente se houver mais de um observador no mesmo evento. Eu gosto dessa ideia e acho que ela resolve boa parte das necessidades de ordenação sem gerar acoplamentos indesejados entre as classes observadoras. Que acha?

  15. Rafael Ponte 11/12/2012 at 12:36 #

    Oi Aniche,

    É exatamente isso que eu faço com Spring hoje em dia. Tenho uma interface comum e injeto uma lista com todas as implementações dessa interface. Se precisar ordenar é fácil.

    Pesquisei se o CDI já tinha esse recurso pronto, acabei encontrando essa issue, https://issues.jboss.org/browse/CDI-4

  16. Rafael Ponte 11/12/2012 at 12:42 #

    Oi Sergio,

    Eu também concordo com você e não vejo muito sentido na ordem dos observers. Eu havia lido também essa issue sobre o assunto e achei algumas das soluções muito verbosas e que sem dúvida fogem da simplicidade.

    No mais, excelente sugestão! Gostei e já está anotada 🙂

  17. Sérgio Lopes 11/12/2012 at 12:57 #

    @Guilherme Balde

    Entendi o problema agora.

    Acho que uma ideia seria usar um Interceptor do CDI em cima dos seus observadores dos eventos. Esse interceptor faria esse if do group_id pra você e não chamaria o proceed() caso não fosse o id desejado no momento.

    Dessa forma você mantém o if feioso centralizado no interceptor e consegue filtrar quais execuções dos observadores são permitidas em quais momentos. Que acha?

  18. Alexandre Saudate 11/12/2012 at 14:18 #

    Oi, Sérgio! Excelente post, parabéns!

    Dúvida rápida… os disparos dos eventos são feitos de maneira síncrona ou assíncrona? Tem como controlar isso?

  19. Sérgio Lopes 11/12/2012 at 14:26 #

    @Alexandre

    Os eventos são síncronos, sempre. O objetivo deles é puramente de design, pra desacoplamento. Não é uma solução pensando em escalabilidade ou paralelismo.

  20. Edilmar Alves 11/12/2012 at 17:13 #

    Sérgio,

    Se no caso de finalizar a compra, os 3 passos tivessem que ser executados dentro de uma transacao? pois “vai que da pau” em alguma delas, por ex., “reservaMercadoria” da pau e “efetuaCobranca” passa sem erros. Imagina que o cliente recebe um boleto mas nao recebe a mercadoria. Existe alguma forma de amarrar todos os eventos do CDI dentro de algum tipo de transacao de EJB ou algo do tipo?

  21. Sérgio Lopes 11/12/2012 at 17:23 #

    @Edilmar

    O Event#fire vai disparar de volta pra você qualquer exception que qualquer um dos observadores disparar. Então se o fire estiver dentro de uma lógica transacional, você pode usar isso pra dar rollback na sua transação.

    Além disso, existem certos recursos transacionais pros eventos. Você pode, por exemplo, vincular a execução de um certo observador só se a transação for bem sucedida. E outras coisas:

    http://docs.jboss.org/weld/reference/latest/en-US/html/events.html#d0e4075

    Mas, claro, no cenário que você descreveu, se uma etapa do processo foi feita e dá pau depois em outra etapa, não há mágica pra desfazer o que já feito (se o email de notificação foi enviado, não há como “des-enviar” hehehe). Você vai ter que controlar isso na mão, de desfazer efeitos das suas lógicas de negócio.

  22. Jairo Ricarte 11/12/2012 at 17:41 #

    @Edilmar
    Você pode utilizar o EJBContext.setRollbackOnly() e desfazer a transação inteira.

    Naturalmente o CDI já vincula os evento à transações.
    Existem as ‘TransactionPhase’ que definem quando o evento será observado:

    AFTER_COMPLETION:
    Identifies an after completion observer method, called during the after completion phase of the transaction.
    AFTER_FAILURE:
    Identifies an after failure observer method, called during the after completion phase of the transaction, only when the transaction fails.
    AFTER_SUCCESS:
    Identifies an after success observer method, called during the after completion phase of the transaction, only when the transaction completes successfully.
    BEFORE_COMPLETION:
    Identifies a before completion observer method, called during the before completion phase of the transaction.
    IN_PROGRESS:
    Identifies a regular observer method, called when the event is fired.

    Fonte: http://docs.jboss.org/cdi/api/1.0/javax/enterprise/event/TransactionPhase.html

  23. Jairo Ricarte 11/12/2012 at 17:47 #

    @Sérgio
    Como adicionar um evento para um POJO pode disparar eventos desnecessários ou Não desejados, não seria mais prático criar um Payload para o evento. ex.: CompraSolicitadaEvent ou coisa do tipo.
    E o payload efetuar o que for necessário pós compra (até mesmo disparar outros eventos)?

  24. Sérgio Lopes 11/12/2012 at 17:57 #

    @Jairo

    Acho que sim, Jairo. Esse objeto do evento pode ser mais espertinho, coordenar vários eventos seguintes e tal. Acho uma boa pra processos mais complexos.

  25. Peres 11/12/2012 at 21:19 #

    Sérgio Lopes – Hoje o Java EE 6 está muito poderoso. JPA, EJB 3.1, JSF 2 e claro o CDI. A casa do código deveria lançar um livro com essas tecnologias e com o eclipse. Pois todo material hoje é como o Netbeans.

  26. Rafael 11/12/2012 at 23:32 #

    Eu sempre gostei desse conceito de event, só falta implementar rápido o mecanismo de estabelecer uma ordem de execução dos observers =b

  27. Rafael 12/12/2012 at 00:28 #

    Perfeito @Sérgio, por ser um processo síncrono, um mecanismo de ordenação além de gerar acomplamento e complexidade, iria tb gerar mal uso desse recurso.

    No caso do seu exemplo haver necessidade de ordenação, uma solução amigável seria disparar eventos a cada conclusão de uma etapa: Ex.

    pagamento.efetuaCobranca() dispara um cobrancaEfetuada

    estoque.reservaMercadoria() observa um cobrancaEfetuada e dispara um mercadoriaReservada

    e assim sucessivamente. Muito mais simples e legível.

    Por outro lado se esse processo fosse assíncrono talvez seria legal ver algo assim no CDI:

    FinalizaCompra dispara eventoCompra(compra)

    Pagamento, Estoque e Notificacoes observariam Compra mas disparariam eventos de finalizacao também.

    Entao caso o Estoque precise executar depois do Pagamento, o observador do Estoque poderia ser algo assim:

    @Observes(waitForEvent=CobrancaEfetuada) Compra compra

  28. Victor Franz 13/12/2012 at 11:26 #

    Olá,

    Recurso muito bacana, mas seu uso vai de muito bom senso.
    Algumas vezes simplesmente acoplamento se faz necessário.
    Por exemplo, se precisa de ordem, pronto, no meu entendimento aí já temos um acoplamento que se faz necessário, pois normalmente essa ordem é devido uma regra de negócio e essa regra deve estar explícita para o desenvolvedor entender como o negócio funciona através do código escrito.
    Se tiver que abrir várias classes, procurar todas as implementações de uma determinada interface, escrever num txt ou em uma folha a ordem para entender o que se passa, aí todo esse ganho no desacoplamento se perde no dia a dia.

    Abraço,
    Victor.

  29. Julio Santana 13/12/2012 at 11:35 #

    @Sérgio,

    Excelente post! Estou ainda engatinhando com a spec do CDI, mas a cada dia tenho gratas surpresas.

    Gostaria que a Caelum ofertasse um curso focado somente em CDI e suas features.

  30. Felipe Caparelli 13/12/2012 at 13:46 #

    Muito bom, parabéns mais uma vez pelo texto sucinto e objetivo.

  31. Frederico Maia 13/12/2012 at 14:04 #

    @Sérgio,

    Massa! Mantenha-nos informados quando o VRaptor der suporte ao CDI. A propósito estou utilizando VRaptor em um projeto meu e gostando muito. Estou achando bem melhor que o Spring MVC.

  32. Cácio Costa 13/12/2012 at 17:39 #

    Estou contigo, @Júlio Santana. Seria massa um curso só de CDI oferecido pela Caelum…

    E estou louco esperando essa versão do VRaptor com CDI: vai ficar show e mais fácil de “vendê-lo” aos “defensores do JSF por ser especificação”…

  33. Leandro 14/12/2012 at 13:01 #

    Sérgio, achei interessante o titulo “Porque você deveria usar cdi em todos os seus projetos Java”, estou justamente nesse dilema, temos aplicações em seam 2.2, vamos iniciar o desenvolvimento de uma nova, e estamos indecisos quanto a utilização do seam 2.3 que comporta jsf2 ou cdi.

    O problema do cdi é que nossa equipe não o domina, já o seam está bem consolidado.

    Gostaria de saber o que pensa sobre isso.

  34. Sérgio Lopes 14/12/2012 at 15:12 #

    @Leandro

    Acho que o que você precisa medir é o impacto do aprendizado das novas tecnologias pela sua equipe vs o uso de algo já “ultrapassado”. Do ponto de vista só de vantagens técnicas, o CDI é bem melhor e o Seam2 já não deveria mais ser usado; o Seam3 tem umas extensões bem interessantes pro CDI. Mas precisa ver se vale a pena pra vocês esse aprendizado novo todo (eu, como bom representante do ramo de educação, diria que vale a pena aprender sim hehehe).

  35. Fabio 14/12/2012 at 16:46 #

    Otimo post! 😉

  36. Rômero Ricardo 14/12/2012 at 17:20 #

    E a ordem de execução dos 3 processos, se for necessária, como definir?

  37. Sérgio Lopes 14/12/2012 at 17:28 #

    @Rômero O CDI não tem ordem para os eventos, o que não necessariamente é ruim. Veja meu comentário anterior sobre isso:

    http://blog.caelum.com.br/diminua-suas-dependencias-com-os-eventos-do-cdi/comment-page-1/#comment-115142

  38. Rodrigo 18/12/2012 at 09:55 #

    Mas e a ordem com a qual cada classe observadora é executada???

  39. Cácio Costa 05/01/2013 at 20:21 #

    Sérgio, fugindo totalmente do assunto, mas gostaria de saber se tem alguma iniciativa do VRaptor suportar Websockets.

  40. Sérgio Lopes 07/01/2013 at 02:59 #

    Cácio, nada específico no VRaptor por enquanto. Escrevi um pouco sobre WebSockets com Jetty aqui no blog caso ainda não tenha visto:
    http://blog.caelum.com.br/websockets-html5-em-java-com-jetty-web-em-tempo-real/

  41. Raphael Lacerda 08/01/2013 at 16:27 #

    Bacana demais. Muita feature inspirada no Seam.

    Uma dúvida, no padrão observer que o CDI adota, tem como disparar um evento assíncrono?

  42. Sérgio Lopes 08/01/2013 at 20:02 #

    Não há nada de assíncrono. Os eventos do CDI são uma solução de design e desacoplamento, não de paralelismo ou escalabilidade.

  43. Dênis Schimidt 17/01/2013 at 08:26 #

    Achei bem interessante a implementação do padrão Observer (GOF) pela própria API. Isso se traduz em simplicidade e desacoplamento total.

    Porém fiquei pensando, que talvez para esse caso a ordem seria importante, pois não faria sentido eu notificar primeiro mesmo se não receber o pagamento. Aí eu fiquei pensando em passar uma factory via construtor (Factory method) e esse iria gerar uma lista de comandos (padrão command) e a classe FinalizaCompra simplesmente executaria essa lista ordenada de comandos. Existe alguma outra m’agica do CDI para contornar isso?

  44. Sérgio Lopes 17/01/2013 at 13:24 #

    Dênis, se você precisar de ordem mas ainda quiser usar eventos pra desacoplar o observador de quem chama, eu faria eventos encadeados. Dispara um evento, esse termina de executar, e dispara outro em seguida, na ordem.

  45. alvinofigueiredo 20/05/2014 at 18:48 #

    Estou pensando em inicia um aplicação comercia em java rcp e gostaria de saber se no JSE eu consigo usar o poder do CDI.

Deixe uma resposta