A reference architecture (part 9)

…continued from part 8

Throwing business faults

In the previous blog posts I set up a working solution, but I didn’t take into account that sometimes things can go wrong. And it is reality that things most definitely will go wrong! That means that during our business logic, we need a way to throw business exceptions.

In the ArchitectureExample.Service.Shared project, add the following classes:

[AttributeUsage(AttributeTargets.Class)]
public class GlobalExceptionHandlerBehaviourAttribute : Attribute, IServiceBehavior
{
private readonly IErrorHandler _errorHandler;

public GlobalExceptionHandlerBehaviourAttribute()
{
// Although we should always use constructor injection, in this case we have no choice to use ObjectFactory directly
// because we cannot change the default constructor as it is used as an attribute.
_errorHandler = ObjectFactory.GetInstance<IErrorHandler>();
}

public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase) { }
public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters) { }

public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase dispatcherBase in serviceHostBase.ChannelDispatchers)
{
var channelDispatcher = dispatcherBase as ChannelDispatcher;
if (channelDispatcher != null) channelDispatcher.ErrorHandlers.Add(_errorHandler);
}
}
}

public class BusinessException : Exception
{
public string ErrorCode { get; set; }

public BusinessException(string errorCode, string message)
: base(message)
{
ErrorCode = errorCode;
}
}

[DataContract]
public class BusinessFault
{
[DataMember]
public string ErrorCode { get; set; }
[DataMember]
public string Message { get; set; }

public BusinessFault(string errorCode, string message)
{
ErrorCode = errorCode;
Message = message;
}
}

public class GlobalExceptionHandler : IErrorHandler
{
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
MessageFault msgFault;
if (error is BusinessException)
{
var businessError = error as BusinessException;
var businessFault = new BusinessFault(businessError.ErrorCode, businessError.Message);
var faultException = new FaultException<BusinessFault>(businessFault, "Oops", new FaultCode("BusinessFault"));
msgFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, msgFault, error.TargetSite.Name);
}
else
{
FaultException faultException = new FaultException(string.Format("{0}", error.TargetSite.Name));
msgFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, msgFault, faultException.Action);
}
}

public bool HandleError(Exception error)
{
return true;
}
}

Then decorate the service implementation in the ArchitectureExample.Service.Adapter project with the GlobalExceptionHandlerBehaviour attribute, for example:

[GlobalExceptionHandlerBehaviour]
public class ArchitectureExampleService : IArchitectureExampleService
{
}

Also decorate each service operation with the [FaultContract] attribute in the IArchitectureExampleService interface in the ArchitectureExample.Service.Contracts project:

[ServiceContract]
public interface IArchitectureExampleService
{
[OperationContract]
[FaultContract(typeof(BusinessFault))]
AddBlogEntryResponse AddBlogEntry(AddBlogEntryRequest request);

[OperationContract]
[FaultContract(typeof(BusinessFault))]
AddCommentToBlogEntryResponse AddCommentToBlogEntry(AddCommentToBlogEntryRequest request);

[OperationContract]
[FaultContract(typeof(BusinessFault))]
void ModifyBlogEntry(ModifyBlogEntryRequest request);

[OperationContract]
[FaultContract(typeof(BusinessFault))]
void PublishBlogEntry(PublishBlogEntryRequest request);

[OperationContract]
[FaultContract(typeof(BusinessFault))]
void UnpublishBlogEntry(UnpublishBlogEntryRequest request);

[OperationContract]
[FaultContract(typeof(BusinessFault))]
FindAllBlogEntriesResponse FindAllBlogEntries();

[OperationContract]
[FaultContract(typeof(BusinessFault))]
FindBlogEntryDetailsResponse FindBlogEntryDetails(FindBlogEntryDetailsRequest request);
}

Then add the following registration in the StructureMapRegistry in the ArchitectureExample.Service.WebHost project:

For<IErrorHandler>().Use<GlobalExceptionHandler>();

If you now throw a BusinessException somewhere in the service code, the global exception handler will automatically convert the business exception to a BusinessFault and throw it so that service consumers can catch it.

