Evitando problemas com Views e DTOs

No seu aplicativo você tem um DTO cujo os dados são renderizados por diferentes Views (telas). Então, toda hora que o seu DTO muda, você percebe sua aplicação travada por uns segundos. Depois de um tempo você percebe que o problema é que mesmo as telas que estão invisíveis estão executando código de apresentação de dados com base no novo DTO. Bem, este é um problema comum quando trabalhamos com DTOs complexos e abusamos do Data Binding.

A notícia ruim é que se você não teve este problema ainda, um dia você terá. A notícia boa é que uma das maneiras de evitar este problema é utilizar o DTOViewHelper, componente disponibilizado neste screencast.

Alguns Frameworks atuais do Flex oferecem maneiras de resolver este problema, mas o DTOViewHelper é independente do Framework que você usa. Além disso, como pode ser visto no Screencast, você pode definir métodos auxiliares dentro do DTOViewHelper com o objetivo compartilhar lógica de apresentação de dados de DTOs entre diferentes Views (veja o exemplo da Data que mostra a string “Hoje” no Screencast).

Clique aqui para fazer o download do código fonte do exemplo “errado” (I could be WRONG).

Clique aqui para fazer o download do código fonte do exemplo “certo” (I could be RIGHT).


17 comentários

  1. André em 4.mar.10 às 10:50 am

    Muito bom! Já tive esse problema. Excelente solução!

  2. Mario Junior em 4.mar.10 às 11:13 am

    LOL! primeira pessoa q vejo usando adequadamente o “data” (q está em todo UIComponent) em vez de criar atributos próprios pra isso. Pouca gente se liga nesses “detalhes” do fw.

    Agora, gostaria de uma opinião sua Beck: Quando (num mundo real) vc usaria creationPolicy=”all” ?

    []s

  3. Beck Novaes em 4.mar.10 às 11:26 am

    No ultimo projeto que participei tem um creationPolicy=”all”. Mas neste caso foi usado por causa do efeito de Flip. Para poder fazer o efeito, a tela seguinte precisa estar criada. Mas é um caso bem específico, ao contrário do que acontece com a maioria das pessoas iniciantes (as vezes até experientes) em Flex. Uma das primeiras coisas que o desenvolvedor Flex aprende errado é usar o creationPolicy=”all” sem critério algum. Resolve um problema pontual, mas degrada a performance da aplicação com o tempo.

  4. Adriano Pessoa em 4.mar.10 às 11:41 am

    Muito legal a dica! E legal também a questão do data que realmente quase ninguém uma da forma mais adequada…

  5. Raphael em 4.mar.10 às 1:20 pm

    Beck muito bom o post!!!!
    Cara minha dúvida é quanto a este teu component PodFlip….
    Não tem jeito de usá-lo com o container pai setadoo com o layout horizontal??
    Aqui ele fica com comportamento estranho….
    Vlw!!
    E parabéns!!!

  6. Beck Novaes em 4.mar.10 às 1:25 pm

    Olá Raphael,

    Se eu entendi bem o seu problema, acho que não tem como não pois o PodFlip é baseado num canvas.

  7. Beck Novaes em 4.mar.10 às 1:37 pm

    Raphael… lendo seu comentário novamente tive a impressão que você não estava querendo definir o layout no PodFlip e sim no container que ele está contido. Bem, eu utilizei o PodFlip apenas em Containers com layout absoluto. Principalmente porque tem um Move nele. Não sei o que pode acontecer ao utilizá-lo em Containers com layout automático.

  8. Emil Beli em 4.mar.10 às 4:15 pm

    I’ll write this post in English as this morning Beck shown significant amount of hostility to my comment that component is generally useless. Probably because of language problem, Beck failed to see the point of it.

    Lets begin from the very beginning.
    Very nice demo, component does what it is intended to do nicely. I have a problem with a concept in general.
    Main argument was performance drawback due to necessity of ‘creationPolicy=”all” ‘. I agree. Beck’s component does it well to solve that problem. Was shown nicely on that 3 tabs.

    However, there was verbal comment something like “now imagine complex objects with many tabs”.
    Yes, I try to imagine exactly that. Pile of tabs with pile of fields. In this scenario, UX (User Expirience) turns into NX (Nightmare expirience).
    As Luciano Lobato linked to one and many more posts before, about architecture of information and visual polutions, it is easy to see the drawback of the design.

    Generally, if you more than 5 tabs, you’re doing it wrong. In this case, you should question the design not the means to solve it’s problem.
    If you have less than 5 tabs, you really don’t need that component. “Doing it right” or “Doing it wrong” is more question of developer’s ego in this case.

    We all know the story about “Hello world” application made by junior programmer, programmer and senior programmer. At the end, it just comes to that.

    Beck, my “useless component” comment was not directed to your coding, but to practical use of it. You may continue to be mad at me, as you please. It’s OK.
    I still find it useless as I use different approach to the problem.

  9. Beck Novaes em 4.mar.10 às 4:29 pm

    Emil,

    Não adianta agora tentar falar que o problema é de UX. Não é!

    O ponto principal aqui é que não importa se você usa tabs ou não. Poderia ser um Accordion ou até mesmo uma interface totalmente diferente como esta aqui: http://blip.tv/file/3086747. Eu usei o DTOViewHelper neste projeto que NÃO TEM UMA TAB e resolvi o problema que foi exposto no post.

    Não importa a user interface (ponto!). O problema é:

    Se você tem um DTO com dados que serão renderizados por diferentes telas/views (QUE NÃO SÃO NECESSARIAMENTE TABS) que podem estar invisíveis num dado momento, você pode ter este problema!

    Você focou nas tabs e talvez isso tenha impedido você de compreender a solução de uma maneira mais genérica e conceitual.

    Quanto a ser grosseiro como você alega, releia o seu Tweet que deu origem ao nosso pequeno “debate”. Eu apenas defendi o meu ponto de vista no mesmo nível da sua crítica. Se não gostou do meu tom, pense duas vezes antes de chamar o post de alguém de inútil da próxima vez.

    []‘s
    Beck Novaes

  10. Emil Beli em 4.mar.10 às 4:44 pm

    Sei que é forma generica e conceitual. É mesmo porque, presumindo:
    DTO(s) são carrgados e dados já estão disponiveis, mas não estão carregados nos componentes da tela, porque, tabs ou qq coisa que for, tem muito menos campos visiveis num momento do que tem no objeto que contem dados.

    Se fosse tão radical para otimizar as coisas, pq você tem mais dados do que pode apresentar?
    Não seria mais certo para pedir os dados necessarios na hora de apresentar-los? Não porque back-end demora? Talvez outro VO?

    Quero dizer que não existe solução generica que está certa. Existem só melhores opçoes nos casos especificos, ou mais-ou-menos genericos. Teu demo/cast foi baseado nos dados INVISIVEIS e evitando caida de perfomance no “creation policy – all’, mesmo que está desligado de qq framework como você diz..

  11. Beck Novaes em 4.mar.10 às 4:57 pm

    Ok. Agora você está sendo um pouco mais sensato. Já não está mais falando de UX, provavelmente porque percebeu que o ponto não é esse, mas resolveu questionar a arquitetura de usar um DTO com muitos Dados Vs. carregar dados sob demanda. Mas este é um outro assunto. Em momento algum eu quis levantar esta questão no post. O fato é: muitos já tiveram este tipo de problema mencionado no post e eu tentei trazer uma solução simples que pudesse ajudar. Acho que a solução tem seus benefícios e no caso do meu projeto fazia muito sentido ter o DTO com todos os dados.

    Manter um debate em alto nível sempre agrega valor. Mas não espere que eu seja indiferente quando alguém, baseado numa provável falta de entendimento da solução, decide fazer uma crítica destrutiva.

  12. Raphael em 5.mar.10 às 6:54 pm

    Obrigado!!
    Eu já desconfiava disso…
    Eu queria saber como definiu o posicionamento dos podFlips na app de logística que você apresentou neste seu video http://blip.tv/file/3086747, “O poder do Skin”.
    Foi na marra? eahueahhea
    Abraço!!
    Obs: Eu estava no FlashCamp e achei show de bola sua apresentação… Pena que a amioria dos profissionais em T.I. entenda o valor que a UX agrega ao produto!

  13. Marcio Duran em 7.mar.10 às 7:34 pm

    Gostei do ScreenCast vejo que você é uma pessoa de muita expressão, todavia gostaria de perguntar sobre valores, o curso pra pessoa fisica fica em quanto ? No Site não tem preços ?

    Obrigado !!!

  14. Beck Novaes em 9.mar.10 às 10:30 pm

    Olá Márcio,

    A DClick não ministra treinamentos desta forma (embora o texto no site esteja desatualizado). O que temos atualmente como DClick é uma parceria com a Caelum para treinamentos presenciais de Adobe Flex. Maiores informações só com o pessoal da Caelum mesmo.

    []‘s
    Beck Novaes

  15. Rodrigo P. Fraga em 16.mar.10 às 11:26 am

    Oi =)

    Gostei da solução, mas deixa eu te explicar como faço, para cada uma dos problemas que você cita:

    1- Problema de lógica repetida:
    Quanto tenho este tipo de “problema”, eu delego a reponsabilidade para meu DTO,
    tanto que passo a chama-los de Entities e sigo princípios do DDD – DomainDrivenDesign, no qual não tenho objetos DTOs “burros”, mas que tem além de estados (atributos), comportamentos (métodos) que dizem respeito a interface (nada de lógica de back-end)
    Neste caso da sua data, eu teria na minha Entity algo como:

    [Bindable(event="dataEntregaChanged")]
    public function get dataEntregaString():String{}
    ———

    2- Processamento desnecessário (Index Combo)
    Para esse problema comum, eu normalmente faço assim:

    [Bindable("entityChanged")]
    public function get estadoIndex():int
    {
    //lógica de posicionar o combo.
    }

    Desta forma, o processamento só ocorre quando o ComboBox for criado e quando o valor da entidade é mudado.
    Não tenho certeza da performance (sem tempo para testar), mas sei que o processamento só ocorre quando a view é exibida, que foi este o objetivo da sua solução, não?
    ———

    3 – População dos dados;
    Kra, normalmente carrego os dados sobre demanda, até por que “no mundo real”, a carga de dados por exemplo de uma lista de itens pode ser muito grande etc…
    Mas achei legal legal a foram que você fez…

    Abraços e espero ter agregado algo…

    Abraços

  16. Beck Novaes em 16.mar.10 às 9:41 pm

    1. Imagine que você tem vários DTOs com um atributo do tipo Date e em toda sua UI vc tem uma maneira padronizada de apresentar a Data que, inclusive, usa o formatter. A vantagem do DTOViewHelper neste caso é evitar que você tenha um “toString” em cada DTO com formatters e lógica repetida. Generalizando um pouco mais a ideia, a vantagem do DTOViewHelper é compartilhar tratamento de dados para apresentação entre diferentes DTOs. O DTOViewHelper é muito mais acoplado a View do que ao DTO e isso conforme os exemplos citados isso garante certa flexibilidade.

    2. Não entendi bem este ponto.

    3. Eu também carrego muita, mas muita coisa mesmo sob demanda. Além disso odeio Refresh’s desnecessários. Por exemplo, você tem uma lista, e adiciona um novo item nesta lista. No lugar de fazer um refresh e pegar todos os itens novamente – como muita gente faz – eu gosto de receber do back-end só o item que acabou de ser adicionado e atualizá-lo na lista (isso é necessário pois precisamos do ID do item que é retornado pelo back-end). Mas nem sempre isso é possível de maneira simples. Se você tem, por exemplo, views com dados resumidos e views com dados detalhados, carregar sob demanda estes dados detalhados para um número considerável de views é muito trabalhoso e com certeza o ganho de performance não compensaria a complexidade. Isso sem contar que para o back-end pode ser mais simples ter uma única chamada que retorna um DTO Complexo do que “N” chamadas para dados aos picados. Com o AMF trabalhar com DTOs complexos não deve gerar necessariamente problemas de performance no trafego de dados. Mas gera pela questão do Binding, por isso o DTOViewHelper pode ajudar.

  17. Rodrigo P. Fraga em 17.mar.10 às 10:29 am

    1 – Ok, concordo contigo. Mas olhando por este lado, o DTOViewHelper se torna também um local para seus métodos útil, subtituindo aquelas classes *Util, que por ventura não gosto muito…
    O caso de formatar a data e outras funções mais genéricas, acho legal colocar neste tipo de classe, agora comportamentos que dizem respeito a classe de domínio, para mim devem ficar na entitdade.
    Eu mesmo não utilizo labelFunction, mas sim tenho métodos (get and sets) dentro da entidade que tratam a exibição de atributos complexos (ex: return myObj.name) e que re-aproveito utilizando numa Datagrid, Combobox, List etc…

    —————————
    2 – Sumiu o fonte do combo o.O, segue aí:

    mx:Combox id=”inputEstados” selectedIndex=”{estadoIndex}”

    [Bindable("entityChanged")]
    public function get estadoIndex():int
    {
    //lógica de posicionar o combo.
    }

    O dispach do “entityChanged” ocorre quando a instância que contém a lista de itens é modificada, no caso, no set data.

    3 – Eu falei assim, mas esse load de lista faço o mais performático o possível,
    num getAll da vida, abandono o recurso de LazyLoad que infelizmente não conheço uma implementação bacana que funcione legal, e carrego somente o necessário, somente o que quero exibir numa grid por exemplo. Se quero todas as informações de um objeto, faço um findById depois disso… mas isso depende muito da UX.
    Uma coisa que utilizo também, é o dataPush então normalmente só faço o load da lista uma vez, depois passa a funcionar somente via pushing (alguém alterou a lista, irá refletir para todos os clients conectados). Tenho uma implementação para BlazeDS e uso a nativa do LCDS.
    ————————–

    Bom, acho que estamos fugindo dos 3 problemas apresentados… =)

    Obrigado pela atenção.
    Abraços!

Deixe Seu Comentário