Session E-LAYDesign Pattern: LayersSteven Black
|
Layer | Role |
---|---|
Application |
Provides services to user. Examples include telnet, TCP, HTTP. |
Presentation |
Structures information and attaches semantics. |
Session |
Provides dialog control and synchronization facilities. |
Transport |
Responsible for segmenting long messages into packets. Recovers lost packets with acknowledgments and retransmissions. Flow control. Congestion control. Breaks messages into packets and guarantees delivery. |
Network |
Responsible for routing packets from source to destination host. Selects a route from sender to receiver. |
Data Link |
Responsible for moving packet from one node (host or packet switch) to next node. Error detection and correction. Medium access. |
Physical |
The physical layer, the lowest layer in the OSI stack, is responsible for moving information between two systems connected by a single physical link. The physical layer provides the abstraction of bit transport, independent of the link technology. Specifies voltage levels, bit spacings. |
The OSI 7-layer model is a cool example because it neatly shows the general types of services required for computers to talk to each other. The Internet protocol stack is a refinement of the OSI 7-layer model, minus the Presentation and Session layers, whose services are either not needed or abstracted into the neighboring layers.
Note that each of the layers in the OSI stack don’t necessarily function on distinct hardware or memory space. For example, it’s common to find the Data Link and Physical layers tightly coupled and interleaved (for performance reasons) within the same Ethernet network interface card.
A large system requires decomposition. One way to decompose a system is to segment it into collaborating objects. In large systems a first-cut rough model might produce hundreds or thousands of potential objects. Additional refactoring typically leads to object groupings that provide related types of services. When these groups are properly segmented, and their interfaces consolidated, the result is a layered architecture.
|
Class: Layer J Responsibility: Provides services used by Layer J+1 and delegates subtasks to Layer J-1 Collaborator: Layer J-1 |
Here are some typical interactions in layered architectures.
Messages that percolate downwards between layers are called Requests. For example, a client issues a request to Layer J. What Layer J cannot fulfill, it delegates to Layer J-1. Note that Layer J often translates requests from Layer J+1 into several requests to Layer J-1.
Messages that percolate upward between layers are called Notifications. A notification could start at layer J where, for example, an observer object detects an observable event. Layer J then formulates and sends a message (notification) to Layer J+1.
Layers are logical places to keep information caches. Requests that normally travel down through several layers can be cached to improve performance.
A system’s programming interface is often implemented as a layer. Thus if two applications (or inter-application elements) need to communicate, placing the interface responsibilities into dedicated layers can greatly simplify the other application’s layers and, as a bonus, make them more easily reusable.
The principle of separating the user interface from the application proper is old. It is rarely practiced, for all the talk we devote to it. The principle of separating the user interface from the application has the hardest consequences and is hardest to follow consistently. For the most part most applications barely separate the GUI from the application code, though they claim otherwise.
Imagine the application is totally program driven, with the user interface just one driving program. I’m not talking simply of separating the interface code from the application code, I mean separate GUI and application components.
In other words: The GUI is not a part of the application. It is the first client of the application.
A typical VFP application has too much special-purpose code in the GUI. This is mostly a fundamental problem with the IDE: The easiest thing to do, by far, is to put code in those GUI control methods.
Why so much GUI code? One cause of ballooned of GUI code is that it does things that should be done by the “model” – which we’ll define, for now, as simply “another layer somewhere”. Another cause of GUI code bloat is many people tend to embed code to maintain various kinds of integrity among objects. The result of these things is “know it all” controls.
Another mistake many developers fall into is GUI elements that pull the data to display directly in from the domain model and then having the GUI elements update the domain objects on any changes being made. Again, nothing could be easier in VFP!
Possibly better is a system of “naïve controls” that rely on separate Renderer objects to fill them with data from the domain model objects, and to update the domain objects with the changes made by the user.
It is easier on the user if input errors are brought up directly upon entry. Having the UI outside the application separates input from error detection. First the UI will change, so isolate and make an interface to it. Then someone will remove the human from the picture entirely, with electronic interchange or another application driving the program. Therefore, just making an interface to the UI component is not sufficient, it has to be an interface that does not care about the UI.
Therefore, put the UI totally outside the application. The application proper is bounded by a program-driven interface, and the UI is just one user of that interface, perhaps not even the first. Other users of the application could be another controlling application, or a testing application. Once the GUI is separate, almost anything is possible.
If the UI is really outside the application, what about when the user starts and then cancels a modification - where are the editing and rollback copies of the object kept? In the UI, or outside the application? What about typing errors - are they detected in the GUI or inside the application?
Answer: Keep edits in the GUI, of course! The user fumbling around in a GUI is a reality of a GUI. You want idiomatic GUI behavior and effects encapsulated in the GUI. Imagine writing a second program-driven interface, and having to deal with all these messages reminding the automated program in real-time that, say, a field is required. Yech!
Building systems requires us to bring many varied and unrelated concepts together. A typical medium or large sized system might involve diverse concepts like domain functionality, transactions, meta-data, database technology, network communication protocols, OS API calls, a GUI, etc.
Given pressure to quickly produce a reasonably fast system, it’s tempting to tie these concepts closely together and so embed transaction-control code in the GUI code, or OS API code in the business code. This leads systems that are:
Write a layer of software to isolate each disparate concept or technology. These layers should isolate at the conceptual level (perhaps business code really needs to know nothing about the OS API - this is all handled transparently by some object management code) and/or at the technical level (handling unhandled exceptions raised by the object management code so they don't find their way into the business code). Isolation should be two-way (the business code 'knows' nothing of the OS API code and vice-versa).
This leads systems that are:
Of course the Isolation Layer itself may be complex and, possibly, represents a single point of failure. Over-application of the pattern leads to a system where everything is strongly decoupled and so the effects of system events may be unpredicatable and design or change is always 'selfish': distribution is hidden from the business designer and so they design without any thought for distribution - something which could bring the system to its knees.
Discussion: The choice a layered architecture can have many beneficial effects on your application if it is applied in the proper way. First, since the architecture is so simple, it is easy to explain to team members and so demonstrate where each object's role fits into the "big picture". If a designer is very strict about clearly defining where objects fit within the layers, and the interfaces between the layers, then the potential for reuse of many objects in the system can be greatly increased.
A common problem with many object designs is that they are too tightly constrained to the limits of the particular application being built. Many designers tend to put too much of the logic of an application in the GUI layer. In this case, there are few, if any, domain objects that are potentially available for reuse in other applications.
Another benefit of this layering is that it makes it easy to divide work along layer boundaries. It is easy to assign different teams or individuals to the work of coding the layers in a four-layer architectures, since the interfaces are identified and understood well in advance of coding. Finally, a four-layer architecture makes it possible to code the bulk of your system (in the domain model and application model layers) to be independent of the choice of persistence mechanism and windowing system.
Layers can make for great abstractions. But remember: abstractions are illusions! Beautiful, clean, elegant abstractions are still illusions. Don't be afraid to "cheat" – for example when you need better performance – and poke through a layer’s formal boundaries. This is the essence of programming. Once you have a layered architecture don't be afraid to hack, just be elegant and rigorous about how you surface your hacks to your consumers.
Buschman, F et al (1996), A System of Patterns, John Wiley & Sons, West Sussex, England, ISBN 0-471-95869-7.
Coplien, Jim: http://www.bell-labs.com/cgi-user/OrgPatterns/OrgPatterns?UserInterfaceOutside
Gamma, E., Helm, R., Johnson, R, and Vlissides, J. (1994), Design Patterns, Elements of Object Oriented Software, Addison Wesley, Reading, MA, ISBN 0-201-63361-2.
Keshav, S, An Engineering Approach to Computer Networking, Addison Wesley, Reading, MA, ISBN 0-201-63442-2.
Rubel, Barry, Patterns for Generating a Layered Architecture, published in: Coplien, J, and Schmidt, D (1995), Pattern Languages of Program Design, Addison Wesley, Reading, MA, ISBN 0-201-60734-4.
I also wish to thank the many contributors to the Wiki Web at http://www.c2.com who have provided many ideas.