To quickly test this, throw a business exception in the AddBlogEntryRequestHandler:

public class AddBlogEntryRequestHandler : IRequestHandler<AddBlogEntryRequest, AddBlogEntryResponse>
{
private readonly IUnitOfWork _unitOfWork;
private readonly IBlogEntryRepository _blogEntryRepository;

public AddBlogEntryRequestHandler(IUnitOfWork unitOfWork, IBlogEntryRepository blogEntryRepository)
{
_unitOfWork = unitOfWork;
_blogEntryRepository = blogEntryRepository;
}

public AddBlogEntryResponse Execute(AddBlogEntryRequest request)
{
throw new BusinessException("FISH", "Something fishy happened!");
var blogEntry = new BlogEntry(request.Title, request.Body);
_blogEntryRepository.Add(blogEntry);
_unitOfWork.Commit();
return new AddBlogEntryResponse(blogEntry.Id);
}
}

And add a new test:

[TestMethod]
public void AddBlogEntryWithFaultTest()
{
string errorCode = null, faultMessage = null;
using (var architectureExampleServiceAgent = new ArchitectureExampleServiceAgent("BasicHttpBinding_IArchitectureExampleService"))
{
try
{
var response = architectureExampleServiceAgent.AddBlogEntry(new AddBlogEntryRequest()
{
Title = "Blog entry title",
Body = "Blog entry body"
});
}
catch (FaultException<BusinessFault> fex)
{
errorCode = fex.Detail.ErrorCode;
faultMessage = fex.Detail.Message;
}
}
Assert.IsNotNull(errorCode);
Assert.IsNotNull(faultMessage);
}

Next time we’ll handle validation of requests and domain entities.

Advertisements

A reference architecture (part 8)

…continued from part 7

The query side

So now the command side works, but what about the query side? As you know, queries bypass the application and domain logic; they just query a data source for information directly and expose it in the desired format. This means that those service operations don’t use the request handler dispatcher to dispatch requests to the correct handler, but instead use the repositories directly.

To clearly separate commands from queries, it’s probably cleaner to split commands and queries in two separate services: a command service and a query service; especialliy if both sides use their own database; but in our case – for simplicity – we will put them all in the same service.

As mentioned earlier we need two queries: FindAllBlogEntries and FindBlogEntryById. As these query operations don’t use the application layer, we have to define their dto’s in the service layer; so add a class library project called ArchitectureExample.Service.Dtos to the Service Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p8_1

Then add the following classes to this project:

[DataContract]
public class BlogEntryDetails
{
    public BlogEntryDetails() { }
    public BlogEntryDetails(int blogEntryId, string title, string body)
    {
        BlogEntryId = blogEntryId;
        Title = title;
        Body = body;
    }

    [DataMember]
    public int BlogEntryId { get; set; }

    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Body { get; set; }

    [DataMember]
    public List<CommentDetails> Comments { get; set; }
}


[DataContract]
public class BlogEntrySummary
{
    public BlogEntrySummary() {}
    public BlogEntrySummary(int blogEntryId, string title, string body)
    {
        BlogEntryId = blogEntryId;
        Title = title;
        Body = body;
    }

    [DataMember]
    public int BlogEntryId { get; set; }

    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Body { get; set; }
}


[DataContract]
public class CommentDetails
{
    public CommentDetails() {}
    public CommentDetails(int commentId, string name, string emailAddress, string commentText)
    {
        CommentId = commentId;
        Name = name;
        EmailAddress = emailAddress;
        CommentText = commentText;
    }

    [DataMember]
    public int CommentId { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public string EmailAddress { get; set; }

    [DataMember]
    public string CommentText { get; set; }
}


[DataContract]
public class FindAllBlogEntriesResponse
{
    public FindAllBlogEntriesResponse() {}
    public FindAllBlogEntriesResponse(List<BlogEntrySummary> blogEntrySummaries)
    {
        BlogEntrySummaries = blogEntrySummaries;
    }

