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:
-
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. -
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. -
Data Store
Holds, organizes, and provides editing facilities for data that supports the application.
- Multiple ViewsOrders, inventory, customers, schedule, ...
- More than one "Business Case"Managing inventory, processing orders, billing, ...
- Multiple sources of dataOrders db, shippers web service, distributers inventory, ...
-
Define abstractions - models - for each of the three layers
-
View Model
Provides classes with public properties, designed to support binding to UI controls. These classes gather data from the application by interacting with application services and they support notification and enumeration protocols needed by the view's controls. This keeps the view simple and independent of other views. Each view simply depends on the view model for support.
-
Application (Business) Model
Provides classes that model the business or computation required for the application. These should represent things familiar to the user of the system, e.g., customers, orders, etc. The states of these objects depend upon the data layer to support persistence. When created they initialize by making queries into the data layer. When disposed they serialize their state back to the data store, using services provided by the data layer.
-
Data Model
The data layer provides a set of objects that wrap db tables, or stored procedures, or XML files. They handle connection management and the create, read, update, and delete (CRUD) operations on the stored data. This is essentially an object to relational mapping. Several frameworks have been created to support this, e.g., Hibernate, Entity Framework, LinqToSql, ...
-
View Model
-
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.
-
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. -
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.
Conclusions:
Loosely-Coupled Systems depend on the implementation of two simple ideas:
- Require all communication between presentation, application, and data layers to use interfaces, creating the objects that implement the interfaces with object factories.
- 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.