T B H P N

Note - Loosely-Coupled Systems

The only constant thing about software during its development is change. Changes occur because: it is being created, we find latent errors in already developed parts, there are performance issues, or customers drive changes in the specifications or their interpretation. It is critically important that each change has a low probability of causing other conseqential changes. We build in robustness against change by partitioning a system into relatively small cohesive components that provide stable interfaces and object factories for the rest of the system to use. Part of the art of building robustness into systems is to partition into a structure that is suited to its application and each part is loosely coupled to the others.
It is common to think of a system as composed of a view, the application intelligence, and a data store. This is the basis for the three-tier model:
  1. View
    Presents application information to the user augmented with visual styles and provides controls for the user to adjust the view and send requests to the application.
  2. Application
    Is responsible for supporting all the activities of the application in response to user requests, perhaps based on "Business Logic", and drawing data from the data store.
  3. Data Store
    Holds, organizes, and provides editing facilities for data that supports the application.
Many of the Graphical User Interface (GUI) frameworks - WinForms, WPF, Asp.Net, gtk, etc - encourage you to tightly bind the view to the application processing and datastore. They do this through wizards, hundreds of examples, and a lot of documentation.

For simple applications that have a single view, simple logic, and one source of data, that's quick, easy, and perfectly reasonable implementation approach. But, many applications need:

  1. Multiple Views
    Orders, inventory, customers, schedule, ...
  2. More than one "Business Case"
    Managing inventory, processing orders, billing, ...
  3. Multiple sources of data
    Orders db, shippers web service, distributers inventory, ...
For these more complex situations it becomes important to avoid tight bindings between layers of the system. We want to be able to add a new view that simply uses services defined by the application layer. We want to add additional business processing by simply adding a new service that uses existing services provided by the data layer. And we don't want the new business process to need to know the implementation details of the data service.

We achieve this by a few simple but powerful ideas:

V-VM-C-AM-DM
  1. Define abstractions - models - for each of the three layers
  2. Use Dependency Injection to avoid concrete couplings between layers

    Dependency Injection is a simple extension of the Dependency Inversion Principle (DIP) that is used to build components. DIP says that clients should not couple to servers. Instead the clients use interfaces implemented by the servers. That way they both depend on the contract specified by the interface, not on implementation details. One more thing is required by DIP - the clients are not allowed to directly create concrete server objects. So DIP requires the use of an Object Factory to support the server abstraction.

    Dependency Injection goes just a little further with the object factory - it provides a container to hold and manage created instances, perhaps using an object pool, and supports the registration of new interfaces and their concrete implementations. This makes the server's functionality extensible via plugin components. The Unity Framework, part of the Microsoft Patterns & Practices material on unity.codeplex.com is an example of the genre.

  3. Service Interfaces
    The service interfaces may appear at each layer, determined by the complexity of the application. So, the view uses service interfaces provided by the View Model and bound to internal VM objects. Similarly, the VM uses service interfaces provided by the Data Model bound to data model objects.
  4. Object Factories
    The View Model objects that implement its interfaces are created by the view through simple calls to the VM container. The Data Model objects that implement its interfaces are created by the View Model by calling into the data model container. This avoids reintroducing concrete bindings between the layers by letting the Object Factory handle those concrete bindings, keeping clients bound only to the service abstraction.
There are several patterns that arrange these ideas in slightly different ways:
  1. Model View Controller MVC
  2. Model View ViewModel MV VM
  3. Presentation Model PM
The Model View Controller, in its original form, used the controller primarily to dispatch event actions in the view to handlers in the model. In Frameworks like WinForms and WPF that dispatching happens in the view in a framework specific way. We, however, can simply delegate calls in the event handlers to the viewmodel using its service interfaces.

Conclusions:

Loosely-Coupled Systems depend on the implementation of two simple ideas:
  1. Require all communication between presentation, application, and data layers to use interfaces, creating the objects that implement the interfaces with object factories.
  2. Represent each layer with a model - View Model (VM), Application Model (AM), and Data Model (DM). The application model is the core of the system, using objects that are familiar to users, making this most complex part, easier to maintain. The view model handles the transformations between the states of business objects and data in the view. The data model abstracts away, for the business model, all of the storage details, e.g., connections strings, stored procedures, etc.
The trick, of course, is not to let the infrastructure of these models and dependency injection run away with the implementation. That's easier to say than to do. The (in)famous J2EE does all of these things and has the reputation of being big, bloated, and viciously hard to learn to use well.

The .Net community is trying to do these same things in a more piece-meal fashion (a good thing, I think) with the Entity Framework for Object Relational Modeling (ORM) and the Unity Framework to handle Dependency Injections.

I like to think that we can simplify this significantly. We will try some experiments in Projects #4 and #5 to see if we can indeed do that!