    [DataMember]
    public List<BlogEntrySummary> BlogEntrySummaries { get; set; }
}

[DataContract]
public class FindBlogEntryDetailsRequest
{
    public FindBlogEntryDetailsRequest() {}
    public FindBlogEntryDetailsRequest(int blogEntryId)
    {
        BlogEntryId = blogEntryId;
    }

    [DataMember]
    public int BlogEntryId { get; set; }
}


[DataContract]
public class FindBlogEntryDetailsResponse
{
    public FindBlogEntryDetailsResponse() {}
    public FindBlogEntryDetailsResponse(BlogEntryDetails blogEntryDetails)
    {
        BlogEntryDetails = blogEntryDetails;
    }

    [DataMember]
    public BlogEntryDetails BlogEntryDetails { get; set; }
}

 

Then add the service operations to the IArchitectureExampleService interface of the ArchitectureExample.Service.Contracts project:

[OperationContract]
FindAllBlogEntriesResponse FindAllBlogEntries();

[OperationContract]
FindBlogEntryDetailsResponse FindBlogEntryDetails(FindBlogEntryDetailsRequest request);

 

And implement them in the ArchitectureExampleService of the ArchitectureExample.Service.Adapter project:

public class ArchitectureExampleService : IArchitectureExampleService
{
    private readonly IHandlerDispatcher _handlerDispatcher;
    private readonly IBlogEntryRepository _blogEntryRepository;

    public ArchitectureExampleService(IHandlerDispatcher handlerDispatcher, IBlogEntryRepository blogEntryRepository)
    {
        _handlerDispatcher = handlerDispatcher;
        _blogEntryRepository = blogEntryRepository;
    }

    public FindAllBlogEntriesResponse FindAllBlogEntries()
    {
        return new FindAllBlogEntriesResponse(_blogEntryRepository.BlogEntries.ToList().Select(be => new BlogEntrySummary(be.Id, be.Title, be.Body)).ToList());
    }

    public FindBlogEntryDetailsResponse FindBlogEntryDetails(FindBlogEntryDetailsRequest request)
    {
        var blogEntryDetails = _blogEntryRepository.BlogEntries.SingleOrDefault(be => be.Id == request.BlogEntryId);
        return new FindBlogEntryDetailsResponse(blogEntryDetails!=null ? new BlogEntryDetails(blogEntryDetails.Id, blogEntryDetails.Title, blogEntryDetails.Body) : null);
    }
}

 

And also the service agent:

public class ArchitectureExampleServiceAgent : ServiceProxyBase<IArchitectureExampleService>, IArchitectureExampleService
{    
    public FindAllBlogEntriesResponse FindAllBlogEntries()
    {
        return Channel.FindAllBlogEntries();
    }

