WCSF Application Architecture 6: Structuring Modules

This article is part of a series;

· WCSF Application Architecture 1: Introduction

· WCSF Application Architecture 2: Application Controller

· WCSF Application Architecture 3: Model View Presenter

· WCSF Application Architecture 4: Environment Abstraction

· WCSF Application Architecture 5: Defining Modules

Introduction

This post is intended to address some common scenarios when using the Web Client Software Factory, in particular relating to how modules should be structured internally, and how they should relate to each other... as with all my other posts, this is based on what I’ve done and what I’ve seen others do – I’m sure there are other ways out there, so please feel free to chip in if you have something to say, suggest, or disagree with.

I’m going to present architectural candidates for you to consider, and discuss why you might choose to use them, which I hope should give you ideas about how they might apply to your situation.

So, get comfortable, and read on!

For consistency with my previous post in this series, I will assume that initially we have some business modules (Customers, Accounts, and Orders) and a foundation module (Infrastructure). I’ll pick and choose from these to illustrate my points as we go.

Model 1: Classic

I’ve named this model “Classic” as it is pretty much the basic use of the WCSF as it was intended;

Note: An arrow indicates a reference – so Customers has a project reference to Customers.Interfaces. The coloured bars indicate responsibilities for each project.

This diagram shows us a number of things;

1. The WCSF provides an out-of-the-box option to define an “interfaces” project for each module, in which the public “contract” of the module is defined. An “Interfaces” project contains contracts (i.e. for services a module exposes) and entities.

2. The Web Site contains the Web Pages and User Controls for a module.

3. The implementation projects (what I call the module projects that are not suffixed with “.interfaces”) contain the full remaining layers of the architectural stack, from UI (presenters etc) through business logic, to resource access.

4. The Web Site may reference both interface and implementation projects.

5. The implementation projects may reference their own or other modules’ “interfaces” projects. This allows modules to take dependencies on services exposed by other modules in a loosely coupled way.

6. As shown by the X marks, modules should not reference each other in any other way. Doing so would cause their implementations to be tightly coupled, and can cause circular references – which would be really bad news.

I’m a big believer in showing some code to demonstrate how this works, so let’s take a simple example and consider the following;

namespace Customers.Interfaces

{

    public class Customer

    {

        public int Id { get; set; }

        public string Name { get; set; }

    }

    public interface ICustomerService

    {

        List<Customer> GetCustomers();

    }

}

namespace Customers

{

    public class CustomerService : ICustomerService

    {

        public List<Customer> GetCustomers()

        {

            // implementation

        }

    }

}

 

Here we see the Customer entity and the ICustomerService contract are defined in the Customers.Interfaces project, and a real implementation of CustomerService in the implementation project. Obviously there would be IView definitions and Presenters in the implementation module too, and ASPX pages etc in the web site, but these are not relevant to this example.

Model 2: Shared Entities

Model 1 is in many ways the ideal – but the problem is life is often more complicated than this! The first thing you’re likely to encounter is when two modules need to share the same entity. So consider what happens if I define the following (slightly contrived) contract in my Orders.Interfaces project;

namespace Orders.Interfaces

{

    public class OrderDetails

    {

        public string OrderReference { get; set; }

    }

    public interface IOrderService

    {

        void PlaceOrder(Customer customer, OrderDetails order);

    }

}

Looks great huh? Ah, but Customer is defined in Customers.Interfaces... I could add a reference from Orders.Interfaces to Customers.Interfaces, but how long would it be before I needed to do the same in reverse and had a circular reference?

This leads me to a piece of advice;

Avoid having references between “.interfaces” projects

Also, remember that in my previous article we discovered that it is good practice to ensure that one and only one module owns access to any particular data source. So we shouldn’t create two different customers entities. So what do we do?

 

 

This diagram has way too many arrows on it to indicate acceptable references, but I wanted to provide a complete view, so I apologise! Basically, then, this boils down to creating a separate shared project in which you put common entities and contracts. Entities that only relate to a single module can stay in that module’s interfaces project.

So in our example, this becomes as follows;

namespace Shared

{

    public class Customer

    {

        public int Id { get; set; }

        public string Name { get; set; }

    }

}

namespace Customers.Interfaces

{

    public interface ICustomerService

    {

        List<Customer> GetCustomers();

    }

}

namespace Customers

{

    public class CustomerService : ICustomerService

    {

        public List<Customer> GetCustomers()

        {

            // implementation

        }

    }

}

namespace Orders.Interfaces

{

    public class OrderDetails

    {

        public string OrderReference { get; set; }

    }

