Here you can see that our missing logic layer has made a triumphant return! This might seem subtle but I think it completely changes class responsibilities and how your developers perceive the system. The logic in the Web Application is responsible for deciding when to go to a web service, or when to use a local cache, how to co-ordinate calls between different resources (you might have multiple web services!), when to enforce security or other policy, and so on. This then leaves your Resource Access layer able to dedicate itself to providing a real shield from the inner workings of the service calls.
As I’m sure regular readers have noticed, I’m a big fan of talking in concrete examples, so let’s see some (simplified) code that demonstrates this approach. Firstly, the Presenter and implied View;
public class MyPresenter<IMyView>
{
private ILogicService _logicService;
public MyPresenter([ServiceDependency] ILogicService logicService)
{
_logicService = logicService;
}
public void OnRetrieveOrders()
{
List<OrderEntity> results =
_logicService.FetchOpenOrdersForCurrentUser(
View.StartDate, View.EndDate);
View.Orders = results;
}
}
The key here is that the presenter is using a WCSF service (badly named as ILogicService) to fetch orders for display to the user. These orders are represented by an “OrderEntity” entity. Next, let’s see an example of an ILogicService implementation;
public class LogicService : ILogicService
{
private IAuthenticationService _authenticationService;
private IEntityTranslatorService _entityTranslatorService;
public LogicService(
[ServiceDependency] IAuthenticationService
authenticationService,
[ServiceDependency] IEntityTranslatorService
entityTranslatorService)
{
_authenticationService = authenticationService;
_entityTranslatorService = entityTranslatorService;
}
public List<OrderEntity> FetchOpenOrdersForCurrentUser(
DateTime startDate,
DateTime endDate)
{
OrderServiceProxy proxy = null;
try
{
proxy = new OrderServiceProxy();
proxy.Open();
proxy.LogOnService(CERTIFICATE_DETAILS);
string orderStatusCode =
proxy.GetOrderStatusCode(OrderStatus.Open);
List<Order> orders = proxy.GetOrders(
_authenticationService.CurrentUserName,
orderStatusCode,
startDate,
endDate);
List<OrderEntity> results =
_entityTranslatorService.Translate<Order, OrderEntity>(orders);
proxy.Close();
return results;
}
catch (Exception)
{
if (proxy != null)
proxy.Abort();
throw;
}
}
}
There are a few really important points to make here;
1. I have chosen to abstract environmental issues, such as authentication, away into a framework service. See my previous post on Environment Abstraction for more information on this.
2. All the management of the interaction with the service proxy is kept out of the Presenter, and instead is handled by our WCSF application service.
3. I am performing Entity Translation between the “Order” and “OrderEntity” objects. The former is actually part of the definition of the Order Service’s Service Contract, in that it defines how the remote service is communicated with. The latter is a Business Entity defined by my WCSF module. Therefore it is important that I expose the Business Entity not the proxy communication entity further up my architecture.
4. The service performs some limited Orchestration – it calls a number of methods on the service to achieve what is seen by the Presenter as a single business operation. Obviously I would prefer a very course grained service design, but I’ve done this to illustrate that this is the right place to perform any required orchestration (potentially across multiple services) and workflow.
5. The disposal of the proxy is done correctly. A “using” block is not the best way to deal with WCF proxies – see Avoiding Problems with the Using Statement.
6. This is also the right place for rich error handling, publishing, and compensation – please don’t follow my poor example in this respect J
7. I haven’t used the rich implementation of the Service Interface pattern I would usually, because constructing the messages would increase the volume of sample code and detract from the point. I’ll probably post about Service Interfaces soon, so watch this space!
The final important point is that this separation of business logic, resource access, entity translation, etc is likely to be repeated by the Order Service.
Potential Gotchas
When using WCF proxies there are a number of things worth knowing about. The first is the best way to use a “using” block, mentioned above.
Secondly, remember that a WCSF service is a singleton – so if you cache the WCF proxy you will be using the same one for every call, no matter which end user is browsing your site. No probs... oh, hang on! But what if you’re using a session-full binding? That would mean all your WCF calls would be using the same session, no matter which end user the call was for! This could certainly be a problem – so be very careful about this.
Thirdly, if you are caching your WCF proxy remember that a faulted proxy cannot be used again. So if your backend service fails and then comes back up, and you’re caching the proxy forever and for every user, your web application will be unusable until you cause it to flush the cache (e.g. by restarting the App Pool). The correct way to handle a faulted proxy is by adding the following code when creating it;
proxy = new OrderServiceProxy();
((IOrderServiceChannel)proxy).Faulted +=
new EventHandler(OrderService_Faulted);
In the event handler you can then react in some way – perhaps by Aborting the proxy and replacing it with a fresh one. This is obviously pretty academic for my example code above, but if you are caching proxies, consider this carefully. Also, if you don’t have a business logic layer, you might find it more difficult to deal with this kind of situation!
Finally, I’ve used the class generated by “Add Service Reference” in Visual Studio. It is actually pretty slow to new up one of these for every business operation when compared to using a cached ChannelFactory. In .NET 3.5 this is less true, but I’d recommend this post comparing the two approaches.
Summary
There are many issues I’ve not covered here – security architecture, for example (Trusted Subsystem versus Delegation), and many, many, many, more. But I hope this has gotten you started on how to structure your code, freeing you up to investigate some of the more subtle complexities. Good luck!
And finally... a Summary of Summaries
This is the last post in my series on Web Client Software Factory Application Architecture. That doesn’t mean I’ll not post about WCSF again of course, just that we have now covered plenty, from the basic patterns involved to some more “fluffy” concepts of designing modules or service layers. I hate television series that have no end (wow, it is seriously difficult not to name and shame here, that’s how much I hate it!), so I’ll take my own advice and finish on a high J
Remember how much I haven’t covered – there is plenty of information out there, from the WCSF community site, landing page, other bloggers, obviously in the factory documentation, and so on. And if you’re blogging about it, do let me know!