    public FindBlogEntryDetailsResponse FindBlogEntryDetails(FindBlogEntryDetailsRequest request)
    {
        return Channel.FindBlogEntryDetails(request);
    }
}

 

And finally add a test:

[TestMethod]
public void FindAllBlogEntriesTest()
{
    using (var architectureExampleServiceAgent = new ArchitectureExampleServiceAgent("BasicHttpBinding_IArchitectureExampleService"))
    {
        var response = architectureExampleServiceAgent.FindAllBlogEntries();

        Assert.IsTrue(response.BlogEntrySummaries.Count > 0);
    }
}

 

Next time we will implement a way to automatically throw business faults by the service if an exception occurs.

A reference architecture (part 7)

…continued from part 6

The infrastructure layer (Tests)

By now we have setup all layers and finally we can write some integration tests to see if everything works.

Note that we will just create some simple tests that calls the service, checks if it returns something and allows you to debug it, as testing is not the scope of this series.

Add a test project called ArchitectureExample.Infrastructure.IntegrationTests to the Infrastructure Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p7_1

Add a ArchitectureExampleServiceTests test file and the following test:

[TestMethod]
public void AddBlogEntryTest()
{
using (var architectureExampleServiceAgent = new ArchitectureExampleServiceAgent("BasicHttpBinding_IArchitectureExampleService"))
{
var response = architectureExampleServiceAgent.AddBlogEntry(new AddBlogEntryRequest()
{
Title = "Blog entry title",
Body = "Blog entry body"
});
Assert.IsTrue(response.BlogEntryId>0);

}
}

Before you can execute this, add an app.config to the ArchitectureExample.Infrastructure.IntegrationTests project:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IArchitectureExampleService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:82/ArchitectureExampleService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IArchitectureExampleService"
contract="ArchitectureExample.Service.Contracts.IArchitectureExampleService" name="BasicHttpBinding_IArchitectureExampleService" />
</client>
</system.serviceModel>

</configuration>

Also, in IIS create a new web site that points to \Trunk\Sources\ArchitectureExample.Service.WebHost at port 82.

Now you can start the test and verify if the blog entry is successfully added in the database.

Next time we will implement the query side.

A reference architecture (part 6)

…continued from part 5

The service layer

Now it’s time to expose the application layer to the outside world.

Add a new class library project called ArchitectureExample.Service.Contracts to the Service Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p6_1

Add the service interface IArchitectureExampleService:

[ServiceContract]
public interface IArchitectureExampleService
{
[OperationContract]
AddBlogEntryResponse AddBlogEntry(AddBlogEntryRequest request);

[OperationContract]
AddCommentToBlogEntryResponse AddCommentToBlogEntry(AddCommentToBlogEntryRequest request);

[OperationContract]
void ModifyBlogEntry(ModifyBlogEntryRequest request);

[OperationContract]
void PublishBlogEntry(PublishBlogEntryRequest request);

[OperationContract]
void UnpublishBlogEntry(UnpublishBlogEntryRequest request);
}

Add a new class library project called ArchitectureExample.Service.Adapter to the Service Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p6_2

Add the service implementation:

public class ArchitectureExampleService : IArchitectureExampleService
{
public AddBlogEntryResponse AddBlogEntry(AddBlogEntryRequest request)
{
throw new NotImplementedException();
}

public AddCommentToBlogEntryResponse AddCommentToBlogEntry(AddCommentToBlogEntryRequest request)
{
throw new NotImplementedException();
}

public void ModifyBlogEntry(ModifyBlogEntryRequest request)
{
throw new NotImplementedException();
}

public void PublishBlogEntry(PublishBlogEntryRequest request)
{
throw new NotImplementedException();
}

public void UnpublishBlogEntry(UnpublishBlogEntryRequest request)
{
throw new NotImplementedException();
}
}

Before we can delegate the requests to the application layer, we need a mechanism to automatically execute the correct request handler, based on request/response objects. To do that, we will create a request handler dispatcher.

First add a new class library project called ArchitectureExample.Service.Shared to the Service Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p6_3

Add the interface IHandlerDispatcher:

public interface IHandlerDispatcher
{
TResponse Execute<TRequest, TResponse>(TRequest request, bool asynchronously)
where TRequest : IRequest, new()
where TResponse : IResponse, new();
void Execute<TRequest>(TRequest request, bool asynchronously) where TRequest : IRequest, new();
}

And also its implementation:

public class HandlerDispatcher : IHandlerDispatcher
{
public TResponse Execute<TRequest, TResponse>(TRequest request, bool asynchronously)
where TRequest : IRequest, new()
where TResponse : IResponse, new()
{
if (asynchronously) Task.Factory.StartNew(() => Execute<TRequest, TResponse>(request, false));

Type openType = typeof(IRequestHandler<,>); //generic open type
var type = openType.MakeGenericType(typeof(TRequest), typeof(TResponse)); // type is your runtime type
var requestHandler = ObjectFactory.GetInstance(type);
var methodInfo = type.GetMethod("Execute");
try
{
return (TResponse)methodInfo.Invoke(requestHandler, new[] { (object)request });
}
catch (Exception ex)
{
// Rethrow the inner exception instead of the invocation exception
throw ex.InnerException;
}
}

public void Execute<TRequest>(TRequest request, bool asynchronously)
where TRequest : IRequest, new()
{
if (asynchronously) Task.Factory.StartNew(() => Execute<TRequest>(request, false));

Type openType = typeof(IRequestHandler<>); //generic open type
var type = openType.MakeGenericType(typeof(TRequest)); // type is your runtime type
var requestHandler = ObjectFactory.GetInstance(type);
var methodInfo = type.GetMethod("Execute");
try
{
methodInfo.Invoke(requestHandler, new[] { (object)request });
}
catch (Exception ex)
{
// Rethrow the inner exception instead of the invocation exception
throw ex.InnerException;
}
}
}

As you see we use StructureMap’s ObjectFactory to get the correct instance of the request handler, so download StructureMap, put the StructureMap.dll assembly in the \Trunk\Libraries\StructureMap folder and add a reference to it from the ArchitectureExample.Service.Shared project (or install the nuget package).

Now we can implement the ArchitectureExampleService in the ArchitectureExample.Service.Adapter project:

public class ArchitectureExampleService : IArchitectureExampleService
{
private readonly IHandlerDispatcher _handlerDispatcher;

public ArchitectureExampleService(IHandlerDispatcher handlerDispatcher)
{
_handlerDispatcher = handlerDispatcher;
}

public AddBlogEntryResponse AddBlogEntry(AddBlogEntryRequest request)
{
return _handlerDispatcher.Execute<AddBlogEntryRequest, AddBlogEntryResponse>(request, false);
}

public AddCommentToBlogEntryResponse AddCommentToBlogEntry(AddCommentToBlogEntryRequest request)
{
return _handlerDispatcher.Execute<AddCommentToBlogEntryRequest, AddCommentToBlogEntryResponse>(request, false);
}

public void ModifyBlogEntry(ModifyBlogEntryRequest request)
{
_handlerDispatcher.Execute<ModifyBlogEntryRequest>(request, false);
}

public void PublishBlogEntry(PublishBlogEntryRequest request)
{
_handlerDispatcher.Execute<PublishBlogEntryRequest>(request, false);
}

public void UnpublishBlogEntry(UnpublishBlogEntryRequest request)
{
_handlerDispatcher.Execute<UnpublishBlogEntryRequest>(request, false);
}
}

Now, based on the request/response, the dispatcher calls the correct operation in the application layer.

We also need something to host the service, so add a new WCF service application project called ArchitectureExample.Service.WebHost to the Service Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p6_4

In the project delete all files except web.config. Add a new file called ArchitectureExampleService.svc:

<%@ ServiceHost Language="C#" Debug="true" Service="ArchitectureExample.Service.Adapter.ArchitectureExampleService" Factory="ArchitectureExample.Service.WebHost.StructureMapServiceHostFactory" %>

Add following classes to ArchitectureExample.Service.Shared project, which will allow the service to work nicely together with StructureMap:

public class StructureMapInstanceProvider : IInstanceProvider
{
private readonly Type _serviceType;

public StructureMapInstanceProvider(Type serviceType)
{
_serviceType = serviceType;
}

public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}

public object GetInstance(InstanceContext instanceContext, Message message)
{
return ObjectFactory.GetInstance(_serviceType);
}

public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}