    public interface IOrderService

    {

        void PlaceOrder(Customer customer, OrderDetails order);

    }

}

You see that the Customer entity has moved out, and now we have no circular reference concerns.

Model 3: Layered Modules

I have historically always separated my application layers by project – I find this helps keep the layering clear, and keeps the volume of code in any one project to a manageable level. The WCSF does not do this by default – but it doesn’t prevent you from doing it! In fact, some of the reference implementations split modules in a similar way;

 

This diagram shows that I have added two new standard C# projects to the solution, and named them Customers.Data and Customers.Logic (note I have omitted the Orders functionality here just to keep the diagram simple). My layers are now kept nice and separate. Each layer may reference the layer below, but not vice-versa.

Now there is an interesting point to make from this diagram – my Logic and Data projects have noreference to the Customers.Interfaces project. This means they cannot deal with the “Customer” entity!

I actually think this is a matter of choice – it depends on how much you wish to ensure your layers are loosely coupled. Using the approach I have taken, you would need to use an adapter or entity translation pattern in the Customers implementation project. Here, the UI facing classes (such as presenters or services) would translate between the outward facing entities defined in Customers.Interfaces, and those used internally by the module (perhaps database-focused entities). The services in the Customers module would simply act as a facade to real logic, contained in the Logic project.

The truth is, it can be a bit of a pain taking this approach, so you may wish to compromise – but the point is, it needs a few moments of thought. Choose an approach that you feel comfortable with and stick to it throughout your solution.

Model 4: Split Modules

An alternative approach to bringing extra clarity to your layering is to physically split up your modules;

 

 

Note: I have excluded the Web Site from this diagram, as I’m sure you’re getting the point!

Here you can see that we have Customers and Orders business modules, without matching interfaces projects. These only contain UI code, allowing you to keep the projects really focused. We then have two foundation modules – CustomersLogic and OrdersLogic (not very well named, I admit!). These modules expose services to the UI modules to allow them to access the “model” (as Model-View-Presenter) that is our business logic. The reference between the UI code and the business logic is loosely coupled as it goes via the foundation modules’ interfaces projects. If you really wanted, you could take this one step further and split the foundation modules into more projects as we discussed in the Layered Modules approach.

This is indeed very similar to the Layered Modules approach, except that it gives you an opportunity to deploy business logic separately to your user interface. This can have big benefits for team working (e.g. different teams working on the UI and business logic), or for deployment – perhaps your UI works for a number of systems, with different business logic plugged in behind?

Model 5: Split Master Logic Module

All of these approaches so far have one thing in common; they expect you to be able to vertically partition your code to some greater or lesser extent. In reality, this is the hardest bit of getting your WCSF implementation right – what parts of the system are really independent from each-other?

In particular, if you are migrating an existing system you may well find all of your business logic and data access is so tightly related, interlinked, and interdependent, that you cannot split it into modules without all sorts of circular references, shared entities, and so on. You could try, and end up with spaghetti dependencies between modules – even if it is loosely coupled, too many dependencies on each-other’s services is not a good sign. Or maybe you just happen to be building a small system that only really has one single area of functionality?

So all this means you could well end up with one single module, immediately losing some of the benefits to team working, componentisation, etc, that come with modularisation. Right?

Well, this is where the Split Master Logic Module approach comes in. Split your UI into modules – because that is often pretty easy - and then keep all your back end logic in a single foundation module that exposes services to the rest of your application;

I must make it clear that with this model you are missing out on the benefits of the complete vertical slice through your architecture you would get with a purist approach, but it does still have a place I believe.

This allows you to work in a loosely coupled way, modularising your UI (including independent deployment of modules if you wish), yet maximising the reuse of any existing business logic you have – it could be as simple as surfacing existing code as WCSF application services. You of course still have all the flexibility of a loosely coupled architecture too, so should you wish to migrate your existing code to a new, more modular architecture, just plug-and-play different service implementations as time goes by.

Conclusion

Well, that’s it – a lightning fast tour of some approaches to splitting your modules up. Have I missed an approach? Have you come up with something better? Do you hate one of the approaches above? As always, shout up and let me know.

The one approach I haven’t discussed is using a Service Layer to physically split the UI from a logic tier. Watch this space and I’ll try to post some thoughts on this soon.

Finally, I threw a few comments into the text above that might have got you thinking... if so, check out;

· The Adapter pattern

· Entity Translation – best explained by Don Smith, in context of the Web Service Software Factory but relevant to us too!

· My thoughts on LINQ solution structures (aimed at a WPF app, but some relevance to the web)

Enjoy!