Em uma arquitetura de software bem definida, a exposição da lógica de negócio é feita através da implementação da camada de Serviços (Service Layer).
O objetivo desta camada é assegurar, de forma coerente e robusta, as chamadas de outras partes do sistema, expondo suas operações (métodos) através de uma interface, que define o contrato com as aplicações clientes que farão as chamadas ao serviço de diversas formas de comunicação (diferentes protocolos. Ex.: TCP, SOAP).
A tecnologia WCF (Windows Communication Foundation) é uma das implementações que a Microsoft disponibiliza para construir serviços de forma transparente, através da configuração de múltiplos canais de comunicação ao serviço. A configuração de serviço WCF é simples, porém sua criação pode não ser fácil, pois um serviço pode conter múltiplas dependências, como: outros serviços, objetos de acesso a dados (DAO – Data Access Objet).
Portanto, como garantir uma configuração do serviço WCF e suas dependências?
O uso do pattern Dependeny Injection e do conceito de arquitetura Inversion of Control (IoC) faz com que a construção em tempo de desenvolvimento e execução seja um trabalho mais simples.
O framework Spring.NET disponibiliza um container IoC para configurar todas as dependências do serviço WCF através da configuração XML.
Contruindo o Serviço
Para exemplificar a ideia acima, criarei um exemplo com uma arquitetura cliente/servidor, no qual terei um projeto chamado Server, onde estarão as classes do serviço e suas dependências. E outro projeto chamado Client, onde estarão as classes para realizar a chamada do serviço. Para a implementação do serviço será utilizada a tecnologia WCF versão 3.5, juntamente com o framework Spring.NET versão 1.3.
No projeto Server, criarei o serviço IDummyService e sua implementação DummyService, com uma dependência à classe IDummyDAO, que terá a implementação DummyDAO:
namespace Server.WCF
{
[ServiceContract]
public interface IDummyService
{
[OperationContract]
void SayHello(string who);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class DummyService : IDummyService
{
public IDummyDAO DummyDAO { get; set; }
public void SayHello(string who)
{
Console.WriteLine(string.Format("Hello, {0}", who));
DummyDAO.MakePersistence(who);
}
}
}
namespace Server.DAO.Impl
{
public class DummyDAO : IDummyDAO
{
public void MakePersistence(string who)
{
Console.WriteLine(who + " saved!");
}
}
}
namespace Server.DAO
{
public interface IDummyDAO
{
void MakePersistence(string who);
}
}
Após a construção, vou configurar o serviço WCF no arquivo App.Config da seguinte forma:
<configuration>
<appSettings />
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="server-binding" transferMode="Buffered">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="dummyService">
<endpoint address="net.tcp://localhost:8732/dummy/tcp" binding="netTcpBinding"
bindingConfiguration="server-binding" name="tcp" contract="Server.WCF.IDummyService" />
</service>
</services>
</system.serviceModel>
</configuration>
Até o momento, a implementação do serviço WCF foi realizada, porém a configuração de sua dependência com o recurso IDummyDAO não foi disponibilizada. Com a utilização do framework Spring.NET a configuração desta dependência pode ser feita da seguinte forma:
<objects xmlns="http://www.springframework.net">
<object id="dummyService" singleton="false" type="Server.WCF.DummyService, Server">
<property name="DummyDAO" ref="dummyDAO" />
</object>
<object id="dummyDAO" type="Server.DAO.Impl.DummyDAO, Server" />
<object id="dummyServiceHost" type="Spring.ServiceModel.Activation.ServiceHostFactoryObject, Spring.Services">
<property name="TargetName" value="dummyService" />
</object>
</objects>
Note, que a criaçao do serviço é feita através do elemento dummyServiceHost, que será disponibilizado através da factory (Spring.ServiceModel.Activation.ServiceHostFactoryObject), que criará objetos do tipo SpringServiceHost (implementação do framework Spring.NET), respeitando a interface ServiceHost da tecnologia WCF. A configuração básica do serviço é realizada através do elemento dummyService, definido como singleton=false para interagir de forma correta com a tecnologia WCF, no qual sua dependência é configurada pela propriedade DummyDAO, referenciada pelo elemento dummyDAO (veja o código da classe DummyService acima). Para habilitar esta configuração, basta adicionar ao arquivo App.Config da aplicação o framework Spring.NET da seguinte forma:
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<appSettings />
<spring>
<context>
<resource uri="assembly://Server/Server/Server-Context.xml" />
</context>
</spring>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="server-binding" transferMode="Buffered">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="dummyService">
<endpoint address="net.tcp://localhost:8732/dummy/tcp" binding="netTcpBinding"
bindingConfiguration="server-binding" name="tcp" contract="Server.WCF.IDummyService" />
</service>
</services>
</system.serviceModel>
</configuration>
Chamando o Serviço
Após a construção e configuração do serviço, vou adicionar ao projeto Client a configuração de chamada do serviço WCF via Spring.NET. Portanto, vou criar o arquivo Client-Context.xml que terá a configuração para chamada do serviço WCF:
<objects xmlns="http://www.springframework.net">
<object id="dummyServiceClient" type="Server.WCF.IDummyService, Server"
factory-object="serverAppChannelFactory" factory-method="CreateChannel" />
<object id="serverAppChannelFactory"
type="System.ServiceModel.ChannelFactory<Server.WCF.IDummyService>, System.ServiceModel">
<constructor-arg name="endpointConfigurationName"
value="serverAppDummyServiceEndpoint" />
</object>
</objects>
O elemento dummyServiceClient define o proxy a ser criado pela factory serverAppChannelFactory, definida pela classe ChannelFactory da tecnologia WCF, no qual referencia o endpoint configurado no arquivo App.Config.
A configuração acima pode ser entendida conforme o código de chamada de um serviço WCF:
return new ChannelFactory<IDummyService>("serverAppDummyServiceEndpoint").CreateChannel();
No arquivo App.Config do projeto Client, vou adicionar o endpoint (endereço de onde está publicado o serviço) e a configuração do framework Spring.NET da mesma forma que fiz no projeto Server, apontando para o arquivo de contexto do cliente:
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<appSettings />
<spring>
<context>
<resource uri="assembly://Client/Client/Client-Context.xml" />
</context>
</spring>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="server-binding" transferMode="Buffered">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:8732/dummy/tcp"
binding="netTcpBinding" bindingConfiguration="server-binding"
contract="Server.WCF.IDummyService" name="serverAppDummyServiceEndpoint"/>
</client>
</system.serviceModel>
</configuration>
Para este exemplo, o projeto Client foi criado como Console Application, no qual utilizarei a classe Program para realizar a chamada ao serviço via Spring:
class Program
{
static void Main(string[] args)
{
try
{
IApplicationContext context = ContextRegistry.GetContext();
IDummyService dummyService = (IDummyService)context["dummyServiceClient"];
dummyService.SayHello("User (" + DateTime.Now.Ticks + ")");
}
catch (Exception cause)
{
Console.WriteLine(cause);
}
}
}
A partir do contexto criado pelo framework Spring, recupero o objeto dummyServiceClient, que respeita a interface IDummyService, para fazer chamadas ao serviço remoto.
Conclusão
Construir e configurar serviços com o framework Spring.NET é uma tarefa simples. O uso de outros paradigmas como AOP (Aspect Oriented-Programming) a serviços WCF podem ser úteis para assegurar o ciclo de vida das operações a cada chamada. Porém, alguns cuidados devem ser tomados:
- Ao usar AOP com Spring.NET aos serviços WCF faz com que os objetos recebam uma camada de proxy a mais do que a criada pela tecnologia WCF, ocasionando em overhead de chamadas no ciclo de uma operação, ou seja, perda de performance da aplicação.
- A configuração de serviços WCF, de forma programática, via Spring é limitada apenas a configuração via arquivo XML, ou seja, não se pode criar serviços singleton utilizando a classe SpringServiceHost, pois objetos associados ao framework Spring já são desta natureza, o que causa um problema na interação entre o framework com a tecnologia WCF.