public class StructureMapServiceBehavior : IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}

public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
// For all of the endpoints in all of the channels, we need to give WCF the StructureMap instance provider.
// Also, since StructureMap needs to know what requestType to create, we have to pass that information along to our instance provider.
foreach (var endpointDispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>().SelectMany(channelDispatcher => channelDispatcher.Endpoints))
{
endpointDispatcher.DispatchRuntime.InstanceProvider = new StructureMapInstanceProvider(serviceDescription.ServiceType);
}
}
}

public class StructureMapServiceHost : ServiceHost
{
public StructureMapServiceHost()
{
}
public StructureMapServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void OnOpening()
{
Description.Behaviors.Add(new StructureMapServiceBehavior());
base.OnOpening();
}
}

public abstract class BaseStructureMapServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new StructureMapServiceHost(serviceType, baseAddresses);
}
}

Then add following classes to ArchitectureExample.Service.WebHost project, so that the required dependencies are registered correctly:

public class StructureMapRegistry : Registry
{
public StructureMapRegistry()
{
For<IArchitectureExampleService>().Use<ArchitectureExampleService>();
For<IHandlerDispatcher>().Use<HandlerDispatcher>();

Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.AssemblyContainingType<AddBlogEntryRequestHandler>();
scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>));
scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<>));
scanner.WithDefaultConventions().OnAddedPluginTypes(apt => apt.HybridHttpOrThreadLocalScoped());
});

