One of the most common initial strategies for selecting the types of object needed in a software system or component is to define a set of logical layers into which we can place candidate classes. There are various layering schemes discussed throughout literature on the subject but they are generally variations on the following four layers:
- Problem Domain (PD)
- User Interface (UI)
- Data Management (DM)
- System Interaction (SI)
This strategy is often called 'separation of concerns' because each class is concerned with only one aspect of the thing it is modelling. Business classes in the PD layer are not concerned with how to display their objects on screens or paper. That is the concern of the UI layer classes. Nor are they concerned with how their objects are stored in files or databases. That is the concern of the DM layer classes and so on.
Figure 1: Four logical layers of classes with sub-layers as described below
The Problem Domain Layer (PD)
The most important layer of classes is the Problem Domain layer. The problem domain of a piece of software is the field of endeavor, area, sphere of activity, or universe of discourse of the topic we considering or the question we trying to solve, or answer. Therefore, problem domain objects model the concepts, ideas and rules within our software after we strip away the stuff specific to the technologies we are using.
Other literature concerned exclusively with business systems might call this layer the Business or Business Logic (BL) layer because it contains the classes that represent business concepts and enforce business rules and policies. I prefer the term Problem Domain because it is more generic and fits better when developing other kinds of software such as development tools, industrial monitoring and control systems, and educational software for example.
In enterprise-scale or distributed systems the PD layer may be split into two sub-layers: the all critical object layer that defines all the objects that model the problem domain concepts and, surrounding this, a service or application programming interface layer that provides coarse-grain chunks of problem domain functionality suitable for calling across address spaces. Alternatively, the service sub-layer is viewed as part and parcel of the System Integration layer.
For two reasons, the analysis, design and coding of the problem domain layer typically represents a longer-term investment than the other layers.
Firstly it forms the heart of the system. We can have the coolest looking user interface and the most efficient and elegant database solution but without the PD layer we do not have a useful system. Conversely if we have the PD layer in place, any old user interface or database will give us a useful system even if it might not be the easiest or prettiest to use. This is not to belittle the importance of a well-designed user interface or database schema but rather to ensure that the problem domain is given the attention it deserves. Too often, development teams focus too much on creating the user interface and problem domain logic becomes embedded, scattered and duplicated across GUI panels, servlets or web pages. Also, typically, data modellers design a database schema for the system but the code to read and write from the database is then added, scattered and duplicated across the same GUI panels, servlets and web pages. The result for anything other than a trivial system is a maintenance nightmare.
Secondly, not only is the problem domain important because it represents the essence of the system, it is also important because when modeled well, it is the most stable layer of the system. The fundamental concepts, ideas, and principles of the problem domain change less frequently than the technologies for communicating information to humans, databases or other systems. Separating the more volatile aspects of our system from the more stable helps us localize any effects of changes in those volatile areas.
User Interface Layer (UI)
The User Interface layer contains classes, Java-style interfaces, JSP's, and ASP's, etc., concerned with presenting information to and receiving commands from human beings. It is sometimes called the human interaction(HI) layer or even the Man Machine Interface layer. Human Interaction layer is possibly the preferable term given the rather derogatory sense of the term user in other contexts. However, user interface seems to have become the more widely-accepted term.
In web-based systems, the UI layer is usually split into two sub-layers. The presentation sub-layer deals purely with the presentation of information to a human user. The session sub-layer handles user input, manages session information, and interacts with the PD layer to execute user requests and commands. For example, in the J2EE world, Java servlets often form the session layer handling user requests, invoking problem domain services as a result, and forwarding the results to Java Server Pages (JSP's) that form the presentation layer to format and display of the information. The presentation layer forms the view part of the classical Model-View-Controller pattern, and has components on both the server and the client device that is usually running some form of web browser. The session sub-layer contains the controller and model logic of the classical Model-View-Controller pattern.
PC-based or more traditional fat-client software generally use a variation of the famous Model-View-Controller pattern too. However, in these cases, the boundary between views and controllers is typically not quite so well-defined. Traditionally view classes display information andcontroller classes handle user actions but in most Graphical User Interface tool kits including Java's Swing and AWT, the view and controller responsibilities are often combined within a single class. These classes typically represent user interface controls like frames, dialogues, panels, labels, fields, buttons, and menus. In other UI toolkits like Apple's iOS UIKit, controllers often have more knowledge of views than they might in a purist implementation of the MVC pattern.
The user interface is frequently the least stable area of a system because user interface requirements tend to change more rapidly than those of any other layer. Fifteen years ago, a green-screen terminal interface was acceptable. Ten years ago, a windowing Graphical User Interface (GUI) was required. Five years ago they wanted to access the system through a web front end. Now they want an user interface specifically for their touch-controlled mobile phones and tablet devices. Tomorrow, they may want it in 3D. Fortunately, if we separate our classes correctly there should be little or no change required in the Problem Domain and other layers to support different user interface technologies and styles. Of course, a new user interface is always a good opportunity for clients to request new features that require enhancements and extensions to the other layers but that is just business as usual.
Data Management Layer (DM)
The Data Management layer contains classes and Java-style interfaces, etc., concerned with storing and retrieving information from files and databases. It is often called the persistence layer instead.
Although it is much more stable than the UI layer, it is a good idea to separate the specifics of data persistence or data management from the problem domain code. Doing this allows the storage approach to change without impacting the problem domain layer. An example might be a decision by an organization to standardize on a particular vendor's RDBMS or a decision to improve performance by storing read only reference data as serialized objects, XML, or property files on a client machine instead of in a central database server, or to introduce in-memory caching for frequently accessed data.
Today none of the mainstream object-oriented programming languages provide sophisticated built in persistence capabilities for the objects of their classes. Instead they rely on services provided by class libraries or application servers to store objects on secondary storage in a variety of ways.
Most nontrivial applications will use some form of database but mapping objects to an RDBMS is a nontrivial task. Although there are also a number of object-oriented databases on the market most have not reached mainstream acceptance in enterprise scale systems. Most traditional RDBMS vendors now offer both object-oriented and XML-based storage features alongside the traditional relational model but these are not standardised and therefore hinder moving a system to another vendor.
There are a number of products that help map Java or .Net objects to a relational database such as the ever-popular Hibernate toolkits. These tools can greatly simplify the Data Management layer to the point where it can be generated very quickly from problem domain classes.
System Interface Layer (SI)
The system interface layer contains classes and Java-style interfaces, etc., that deal with communicating with external systems. In other words it defines the objects needed to exchange messages and data with other systems that are generally outside the scope of our control. These might be other systems within our organization, the systems of suppliers or customers, or web services. The classes of this layer typically send, receive, encode or decode network messages and temporary data files.
In enterprise systems this layer is frequently called the integration layer and contains classes that act as clients for the services defined in the service sub-layer of the PD layer.
Also sometimes called the System Interaction or External Interface layer, the SI layer is more stable than the user interface layer but still may be subject to change. For example, a programme of replacing legacy systems over a number of years might require a number of changes to System Interface layer classes over that period of time. Also, external systems may vary across deployment sites requiring slightly different SI layers depending on location.
Interactions and Dependencies Between Layers
The classes in each layer interact with the classes in other layers for well-defined reasons. For example:
- The UI layer interacts with the PD layer to populate its views and allow users to perform their jobs.
- The PD is responsible for keeping the UI layer up-to-date with any changes that occur to business objects.
- The UI layer should not access either the DM or SI layers directly because this bypasses the problem domain rules and logic in the PD layer.
- The PD layer uses the DM layer services to store and retrieve objects from disk.
- It is unusual for the DM layer to initiate changes in PD objects.
- The PD layer uses the SI layer services to send/receive data from external systems.
- The SI layer often informs the PD layer of changes at external systems.
- Interactions between PD and SI can be done in real-time or in batch update mode.
Ideally we want to protect our problem domain layer from the effects of changes in the other three layers. We might even want to replace one of the other layers completely and it would be nice to be able to do so without disturbing the problem domain code. Therefore, from a dependency point of view, we want the UI, DM, and SI layers to be dependent on the PD layer but not have any dependencies in the other direction.
However, as we can see from the list above, the PD layer needs to use the services of the other layers to display information to users, to store and retrieve data from a database, and to send messages to other systems. This would seem to imply a dependency from PD to UI, SI and DM. Fortunately, there are numerous good design patterns for resolving this apparent conflict in requirements.
Another reason for separating our classes into these layers is that these layers form natural boundaries for sub-teams within larger development teams.
The skills required to design and implement the classes are different for each layer and so are the standard libraries of classes used. The size and complexity of the Java class libraries has grown enormously since Java burst into mainstream development in the late 1990's especially if we consider the Java 2 Enterprise Edition (J2EE). Splitting developers according to logical layer means each developer can concentrate and specialize in the in-depth knowledge and use of a more manageable number of class libraries. For example, in the Java world:
- The UI layer team needs to know the user interface tool kit being
used. For a Java application that might mean
knowing the Java Foundation Classes (JFC), Servlets, Java Server Pages
(JSP) and Java Server Faces (JSF) libraries. In the .Net world we have
Windows Presentation Foundation (WPF), Web Forms, ASP.Net MVC,
Silverlight, etc.,. Apple developers
are faced with various MVC and Web frameworks for OS-X and iOS. All
good user interface development idioms, like performing actions on
mouse button release (or end of a touch) instead of mouse button press
(or touch start).
- The DM layer team needs to be intimate with the chosen database,
object to relational mapping tools or file system
being used. In Java this might include knowing Hibernate, JDBC, Java
Persistence API (JPA) and persistence of Enterprise Java Beans (EJB)'s.
In .Net, it is ADO.net, Entity Framework, NHibernate, etc. For the
Apple world, its CoreData, SQLLite, etc. Then there is understanding
SQL, query optimization, database indexes, transaction isolation
levels, normalization, etc.
- The SI development team needs an understanding of the
communication protocols and middleware products in use and
for Java teams that can mean the Java web service API's and networking
classes, Java Messaging System (JMS) library, the Message Bean flavor
of EJB's and possibly JavaMail libraries among others. In the .Net
world we have Microsoft Windows Communication Framework (WCF). In all
worlds, XML, XML Schemas and WSDL
knowledge is frequently required, as well as paradigms like RESTful
services WS* specifcations.
- The PD team often needs little of the technical knowledge required by the other layers' teams but needs to understand the business rules and logic. In a distributed object system written in Java the PD team would also need to know about either Java's remote method invocation (RMI) capabilities, EJB's, or Java's support for CORBA standards.
Smaller development teams may not be big enough to organize in this way. The developers on these teams are required to be 'jacks of all trades' and know as much as possible about all the technology components being used. Because of this, it can useful to assign certain developers to be the team's guru for the class libraries and API's of a particular layer so that there is someone knowledgeable to ask for each layer.