26 de abr. de 2010

three-valued logic (3VL)

Salve,

Já que tenho um tempinho livre, resolvi falar sobre o a lógica ternária. Provavelmente todos que estão lendo este tópico, entendem lógica boleana, e o tipo boleano (boolean), e é sobre ele que a discussão paira.

O tratamento da lógica ternária é um desafio para solução de implementação e abaixo mostrarei algumas idéias mais simples.


Tipo boleano

O tipo boleano, cuja premissa foi cunhada por Boole (http://en.wikipedia.org/wiki/Boole), identifica uma informação com dois, apenas dois, estados, como o BIT. Para programadores de linguagens de alto nível, tal como Java, entendemos como simplmesmente:

verdadeiro ou falso

Quem passou por uma cadeira de lógica e álgebra booleana, deve conhecer a famosa tabela, esta:

TRUE   AND  TRUE   =  TRUE
TRUE   AND  FALSE  =  FALSE
TRUE   OR   FALSE  =  TRUE
FALSE  OR   FALSE  =  FALSE

Bem simples não?


Three-valued logic

A lógica ternária, inclui um terceiro estado, indeterminado, ou formalmente unknown (desconhecido). Não é novidade, ele é utilizado no SQL, conhecido como o valor NULL.

Imagine um campo fumante em uma tabela qualquer de uma base de dados relacional (SQL), e que seja do tipo boolean. Este campo pode ter os seguintes estados:

+---------+------------+
| Nome    |  Fumante   |
+---------+------------+
| João    |  true      |
| Maria   |  null      |
| José    |  false     |
+---------+------------+

Em teoria, entende-se que João é fumante, José não, e não sabemos se Maria fuma ou não, correto?

Esse é o enfadonho "Boolean de três estados". Existe uma tabela, que inclui o valor desconhecido, como pode ser visto abaixo:

ABA OR BA AND BNOT A
TrueTrueTrueTrueFalse
TrueUnknownTrueUnknownFalse
TrueFalseTrueFalseFalse
UnknownTrueTrueUnknownUnknown
UnknownUnknownUnknownUnknownUnknown
UnknownFalseUnknownFalseUnknown
FalseTrueTrueFalseTrue
FalseUnknownUnknownFalseTrue
FalseFalseFalseFalseTrue
fonte: http://en.wikipedia.org/wiki/Ternary_logic


"Maldição do Boolean de Três Estados"

Quando se está programando, podemos cair nesta questão, neste detalhe.

Por exemplo, utlizando um método no Java para separar em uma lista somente os fumantes.

// uma classe de domínio

public class Funcionario {
    // bla bla bla

    public Boolean isFumante() {
        return isFumante;
    }
}

// em uma classe qualquer

private List getFumantes(List todos) {
    List fumantes = new ArrayList();

    for (Funcionario funcionario : todos) {
        if (funcionario.isFumante()) {
            fumantes.add(funcionario);
        }
    }
    return fumantes;
}

Pergunto, há algum problema com essa solução de implementação?

tic, tac, tic, tac, tic, tac, ....

CLARO.

Esta código está propenso a:

Exception in thread "main" java.lang.NullPointerException
at Teste.main(Teste.java:9)

E o problema está aqui:

if (funcionario.isFumante())

Se o retorno do método for null, já que estamos usando a classe Boolean como retorno e não o tipo primitivo boolean, o teste condicional lançará uma NPE.
Esta é a maldição do "boolean de três estados".



Programando ciente do 3VL

Existem algumas estratégias para solução deste problema, e o DataSource da classe de domínio é extremamente importante. Caso venha de uma base SQL, onde NULL é um valor aceitável, ou seja, quer dizer "não informado", a melhor estratégia é implementar na classe cliente, o chamador do método.

Implementar na classe servidora seria mais simples, por exemplo, testando se o valor é nulo e retornando falso, entretanto, se na camada de apresentação for necessário informar em um rótulo ou caixa de texto: Fumante: SIM, ou Fumante: Não, ou Fumante: ____, o terceiro valor (NULL) serviria para manter a informação vazia.

Então, tudo depende. Se não haverá problema na camada de apresentação, e o valor desconhecido, vindo do DataSource, na prática, não é necessário, o melhor é implementar na classe servidora, como no exemplo:

public class Funcionario {
    // bla bla bla
    public Boolean isFumante() {
        return isFumante == null ? false : isFumante;
    }
}

Sempre retornará falso caso o valor seja nulo, o que significa, NPE-Free!

Por outro lado, se o valor desconhecido (NULL) é necessário, implemente no cliente, como no exemplo:

for (Funcionario funcionario : todos) {
    if (funcionario.isFumante() != null && funcionario.isFumante()) {
        fumantes.add(funcionario);
    }
}


Lembre de testar se é diferente de nulo primeiro, assim se for nulo, a segunda parte da expressão não será avaliada, desde que você use o operador && e não apenas um & (o && é conhecido como operador de curto circuito: http://en.wikipedia.org/wiki/Short-circuit_evaluation).



Programação defensiva

Uma sugestão, é procurar saber se o retorno do método é seguro, por exemplo, consultando o Javadoc ou assistente de código do IDE.

Nem sempre temos a mão o código fonte para consultar, e até alterar, a classe servidora, então para garantir:

  • Se o retorno for Boolean, com B maiúsculo, significa que retorna um objeto, um wrapper, e que o valor NULL é admissível,  então, teste antes:

if (classeQualquer.metodoBoleano() != null && classeQualquer.metodoBoleano()) ...

  • Caso contrário, caso o retorno do método seja boolean, com B minúsculo, significa que retorna um tipo primitivo, e realmente, ou virá verdadeiro ou falso, e neste caso o teste não é necessário:

if (classeQualquer.metodoBoleano()) ...


Casos semelhantes acontecem com outros Wrappers, como Integer, Double, etc, mas que ficam para um próximo tópico.


Claro, existem outra soluções viáveis, fiquem a vontade para sugerir.

Obs.: Usando um exemplo, o Apache Velocity é ciente do 3VL, se eu utilizar #if($cliente.isFumante), e isFumante é nulo, a expressão avalia falso sem warnings.

Referências:

Java passa por valor

Salve

No melhor estilo Nerd, neste fim de semana, em um BoF*, rolou o assunto de como as linguagens passam as variáveis para os métodos (functions, procedures, ). Estavam presentes programadores PHP, C e Java (eu).


Tudo ia bem, até por que entendo pouco de PHP e C, foi então que chegamos nas linguagens orientadas a objeto (eu sei que PHP é orientada a objetos -tem suporte pelo menos- mas quem usa?).

A discussão rola na internet também, e a pergunta que não quer calar é:

"Java passa as variáveis por valor ou por referência?"

Obs.: Passar por valor é às vezes chamado de "Passar por cópia".

Como funciona no Java -e maioria de linguagens orientadas a objetos-:

Imagine a seguinte situação:

int x = 12;
int y = x;

System.out.println(x);
System.out.println(y);

Este caso é um exemplo com tipos primitivos, e a resposta será:

12
12

E se decorrer eu adicionar:

y = 24;

System.out.println(x);
System.out.println(y);

A resposta será:

12
24

Sim, na linha em que declaramos y = x, a variável y é colocada na pilha e logo após recebe uma cópia do valor de x, então tenho isso na pilha:

x = 12
y = 12

A alteração de uma, não influencia a alteração de outra, isto é passagem por valor. Então se y = 24, x continua sendo 12. y obteve uma cópia dos bits de x.



Contudo, a situação se complica um pouco quando falamos de objetos, imagine a situação;

Cliente x = new Cliente();
x.setNome("Juliana Paes");

Cliente y = x;

x.setNome("Débora Secco");

System.out.println(x.getNome());
System.out.println(y.getNome());

A resposta será:

Débora Secco
Débora Secco

AHA! Então passou por referência! NÃO, passou por cópia.



Quando falamos em referência, referência = variável, não o Objeto no Heap. O que aconteceu neste caso foi o seguinte:

Memória ----------------------------------------+
Stack -------+                                  |
x = EF4EA3   |                                  |
y = EF4EA3   |                                  |
                                                |
Heap ----------------------------------------+  |
+EF4EA3---------+                            |  |
| Cliente       |                            |  |
| Juliana Paes  |                            |  |
+---------------+                            |  |
------------------------------------------------+

As variáveis contém uma referência para o objeto no heap, que tem um ID indeterminável (só a JVM sabe, provavelmente um número de 32 ou 64 bits), logo na declaração das variáveis:

x = referência para objeto EF4EA3 no Heap
y = x    //y = EF4EA3

Ou seja, y tem uma cópia do valor de x, o mesmo caso da cópia dos bits, que é a referência, o "id", do objeto. Para ilustar imagine esta situação:

Cliente x = new Cliente();
x.setNome("Juliana Paes");

Cliente y = x;

x = new Cliente();
x.setNome("Débora Secco");

System.out.println(x.getNome());
System.out.println(y.getNome());

Qual é a saída? Bem, está abaixo:

Débora Secco
Juliana Paes

Dois objetos foram criados, usando a variável x. No primeiro instante, y obteve uma cópia do valor de x, uma referência para Juliana Paes. Mais tarde, x recebeu um novo cliente, logo, uma nova referência a outro objeto no Heap, e esta alteração no valor de x não altera o valor de y, isto é o que quero dizer com passagem por valor. 

Cada variável tem um valor, que é a referência a um objeto, não o próprio objeto (isto é bem básico).



Na passagem para métodos, acontece o mesmo:

// em algum lugar 
Cliente x = new Cliente();
x.setNome("Juliana Paes");

changeCliente(x);

System.out.println(x.getNome());

//


public void changeCliente(Cliente x)  {
    x = new Cliente();
    x.setNome("Débora Secco");
}

Qual é a saída do console?

Ora, Juliana Paes, claro. Existem duas variáveis com o nome x, uma fora do método, e outra, local, dentro do método. A mudança da variável interna, não afeta a externa, afinal, ela tem uma cópia do valor.



// em algum lugar 
int x = 18;

change(x);

System.out.println(x);

//


public void change(int x)  {
    x = 9;
}

A saída é 18.



Algumas linguagens permitem usar, alternativamente, a passagem por referência, como a C#.

Ilustrando:

// em algum lugar 
int x = 18;

change(x);

Console.WriteLine(x);
//

public void change(ref int x)  {
    x = 9;
}

A saída é 9. A diferença foi o uso da palavra-chave ref antes de declarar o parâmetro.



Em VB.NET, para passar por valor (default) seria assim:

Sub Change(ByVal x As Integer())
    // faz algo
End Sub 

E por referência:

Sub Change(ByRef x As Integer())
    // faz algo
End Sub 


Obs. 1: Não conheço muito a sintaxe do VB, se alguém detectar um erro notifique-me por favor.
Obs. 2: Não entrei em detalhes como o de objetos distribuídos, em que o objeto é serializado e enviado uma cópia de um espaço de memória para outro (ex.: de uma JVM para outra). Isto é uma condição excepcional.
Obs. 3: Relacionado à obs 2, é por isso que usamos interfaces locais sempre que possível, e interfaces remotas só quando for estritamente necessário (EJB, etc).


Resumindo, Java passa por valor, não por referência. 


Este assunto é complicado de explicar, confuso e difícil de entender geralmente, mas seu conhecimento é um fundamento.

Dúvidas, críticas, correções, sugestões, fiquem a vontade.


E, falando em referências, leitura recomendada:

23 de abr. de 2010

Java Memory Architecture

Salve

Falando um pouco sobre detalhes de baixo nível em Java, é bastante curioso o modo como a memória é gerenciada no Java, e neste pequeno texto vou expor um pouco do que sei a respeito.


Coleção de Lixo

Java é uma linguagem orientada a objetos (não totalmente, existem tipos primitivos em Java) que utiliza gerenciamento automático de memória, um recurso conhecido como Coleção de Lixo (Garbage Collection). Isto quer dizer, que não é necessário desalocar o recurso manualmente, como uma referência para um objeto, eliminando boa parte do trabalho que isto exige (que diga os desenvolvedores C/C++).

Entretanto, não entenda que é desnecessário preocupar-se com o gerenciamento de memória, porque É!
Vários problemas podem acontecer devido ao descuido e desenvolvimento "despreocupado" de alguns programadores, como o OutOfMemoryError (OOME) eStackOverflow. Entre os OOME presenciamos com frequência o PemGen Space.



Algoritmos de Coleta de Lixo

Não é somente o Java que usa o mecanismo de coleta, outras linguagens e plataformas como .NET, Ruby, etc, também utilizam a mesma técnica. As preocupações mais relevantes são:

  • Quantos objetos podem recolhidos e quanta memória pode ser desalocada a cada coleta: Existe um intervalo de tempo para o coletor de lixo agir, e cada ação é importante que ela seja eficiente;
  • Tempo necessário para coleta: Enquanto o coletor de lixo está agindo, alguns bloqueio de memória são impostos e recursos como o processador são muito utilizados, podendo fazer até a aplicação para de responder por um pequeno intervalo de tempo;
  • Aproveitamento do multiprocessamento: A coleta de lixo pode tirar proveito do paralelismo (SMP) disponível em servidores, clusters (né Guilherme?) e até estações de trabalho e outras plataformas com mais de um núcleo de processamento.

Existem várias técnicas para gerenciar a memória usando a coleta de lixo, entre elas:

- Mark and Sweep: Os objetos referenciados são marcados em uma primeira passada e em outra passada subsequente todos os objetos não marcados são recolhidos. Este tipo de coleta demanda muito processamento, e por isso é conhecido como um algoritmo Stop The World (para o mundo), causando algunsSlow Downs (lentidões na resposta da aplicação).

- Reference Counting: Neste caso a alocação de objetos é monitorada e as referências, como o próprio nome diz, são contadas. Objetos sem referências, dado um intervalo de tempo, são coletados.

- Generational Copying: Este último é o mais empregado, e em uso nas implementações atuais da JVM. O espaço de memória é divido em gerações jovem (young generation) e velha (old generation). A maioria dos objetos tem vida curta, por exemplo, quando são instanciados no corpo de um método, então, por padrão, os objetos são alocados em um espaço de memória menor (young generation), que é limpo sempre que alcança o limite e os objetos sem referência são desalocados, e os sobreviventes, teoricamente são os de vida longa e são movidos para um espaço maior, onde a coleta é menos freqüente (old generation).



Arquitetura da memória na Java

A plataforma Java mantém dois espaços bem claros para alocação, a pilha (stack), onde ficam as variáveis, métodos e o carregador de classes (Class Loader), e o heap, onde vivem os objetos.

+----------+ +--------------------------------+
|          | |                                |
|  PILHA   | |             HEAP               |
|          | |                                |
+----------+ +--------------------------------+

O Heap é divido em três espaços bem definidos:

+----------+---------------------+------------+
|  YOUNG   |        OLD          | PERMANENT  |
|GENERATION|     GENERATION      | GENERATION |
|          |                     |            |
+----------+---------------------+------------+

Os objetos recém criados são armazenados na Young Generation (chamado de Eden na implementação HotSpot), onde são feitas coletas mais frequentes, chamadas de Minor Collects

Os objetos de vida longa são movidos para Old Generation, onde as coletas são menos frequentes, mas que liberam mais memória sempre que são realizadas, pois ali residem objetos maiores, sendo chamadas de Major Collects

O espaço de geração permanente é responsável por manter o pool de Strings (instâncias de Strings são reusadas, fica para outro tópico) e classes (Classes são objetos).



Parâmetros da JVM e Tunning

Para alterar o espaço reservado para alocação de memória é comum usarmos parâmetros como os que seguem abaixo:

java aplicacao -Xms768M -Xmx1024M

Quer dizer que inicialmente 768 Mega Bytes serão reservados para a JVM, e que poderá expandir até 1 Giga Byte. Note que este espaço máximo é da Young + Old Generation e não inclui o Permanent Generation:




+----------+--------------------+------------+
|  YOUNG   |        OLD         | PERMANENT  |
|GENERATION|     GENERATION     | GENERATION |
|          |                    |            |
+----------+--------------------+------------+

|--------  1024 MB -------------|

Para alterar o tamanho máximo da Permanent Generation é necessário adicionar o parâmetro -XX:MaxPermSize=128M (exemplo para 128MB). Logo o parâmetro abaixo usaria 1,5GB da memória disponível:

java aplicacao -Xms768M -Xmx1024M -XX:MaxPermSize=512M




+----------+--------------------+------------+
|  YOUNG   |        OLD         | PERMANENT  |
|GENERATION|     GENERATION     | GENERATION |
|          |                    |            |
+----------+--------------------+------------+

|--------  1024 MB -------------|-- 512MB ---|
|------------ 1536MB (1.5GB) ----------------|


Para otimizar a coleta, é possível alterar o algoritmo usado para a coleta. Por padrão, a Máquina Virtual usa o Serial Collector, que é bem eficiente, mas consiste em uma thread em background, responsável pela coleta, o que é bom para uma ambiente com poucos processadores disponíveis, mas que deixa de aproveitar o multiprocessamento disponível em alguns ambientes. Para habilitá-lo explicitamente é usado o seguinte parâmetro:

java aplicacao -XX:-UseSerialGC

Para ambientes multiprocessados, é interessante usar outra estratégia, que tire proveito do paralelismo. O algoritmo Parallel Garbage Collector faz isso, e para habilitá-lo, parametrize da seguinte maneira:

java aplicacao -XX:-UseParallelGC

ParallelGC consegue fazer vários Minor Collects em paralelo, sendo especialmente otimizado para plataformas multiprocessadas e aplicações com grande utilização de objetos de vida curta (pequeno escopo).

Outro algoritmo, que causa menos slow downs é o Concurrent Mark and Sweep. É especialmente interessante para Major Collects, usado em máquina com multiprocessamento e grande volume de memória disponível. Para usar:

java aplicacao -XX:-UseConcMarkSweep

Note, para acompanhar as coleções realizadas adicione o parâmetro -verbose:gc

A saída é algo como:




[GC 25066K->24710K(62848K), 0.0850880 secs]
[GC 28201K(62848K), 0.0079745 secs]
[GC 39883K->31344K(62848K), 0.0949824 secs]
[GC 46580K->37787K(62848K), 0.0950039 secs]
[Full GC 53044K->9816K(62848K), 0.1182727 secs]
....

É possível ver os Minor Collects e os Major Collects agindo, a memória desalocada e o tempo despendido em cada um.



Mais informações

Uma lista completa de parâmetros que podem ser usados para tunning está aqui:

Ótima leitura:

Recentemente a SUN está trabalhando em um algoritmo experimental chamado Garbage First (G1) que divide a memória em áreas menores e promete reduzir o tempo de coleta, além de possuir várias opções de parametrização:

Acho que já é suficiente para uma boa leitura. Abraço a todos, e podem comentar, criticar e perguntas a vontade.

20 de abr. de 2010

Programação por intenção

A pouco criei um artiguinho sobre programação por intenção, na íntegra aqui:

Artigo: programação por intenção

What a Hell is SOA?

Arquitetura do ponto de vista de um desenvolvedor ...

Introdução

É realmente complicado tentar definir o que é SOA, mas comecemos pelo significado do acrônimo: Service Oriented Architecture ou em português Arquitetura Orientada a Serviços.

Com exceção da palavra "Orientada" as outras são muito difíceis de definir, ambas são termos sobrecarregados. 

Como definimos arquitetura? O que é serviço?

Arquitetura

Existem várias definições para arquitetura, nota-se que ela é muito subjetiva e está ligada ao ponto de vista, ao papel, por exemplo um desenvolvedor não tem a mesma visão de arquitetura que o gerente de projetos, e este por sua vez não tem a mesma visão que o cliente. Então, talvez alguns pequenos pontos ajudem a esclarecer.

Arquitetura é sobre:

  • A organização da estrutura de componentes significantes, que interagem através de interfaces, no mais alto nível de um sistema e seu ambiente.

  • Coisas importantes e difíceis de mudar no futuro. Tomar decisões sobre estas coisas é definir a arquitetura.

  • Requisitos não funcionais: segurança, escalabilidade, confiabilidade, etc.

  • Todo sistema envolve uma série de componentes maiores construídos sobre componentes menores. A equipe de desenvolvimento tem um entendimento compartilhado de como estes componentes interagem. Este entendimento pode ser chamado de arquitetura.

Serviço

Michaelis sobre serviço:
serviço
ser.vi.ço
sm (lat servitiu) 1 Ato ou efeito de servir.
Não há consenso sobre o que serviço significa, mas sabemos que ele deve estar disponível para atender quem queira consumi-lo. Mais provavelmente, um serviço deve ser o encapsulamento das regras de negócio, sem lógica de apresentação, auto contido podendo retornar a informação necessária em uma única chamada, sem estado, usável por diferentes aplicações e independente de localização.

Service Oriented Architecture

Para cada pessoa, ou papel desempenhado por ela, SOA pode significar algo diferente. Para alguns SOA é sobre expor a aplicação através de Web Services, para outros é apenas utilizar XML e para mais alguns SOA é a mesma arquitetura de sempre (Same Old Architecture) e nada muito diferente de CORBA, RMI ou DCOM.

Se para cada pessoa, a visão de arquitetura e de serviço é subjetiva, então, como fica a Arquitetura Orientada a Serviços?


Minha visão como desenvolvedor

Bem, o que eu vejo é uma ênfase, impulsionada pela dinâmica dos negócios da atualidade, focada na descentralização, no reuso, na independência, na facilidade de interligação da aplicação e do intercâmbio das informações com aplicações legadas ou mais modernas.

A todo momento empresas são compradas ou cooperam com outras, formam um conglomerado, etc, e faz-se necessária a exposição da informação e dos processos de negócio.

A justificativa para SOA não é muita nova, me parece a mesma que impulsionou outros paradigmas e tecnologias como:

  • Linguagens orientadas a objetos, que aproxima o modelo do código à realidade;
  • Desenvolvimento com componentes, que encapsula lógica do negócio em um bloco de classes autocontidas;
  • Componentes distribuídos e chamada de procedimento remoto (RPC): busca da interoperabilidade e intercâmbio de informações, impulsionado por CORBA e COM+;

Então, onde entra a Arquitetura Orientada a Serviços? 

Usar uma linguagem orientada a objetos, distribuir a aplicação em componentes expostos através de chamadas remotas já não é isto?

Não.

Isto pode ser feito com Java EE e componentes EJB que usam RMI sobre IIOP ou Microsoft .NET com .net Remoting sobre HTTP ou TCP e SOAP binário (e é até relativamente fácil de fazer), mas ... -pequena pausa- ...  SOA é um problema de arquitetura (por isso o 'A'), não de implementação.

Obs.: até mesmo Web Services são fáceis de declarar, em plataformas como Java EE e .NET basta um metadado (anotação). Mas, outra vez, repita comigo, "não é uma implementação é uma arquitetura".


Orientando a Serviços

A solução SOA envolve geralmente um protocolo de comunicação e formato de mensagens conhecidos e populares, como TCP/HTTP/SOAP e XML/JSON, e possui características como requisições assíncronas e sem estado.

Pontualmente para orientar a arquitetura da aplicação por serviços, entre outros problemas de design, é necessário:

  • Definir a política do serviço;
  • Definir como o serviço será exposto, o contrato do serviço;
  • Definir como os clientes se conectarão ao serviço;
  • Definir o formato das mensagens trocadas entre provedor e consumidor do serviço;

Entre outros pontos relevantes, alguns são:

  • Como serão feitas transações?
  • Como o cliente obterá resposta do serviço?
  • Como os clientes descobrirão o serviço?

Um serviço deve ser atômico e auto contido, alguns para leitura de informações, outros para gravação e outros mais complexos para Workflow. 

A ausência de estado (statelessness) é um requisito desejado, sendo que a escalabilidade é um fator que pesa muito na arquitetura, sendo um dos requisitos não-funcionais mais importantes.

Resumindo, um serviço, que representa uma atividade de negócio, deve permitir que qualquer cliente se conecte a ele, seja este cliente a camada de negócios de outra aplicação ou um frontend remoto.

É importante que não exista a dependência de tecnologias específicas. O consumidor é ignorante de como o provedor processa o serviço e vice-e-versa. A interoperabilidade deve ser dependente do formato da mensagem e tipo de protocolo, mais nada.

Note que a idéia de distribuir um serviço implica em mais algumas preocupações, dado que existem fatores que podem influenciar no seu funcionamento como:

  • Problemas de comunicação: atraso no recebimento da requisição ou envio da resposta, ou mesmo o não recebimento;
  • Problemas de validação: pode ocorrer o caso de o processo ser inválido dada uma regra do negócio e uma estratégia para o tratamento de exceções deve ser formulada;
  • Problemas de autorização: definir quem acessará e o que é extremamente importante. Rastrear as alterações também é.
  • Problemas de dependência e transações: no caso de o serviço ser dependente ou ter dependências de outros serviços. Como serão gerenciadas? E se um deles falhar?


E a implementação?

A maior parte dos problemas é promessa de solução da maioria dos ESB's disponíveis, entretanto, há que se avaliar.

Estou desenvolvendo um estudo de caso (sem ESB) e em outro tópico lanço para discussão.

Referências

Boa leitura, recomendo:
Who Needs an Architect?
http://martinfowler.com/ieeeSoftware/whoNeedsArchitect.pdf

Ótimo livro sobre arquitetura corporativa:
Patterns of Enterprise Application Architecture
http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420

Embora não fale especificamente de SOA, fala muito, e bem, de integração de sistemas corporativos:
Enterprise Integration Patterns
http://www.amazon.com/Enterprise-Integration-Patterns-Designing-Deploying/dp/0321200683/ref=pd_sim_b_1