For<IDatabaseFactory<EfArchitectureExampleDbContext>>().HybridHttpOrThreadLocalScoped().Use<DatabaseFactory<EfArchitectureExampleDbContext>>();
For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<UnitOfWork>();
For<IBlogEntryRepository>().HybridHttpOrThreadLocalScoped().Use<BlogEntryRepository>();
}
}

public class StructureMapBootstrapper : IBootstrapper
{
public void BootstrapStructureMap()
{
ObjectFactory.Initialize(x =>
x.Scan(scanner =>
{
// Include the following assemblies in the scanning process and look for registries
scanner.TheCallingAssembly();
scanner.AssemblyContainingType<StructureMapRegistry>();
scanner.LookForRegistries();
}));
}

public static void Bootstrap()
{
new StructureMapBootstrapper().BootstrapStructureMap();
}
}

public class StructureMapServiceHostFactory : BaseStructureMapServiceHostFactory
{
public StructureMapServiceHostFactory()
{
// Put bootstrapper initialization here
StructureMapBootstrapper.Bootstrap();
}
}

Configure the service in the web.config file:

<?xml version="1.0"?>
<configuration>

<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>

<connectionStrings>
<add name="EfArchitectureExampleDbContext" connectionString="Data Source=.;Initial Catalog=ArchitectureExample;Persist Security Info=True;User ID=architectureexample;Password=architectureexample" providerName="System.Data.SqlClient"/>
</connectionStrings>

<system.serviceModel>

<services>
<service behaviorConfiguration="serviceBehavior" name="ArchitectureExample.Service.Adapter.ArchitectureExampleService">
<endpoint address="" binding="basicHttpBinding" contract="ArchitectureExample.Service.Contracts.IArchitectureExampleService" bindingConfiguration="basicHttpBinding">
<identity>
<dns value="" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>

<bindings>
<basicHttpBinding>
<binding name="basicHttpBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
</security>
</binding>
</basicHttpBinding>
</bindings>

<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>

<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>

<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

</configuration>

Almost there… Only thing left is the service agent. The service agent abstracts the service usage for the consumer of the service, which makes it easier to use; but it also shields other concerns like logging and caching.

Add a new class library project called ArchitectureExample.Service.Agent to the Service Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p6_5

First add the following class to the ArchitectureExample.Service.Shared project:

public class ServiceProxyBase<T> : IDisposable where T : class
{
private readonly string _serviceEndpointName;
private readonly object _sync = new object();
private ChannelFactory<T> _channelFactory;
private T _channel;
private bool _disposed = false;

protected ServiceProxyBase(string serviceEndpointName)
{
_serviceEndpointName = serviceEndpointName;
}

protected T Channel
{
get { EnsureChannel(); return _channel; }
}

public void EnsureChannel()
{
if (_disposed) throw new ObjectDisposedException("ServiceProxyBase<" + typeof(T) + "> has been disposed");
lock (_sync)
{
if (_channel != null) return;
_channelFactory = new ChannelFactory<T>(_serviceEndpointName);
//_channel = _channelFactory.CreateChannel(new EndpointAddress(_serviceEndpointUri));
_channel = _channelFactory.CreateChannel();
}
}

~ServiceProxyBase()
{
Dispose(false);
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposeManaged)
{
if (_disposed) return;
if (disposeManaged)
{
DisposeChannel();
}
_disposed = true;
}

private void DisposeChannel()
{
if (_channel == null) return;
lock (_sync)
{
if (_channel == null) return;
var communicationObject = (object)_channel as ICommunicationObject;
if (communicationObject == null) return;
var success = false;
try
{
if (communicationObject.State != CommunicationState.Faulted)
{
communicationObject.Close();
success = true;
}
}
finally
{
if (!success) { communicationObject.Abort(); }
_channel = default(T);
}
}
}
}

