Thursday, 19 January 2012

Enterprise Application Integration: Part 3

Click here to download the code from Codeplex. Please make sure you click on 'Code for Part 3'. This example cannot be run using SQL Server Express, it must be run on SQL Server. Please run the script in the SQL Scripts solution folder to create the required databases (even if you have done this for the last part).

General Refactoring

Database Scripts

The databases scripts were creating the database files to a specified directory. This has been rectified.

Response Objects

I now no longer favour the generic Response objects returned by the Application Layer. The need to cast this to the actual type adds complexity for the consumer. The point was to return other types in various circumstances (ie. validation failures or if an entity is not found). Instead, Fault Exceptions will handle these circumstances (although I do wonder whether a failed validation is really exceptional?). I still favour Data Transfer Objects.

On some projects, I always have a request object, even if it only has one property. The advantage of this is that if a new version of the service is released, it will be backwardly compatible with existing consumers of the service.

WCF Layer

The Remote Façade Layer has been renamed the WCF layer, because this is more meaningful, and it is not truly an implementation of the Remote Façade pattern. I am wondering if this layer is truly needed - the Application layer is already polluted with WCF related stuff.

Logging and Infrastructure Layer

I have added in some logging. This is in a new layer - the Infrastructure layer. This is not yet perfected - if the Domain needed to log events, it would need a dependency on the Infrastructure layer, violating a principle of the Domain Model Pattern. Each service operation logs the content of the request, and errors are also logged. The logging still relies on SQL - I may change this in future to harness NHibernate, to keep it consistent with the rest of the project.

Note that depending on which binding you are using, you can't always use Application_Error in Global.asax, so instead a Behaviour Attribute is added to each service to log errors. Hopefully, I will figure out a way to log service calls with attributes, removing the need to add _log.Add() at the start of each service, forming a type of AOP. Also note that setting up behaviour extensions in web.config is affected by a bug that has existed since the dawn of WCF and has not yet been fixed.

Also, Application_EndRequest does not work for some bindings, so I have moved this to the end of each service call. Hopefully I will be able to fix this with a Behaviour Attribute.

Integration Refactoring

One thing I haven't been happy with when using XML or WCF Web Services in the past is the point-to-point connections between applications, which makes the applications tightly coupled. This leads to many design problems, including entities being duplicated between systems - like the Site entity in the account system.

I have been reading up on SOA, and while I don't claim to be building an SOA here, I am borrowing some concepts. One borrowed concept is the composite service. In this system, some composite services consume the services from the Client Data System and the Accounts System, and any integration is performed in the composite services. This removes the reference from the Accounts System to the Client Data System, removes the need for Service Gateways, removes the need for any Client Data System entities or properties from the Accounts System, removes the need for the consequent mapping files, and removes the need for the views in the database. The two systems have become completely decoupled.

The only connection that remains are foreign keys in the Accounts System to the Client data System (for example - Site ID in the Account entity). I read somewhere even this is frowned upon, but I'm not at all sure how this would work? I'll leave this for now.

There is some business logic in the composite services> I'm not sure this is the best place for it, and may review it later. Some validation is occurring in the composite services too, and again I will review this. For example, the Account composite service checks if the client is established before calling the Account Service to create it - an alternative would be to pass the Client entity regardless to the Account Service and let it decide whether to create the account?

The validation is all a bit spread out - maybe this could go in the UI.

Another bonus is that I have avoided replicating the FullName business logic.

The Contact entity may appear to be replicated in the Accounts System, but in fact this only holds information about a contact that is specific to the Accounts System, and does not replicate any properties from the Client data System (except the ID to tie them together).

No comments:

Post a Comment