And then add the service agent to the the ArchitectureExample.Service.Agent project:

public class ArchitectureExampleServiceAgent : ServiceProxyBase<IArchitectureExampleService>, IArchitectureExampleService
{
public ArchitectureExampleServiceAgent(string serviceEndpointName)
: base(serviceEndpointName)
{
}

public AddBlogEntryResponse AddBlogEntry(AddBlogEntryRequest request)
{
return Channel.AddBlogEntry(request);
}

public AddCommentToBlogEntryResponse AddCommentToBlogEntry(AddCommentToBlogEntryRequest request)
{
return Channel.AddCommentToBlogEntry(request);
}

public void ModifyBlogEntry(ModifyBlogEntryRequest request)
{
Channel.ModifyBlogEntry(request);
}

public void PublishBlogEntry(PublishBlogEntryRequest request)
{
Channel.PublishBlogEntry(request);
}

public void UnpublishBlogEntry(UnpublishBlogEntryRequest request)
{
Channel.UnpublishBlogEntry(request);
}
}

The service agent also implements the service interface; which means that if you change the service or add operations, you must also change the service agent so that service and agent are kept in sync.

That completes the service layer, that now looks like:

p6_6

Next time we will write a test to see if everything works!

A reference architecture (part 5)

…continued from part 4

The infrastructure layer (Data Access)

The next thing to do is to build the concrete repository implementation for the repository interfaces that we defined in the domain layer.

Add a class library project called ArchitectureExample.Infrastructure.DataAccess to the Infrastructure Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p5_1

In this project we will implement the IBlogEntryRepository interface and use Entity Framework ‘code first’ to map the database entities to the domain entities, and also create a UnitOfWork based on the Entity Framework context.

We will also need a project to put shared stuff in, so add a class library project called ArchitectureExample.Infrastructure.Shared to the Infrastructure Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p5_2

As we will use Entity Framework 4, download it, put the EntityFramework.dll assembly in the \Trunk\Libraries\Entity Framework folder and add a reference to it from the ArchitectureExample.Infrastructure.DataAccess and ArchitectureExample.Infrastructure.Shared projects. (Optionally you can of course also get the nuget package.)

Let’s first put some base classes in place needed by the data access layer. Add the following classes to ArchitectureExample.Infrastructure.Shared:

public abstract class DisposableObject : IDisposable
{
public bool Disposed { get; private set; }

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

~DisposableObject()
{
Debug.Assert(Disposed, "WARNING: Object finalized without being disposed!");
Dispose(false);
}

private void Dispose(bool disposing)
{
if (!Disposed)
{
if (disposing)
{
DisposeManagedResources();
}

DisposeUnmanagedResources();
Disposed = true;
}
}

protected virtual void DisposeManagedResources() { }
protected virtual void DisposeUnmanagedResources() { }
}

public interface IDatabaseFactory<TContext> : IDisposable where TContext : DbContext, new()
{
TContext Get();
}

public class DatabaseFactory<TContext> : DisposableObject, IDatabaseFactory<TContext> where TContext : DbContext, new()
{
private TContext _dataContext;
public TContext Get()
{
return _dataContext ?? (_dataContext = new TContext());
}

protected override void DisposeManagedResources()
{
if (_dataContext != null)
{
_dataContext.Dispose();
_dataContext = null;
}
}
}

public abstract class RepositoryBase<TContext, TEntity> : IRepository<TEntity>
where TContext : DbContext, new()
where TEntity : class
{
private TContext _dataContext;

protected TContext DataContext
{
get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
}

protected readonly IDbSet<TEntity> dbset;

protected RepositoryBase(IDatabaseFactory<TContext> databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<TEntity>();
}

protected IDatabaseFactory<TContext> DatabaseFactory { get; private set; }

public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> filter)
{
return dbset.Where(filter);
}

public virtual void Add(TEntity entity)
{
dbset.Add(entity);
}

public virtual void Update(TEntity entity)
{
dbset.Attach(entity);
DataContext.Entry(entity).State = EntityState.Modified;
}

public virtual void Delete(TEntity entity)
{
dbset.Remove(entity);
}

public virtual void Delete(Expression<Func<TEntity, bool>> where)
{
IEnumerable<TEntity> objects = dbset.Where<TEntity>(where).AsEnumerable();
foreach (TEntity obj in objects)
dbset.Remove(obj);
}

public IEnumerable<TEntity> QueryObjectGraph(Expression<Func<TEntity, bool>> filter, string children)
{
return dbset.Include(children).Where(filter);
}
}

Next, create the Sql Server database called ArchitectureExample, and add the tables BlogEntry and Comment:

CREATE TABLE [dbo].[BlogEntry](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](200) NOT NULL,
[Body] [nvarchar](2000) NOT NULL,
[LastPublishDate] [datetime] NULL,
[LastModifiedDate] [datetime] NOT NULL,
CONSTRAINT [PK_BlogEntry] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Comment](
[Id] [int] IDENTITY(1,1) NOT NULL,
[BlogEntryId] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[EmailAddress] [nvarchar](200) NOT NULL,
[CommentText] [nvarchar](1000) NOT NULL,
[DateWritten] [datetime] NOT NULL,
CONSTRAINT [PK_Comment] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Comment] WITH CHECK ADD CONSTRAINT [FK_Comment_BlogEntry] FOREIGN KEY([BlogEntryId])
REFERENCES [dbo].[BlogEntry] ([Id])
GO

ALTER TABLE [dbo].[Comment] CHECK CONSTRAINT [FK_Comment_BlogEntry]
GO

Also create a user ‘ArchitectureExample’  that has sufficient access rights to this database.

Next add the following classes to the ArchitectureExample.Infrastructure.DataAccess project:

public class EfArchitectureExampleDbContext : DbContext, IUnitOfWork
{
public DbSet<BlogEntry> BlogEntries { get; set; }
public DbSet<Comment> Comments { get; set; }

public void Commit()
{
base.SaveChanges();
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BlogEntry>().ToTable("BlogEntry");
modelBuilder.Entity<Comment>().ToTable("Comment");
}
}

public class UnitOfWork : DisposableObject, IUnitOfWork
{
private IDatabaseFactory<EfArchitectureExampleDbContext> _databaseFactory;
private EfArchitectureExampleDbContext _dataContext;

public UnitOfWork(IDatabaseFactory<EfArchitectureExampleDbContext> databaseFactory)
{
_databaseFactory = databaseFactory;
}

public EfArchitectureExampleDbContext DataContext
{
get { return _dataContext ?? (_dataContext = _databaseFactory.Get()); }
}

public void Commit()
{
DataContext.Commit();
}

protected override void DisposeManagedResources()
{
if (_databaseFactory != null)
{
_databaseFactory.Dispose();
_databaseFactory = null;
}
if (_dataContext != null)
{
_dataContext.Dispose();
_dataContext = null;
}
}
}

public class BlogEntryRepository : RepositoryBase<EfArchitectureExampleDbContext, BlogEntry>, IBlogEntryRepository
{
public BlogEntryRepository(IDatabaseFactory<EfArchitectureExampleDbContext> databaseFactory) : base(databaseFactory)
{
}

public IQueryable<BlogEntry> BlogEntries
{
get { return DataContext.BlogEntries; }
}

public IQueryable<Comment> Comments
{
get { return DataContext.Comments; }
}

public void Dispose()
{
DataContext.Dispose();
}
}

At this time we also completed the data access component, and the infrastructure layer should look like:

p5_3

Next time we will continue with the service layer.