OBJECT-ORIENTED LAYERED ARCHITECTURE AND SUBSYSTEMS

From: Universe (no email)
Date: 09/14/04


Date: Mon, 13 Sep 2004 21:18:00 -0400

OBJECT-ORIENTED LAYERED ARCHITECTURE AND SUBSYSTEMS
By Elliott Coates

Recently the topic of architecture for object-oriented (OO) projects
has come to the fore amongst OO software engineers and computer
scientists. It is crucial to have a good understanding of
object-oriented architecture and it's role in OO software development,
as this determines how we approach OO project development in general.
The discussion here will primarily focus on the physical construction
of large scale OO system architectures which meet the use case
requirements of system owners, users, and clients. We will explore how
systems should be given an overall design, and on that basis how they
should be developed.

WHAT IS ARCHITECTURE?
Project, or system architecture includes the parts of a system, and
the protocol for how those parts relate to each other. As with most
systems, an architecture has discrete parts, and process flows between
those parts. Project use cases, or requirements as reflected in
analysis are the key things which should guide, or lead project
architecture. Project architecture, grounded in the domain analysis
object model, should be the key thing guiding a system's physical
implementation.
Architecture should as closely as possible reflect domain
"vocabulary", or "semantics" - domain objects, idioms, and
relationships. Architecture which hews to application analysis
semantics in each of its layers, or levels, helps to make the
architecture more understandable for creation, enhancement, and
maintenance programmers, and developers. It also contributes to the
running executable being a physical model, as some of the very first
OO developers, Madsen, Moller-Pederson and Nygaard advocate.
"Object-oriented programming. A program execution is regarded as a
physical model, simulating the behavior of either a real or imaginary
part of the world. The notion of a physical model should be taken
literally"[1].
Architecture takes what has been discovered and understood during
analysis and casts it into a physical framework of parts, processes,
and interfaces which achieve the various project requirements.
Architecture stands between overall project analysis, and the
project's physical design, and implementation. The architectural
framework should, at the very least, support the fulfillment of all
analytical system use cases for the immediate project.
Ideally, we want to create an architecture which is capable of, at
least partially, satisfying the needs of future projects, not just
those of the immediate project. We want to develop an architecture
with as many classes, groupings of classes, and components as possible
that can be reused in future projects. This is achieved in a variety
of ways. Some of the major ways reuse can be achieved are discussed in
later sections of this work beginning with the section titled Screens,
Libraries, and Applications.
Project architecture encompasses both the hardware, and software
aspects of a project's system. It involves why, and how the system is
broken up into subsystems, interfaces between subsystems, what
hardware and software platforms each system part runs on (how the
system is distributed), performance considerations, coding rules, etc.
A major aspect about architecture which is determined by the preceding
factors is the contract specification for inter-layer communication.
Inter-layer communication usually takes place between all adjacent
system layers of abstraction--from the lowest device driver layers up
through the highest application layers.
In addition to fulfilling analytical use case requirements,
architecture also attempts to achieve the following qualities in a
system:
Extensibility
Flexibility
High Intra-Class/Module Cohesion
Loose Inter-Class/Module Coupling
Reusability
Robustness
These qualities are closely related and each contributes to the
achievement of the other.

IT ALL STARTS WITH ANALYSIS
As was stated above: "Architecture takes what has been discovered and
understood during analysis and casts it into a physical framework of
parts, processes, and interfaces which achieve the various project
requirements." It is a very important to be aware of, and understand
the relationship between, system architecture, and project analysis.
Traditionally, software analysis has consisted of 4 major activities:
Use case gathering
Domain discovery
Detailed investigation of use cases and constraints
Formulation of high level use case solutions
Use case gathering mainly involves compiling a list of desired system
functionalities.
Domain discovery means identifying and understanding the operation,
behavior, semantics, objects, and relationships present in the
client's application domain. This includes investigating existing
software systems in the client domain.
Detailed investigation of use cases means doing an in depth study of
the use cases. This includes determining use case performance
constraints, volumetrics (quantities involved), and the user
interface.
Formulation of high level use case solutions means identifying the
high level objects, and actors involved in the achievement, or
fulfillment of the use cases desired for the system.

ANALYSIS DETERMINES ARCHITECTURE
In the development of a software system the use cases and associated
information identified in analysis are the highest level of system
strategy. The aim of system architecture, at the very least, is to
implement the strategy identified in the analysis phase.
In order to implement the strategy given from analysis, system
architecture must take into account the existing physical environment,
and it must determine what needs to be added to that environment to
make the new system operational. The physical environment consists of
the hardware platforms the system components will execute on, the
operating system on each of the hardware platforms, the network the
system will ride over, the one or more languages used to code for the
system, etc..
Given these concerns, architecture is the highest level physical
design, or plan for a computer system. While analysis determines the
highest level conceptual plan for a system, architecture takes that
conceptual plan and makes the rubber meet the road.
With both analysis and architecture, specific implementation decisions
should be delayed as much as possible (see the section Delaying
Implementation for more on this).

LAYERS, SUBSYSTEMS AND USE CASES
Most large, complex software systems should be built using an
architectural structure which is based upon horizontally layered
subsystems. This is because the use cases of large systems are
generally based on layers, or levels of abstraction. Each level of
abstraction has semantics specific to it which are different from and
somewhat logically apart from the semantics of other layers. Typically
a system's highest layers express, and reflect most, if not all, key
client use cases in their conceptual totality.
For example the use cases of how a greenhouse is run from a botanist's
viewpoint is in many ways separate and apart from the use cases of how
data about the greenhouse is physically displayed. The 2 layers of
abstraction have differing semantics and ways they are used. Yet the 2
layers are united in a single application program at different levels,
or layers of abstraction.
Enhancing the concept of system layering is the notion that the
implementation of a use case in layer should be as non-committal as
possible. This allows for maximal polymorphic substitution of various
code modules which all carry out the same necessary tasks for a layer.
In addition it allows maximum flexibility in designing a layer itself.
Major domain abstractions in higher layers frequently use services
provided by clients that reside in layers below them to implement
their policy.

ANALYTICAL AND PHYSICAL ISSUES EXIST IN EVERY LAYER
Each system layer generally has a use case analytic sub-layer in
addition to a physical implementation sub-layer. The analytical
sub-layer is the policy, or what aspect of a layer. Whereas the
physical implementation sub-layer is the how sub-layer. A horizontally
layered system has multiple layers each with their own analysis and
physical sub-layers.
Even utility layers have an analytical, or logical (conceptual)
aspect. This analytical aspect, and the domains the utility resides in
are determined by the nature of the one, or more use cases the utility
is meant to fulfill. Utility layers, as with other utilities, first of
all solve, serve, or address one, or more logical, or conceptual
issues in one, or more domains. Every system layer, or level,
including utility oriented ones, take analytical, conceptual issues
and make them real by implementing them in physical design, and
coding.
The fact that each layer is made up of both logical, and physical
sub-layers is both enhanced by, and facilitates the use of Service
Access Points (SAP's) discussed in more detail later (see "Facades and
SAP's"). For now it's enough to say that SAP's are frequently
abstract, what oriented interface entities (e.g. classes) designed for
client entities to access the services of a layer. SAP's support the
creation, and accessing of multiple, concrete implementation, or how
entities (classes).
The key entities, and relationships constituting each layer comes out
of general domain analysis for that layer, as well as analysis
addressed to specific application requirements, and needs within the
domain. Key domain entities typically span both the analytic, and
physical sub-layers of a system layer.

MIRRORING DOMAIN MODELS IN LAYERS
A major aspect of OO technology (OOT) is the attempt to make as direct
a connection as possible between the domain object model discovered
during domain analysis and the final physical design, and coding of a
system. A domain object model consists of key existing domain objects
and their relationships to each other. This is a major "win" for the
OO paradigm versus other software paradigms. Making as direct a
connection as possible allows the logical understanding gained through
object modelling during domain analysis to make physical design and
coding less complex, and more intuitive.
Each layer of a layered architecture should "project" a domain object
model discovered during analysis. Each layer of a system reflects some
domain, whether that domain exists in the real world, the imagination,
or in the world of computer science and engineering. Examples of the
latter would be the domains of compiler technology, and the domain of
algorithmic sorts.
Layering the structure of a software system typically allows the
logical essence of the domain objects and their interaction, as they
relate to project use cases, to be portrayed in a more direct manner
than they would be otherwise. This may be referred to as domain
logical object interaction (LOI). Without layers, LOI in the various
domains, and levels of abstraction that make up a software application
have a greater likelihood of becoming entangled, and thus more
difficult to separate and understand. Expressing domain LOI is often
complicated and it is troublesome to achieve logical fidelity between
the conceptual essence of application domain, object collaboration and
its expression in the architectural design, and structure of the
software system under development.
Domain abstractions, with appropriate generalizations, should appear
in the logical system model as a direct mirror of such abstractions in
the project use case requirements. The primary reason is to make the
program as intuitive as possible for enhancement and maintenance
programmers. Such programmers will typically be schooled in domain
abstractions. Following domain abstractions in the design of a program
usually helps enhancement, and maintenance programmers to understand
both the global, and local structure of the program more quickly, and
deeply.

MULTIPLE DOMAINS IN EVERY APPLICATION
The typical large, or complex system spans multiple domains. In other
words, the issues involved in the typical system encompass a number of
fields, or areas of interest.
For instance in addition to the highest layer which is dominated by
issues related to the target application domain, there may be a
database layer, a transaction layer, a network layer, etc. In general
each of those areas, or domains, constitutes a subsystem. Each of
these subsystems has a layered relationship to the other.

USER VERSUS DEVELOPER INTERESTS
What primarily interests application domain users of a system is the
operation of the system in fulfilling use case requirements.
Application domain users generally only have an interest in the
architecture of a system as regards its top, or uppermost layers; they
mainly have a top slice, "horizontal" interest in the system. Most of
these users do not have an interest in lower level processes[3]. The
extent of their interest in the system architecture is a subset of the
interest developers have in system architecture.
While each layer has analysis, architectural, and coding phenomena
associated with it, it is only system architecture as a whole which
runs across all layers. And this architectural whole is first of all
driven, and parametrized (led and constrained) by the logical use
cases defined for the highest layer of the project system.
System developers must address implementation issues across all
layers; they have a mainly "vertical" interest in the system. The
interest of system developers spans every layer from top to bottom.
While the developers must implement vertically, their drive and basis
for development is to fulfill top, or uppermost layer, horizontal use
cases for application domain users. System developers must be
concerned with the gist of a system as it encompasses analysis,
architecture and design at each level as well as in the interaction of
the various layers of a system taken as a whole. [4]

SUBSYSTEMS ARE VARIED AND LAYERED
There are usually a number of different kinds of layered subsystems in
the architecture of the average computing system. For example many
systems, in addition to domain semantic based software subsystems,
have a Web subsystem, a transaction monitor subsystem, and a logical
display subsystem, among others. Subsystems exist in hardware,
software, and a combination of both. Many of these subsystems have
parts which reside both in application programs themselves as well as
in the operating system with its ancillary, allied services.
Each of these subsystems is a micro-architecture and many of these
subsystems overlap.
Typically each subsystem coordinates and implements many processes and
tasks and uses many processes and tasks belonging to other subsystems
for its various coordination's and implementations.

HORIZONTAL AND VERTICAL USE CASES
With a layered architecture the primary ways the system will be used
are embedded in both the horizontal and vertical layers of the system.
Each layer implements, or expresses use cases for that level of system
abstraction. From an overall system perspective there are vertical use
cases in the way the various horizontal use cases are tied together.
Additionally, what underlies the implementation of each horizontal
layer of use cases is the vertical aggregation of horizontally layered
use cases below each horizontal layer.
More often than not there are multiple functions, or use cases, in the
same layer. For instance the user interaction layer (the OSI
application layer) of many systems has application maintenance
functions in addition to the functions, or use cases, that most
application users become familiar, and involved with. This gets to the
notion of systems having many tops, which is discussed in more detail
in the section Advanced Subsystem And Layer Concepts, at the end of
this work.
System architects should determine the use cases implemented at each
level of system abstraction and how they will interact as a single
multi-layer, vertical aggregate. On the other hand, layer (or what
here is the same thing, subsystem) architects should have only to be
concerned with the use cases of 3 layers. These are the subsystem
layer itself, the layer above it, and the layer below it.
A subsystem layer serves itself and the layer above it. And a
subsystem layer depends on itself and the layer below it for services.

NEVER THE TWAIN SHOULD MEET: OR LAYERED DIRECTIONAL PROCUREMENT AND
PROVISION OF SERVICES
Layers being systems in themselves, do what all healthy systems do,
they both procure input from their environment, and provide output to
their environment. In nature there are both bi-directional as well as
unidirectional layered systems. Bi-directional layered systems both
provide and procure major services at their upper side, just as they
both provide and procure major services at their lower side.
Unidirectional layered systems procure major services in one direction
while providing major services in the opposite direction.
Most engineering disciplines - software, mechanical, electrical, etc,
- strive to construct "unidirectional" layered systems. Each system
layer provides major services at its upper side, and each layer
procures services through its lower side. The services a layer
provides at its upper side make it possible for a higher layer to
operate, while the services it procures through its lower side are
those the layer requires for its own operation.
Constructing systems in this manner helps make it possible to link and
operate a lower layer without the existence of a higher layer.
Obviously if a layer requires major services from a higher layer, the
higher layer must be constructed and operational for the lower layer
to operate.

LAYERS AND DEPENDENCY
Classes or objects in a layer should only depend, for compilation and
linking purposes (physical dependency purposes), on classes or objects
within the same or lower layers. Constructing a layer and its objects
in such a manner makes it possible to construct lower layers before
higher ones.
At the same time classes or objects in one single layer package should
not have a cyclic dependency on objects in other packages - either
within or outside of the layer[5]. This eliminates spaghetti like
physical dependencies which causes a small change to ripple through a
larger number of code units than it should. And it helps to lessen
compilation and interpretation times.
Another point about layers and dependency is that, what makes it
possible to swap one layer for another is a well known layer interface
protocol between the layer and both its upper and lower adjacent
layers.
When classes, or objects do not physically depend upon higher layers,
or have cyclic dependencies, program complexity is reduced and program
flexibility as well as robustness are increased.

PRE-EXISTING LOWER LAYERS
It should be noted here that the existence of a specific lower layer
may, or may not be necessary for the operation of specific higher
layers. Lower layers often exist in their own right, to fulfill the
purpose of that lower layer independent (regardless) of the existence
of what is regarded as higher layers in relation to what is going on
in that layer. Each layer, in a sense contains one, or more
micro-applications, which have a life related to, but also somewhat
independent of the overall macro project application[6].
Lower layers may have evolved without regard to the higher level
layers which use them. Yet it is also the case that certain lower
layers exist, or evolve according to the needs of higher layers.
The services provided by lower layers serve to parametrize -
constrain, pressure and provide the scaffolding for - what higher
layer use cases, processes, and applications are able to do. While
what a higher layer does leads what is going on in that higher layer,
lower layers are capable of both promoting, and inhibiting what goes
on in higher layer. Lower, as well as same level processes serve as a
skeleton, or framework for higher layer use cases, processes, and
applications. The lower layer skeleton provides services for the
higher layer use cases - for example display services.
While a lower layer parametrizes the design and operation of a higher
layer, the higher layer should lead, or guide the activities of a
lower layer as it relates to the activities of the higher layer[7].

SEEING THE FOREST NOT JUST THE TREES
A major reason to determine an architecture before coding is that it
allows for the creation of mechanisms which can be shared among
various parts of the program. Also it allows for interfaces to be
designed which accommodate interaction among all of the parts of the
program. This reduces having to rework interfaces as existing parts of
the system are modified or new parts are added to the system.
Additionally it allows for better time line management and resource
planning by project managers. The managers are able to have a better
idea of what resources are necessary to successfully complete a
project, and they are also able to get a better handle on what risks
are involved.
In addition, "The initial sketch [architectural plan] helps to give
some overall uniformity to the system architecture without
[necessarily] being a straitjacket to the [coding] process."[8]
While the overall architectural plan should be determined before
creating new production code, testing, or prototyping[9] high risks
areas can reduce failure by alerting the developers to things that
just won't work. In this way we don't go too far with an unworkable
overall architecture, or architectural sub-plan.
A final point on seeing the forest and not just the trees is that the
"big picture" logical solution, and architecture are first line issues
in determining how easy a system is to adapt to modifications and
extensions. Well thought out logical partitioning based on good
analysis solutions, and good global high level physical design -
architecture - facilitate system modification and extension.

COMMON MECHANISMS
Frequently, system architecture specifies "shared mechanisms"[10]
within and across layers. After analyzing major use cases it is often
discovered that there are processes, and mechanisms which are common
to all, or most of the use cases. These common architectural
mechanisms should be formally specified. The use of common mechanisms
should be mandated over one shot mechanisms when they can effectively
be used in place of them.

ITERATIVE ARCHITECTURE AND ITERATIVE PROCESS
Iteration is associated with architecture in a number of significant
ways.
It is often the case that an application's initial architectural plan
gels only after a number of iterations within the analysis phase
itself. That is, an iterative and incremental cycle spins a number of
times within the initial analysis phase even before things progress
further along into other the phases - architecture, local design,
implementation, testing, and maintenance - of the software development
lifecycle[11].
To a greater or lesser extent depending on the particular project, the
formulation of system architecture is iteratively modified by feedback
from implementation. Architecture should be validated by implementing
one or more of its riskiest parts first and early on. These
implemented parts are then tested by users and feedback is gathered
according to which the existing architecture is modified. In some
cases implementation feedback even dictates that the existing
architecture be scrapped.
But while analysis often iterates before settling on an architecture
and architecture receives feedback from implementation that doesn't
mean that architecture is necessarily nebulous in the time frame
beginning just after the initial round of analysis has been completed
and running to before implementation has been achieved. Architecture
may be more or less nebulous in this time frame, and more or less
subject to feedback from implementation. A common scenario is that the
main outlines of architecture are formulated after initial analysis
and this outline is fleshed out through feedback from implementation,
and guidance from iterative and incremental analysis which takes place
after initial analysis.
Also a distinction should be made between the architectural plan and
the implementation of the architectural structure. While the
architectural plan may be relatively complete before high level
production coding begins, the completion of the architectural
structure itself typically progresses in phases with each major,
iterative coding cycle.

MACRO SCALE AND MICRO SCALE ARCHITECTURE
While there is an overall, or macro, project architecture, each part
of a system has its own micro architecture. For example each layer of
a horizontally layered system has its own architecture apart from the
architecture of all layers taken together. It is not only important to
address the architectural issues relevant to the project as a whole,
but to address micro - subsystem - architecture as well. And just as
project use cases, or requirements as reflected in analysis are the
key things which should guide, or lead macro project architecture,
they should also lead, or guide each layer's micro architecture.
Often different methods and approaches must be taken to deal with the
different levels of architecture - macro vs. micro. This is similar to
the way that we use telescopes to observe and study the macro aspect
of the universe: stars, galaxies, clusters of galaxies, etc, whereas
we use the microscope to observe and study the micro aspect of the
universe: molecules, electrons, quarks, etc.

INVARIANCE AND ARCHITECTURE
Inter-layer (macro), as well as intra-layer (micro) architectural
frameworks should incorporate invariant domain processes. A layer, and
how it relates to other layers should incorporate invariant
application domain behavior which is relevant to each of the specific
domain applications relevant to that layer. In this way invariants do
not have to be repeatedly duplicated in the high level coding for each
specific application.

CONTROLLERS, ENTITIES, BOUNDARIES AND LAYERS
Typically each subsystem layer has its own Entity, Controller, and
Boundary classes.[12] Such a decomposition promotes loose coupling and
high cohesion between groups of application classes.
Controller classes typically express behavior leftover in a use case
scenario after previously dividing scenario behavior amongst entity
classes. Often controllers represent the activities of use case
actors. Entity classes typically represent major non-actor
abstractions. Boundary classes are responsible for handling the input
and output related to controller and entity classes.
Boundary classes handle user, and other processes, input/output to and
from entity and controller classes. Often boundary classes allow
controller and entity classes to be isolated from, and unconcerned
with the details of the specific input/output devices present on a
particular hardware system. In some architectural systems, controller,
and entity classes write to boundary classes in a layer that provides
a logical API for input and output. These logical boundary layers also
allow indirection with respect to input, and output . In other words,
data from controller, and entity classes may be directed to either a
printer or monitor unbeknownst to the controller, and entity classes.
A notable example of a logical boundary layer resides within the
architecture of Microsoft (MS) Windows. MS Windows controller and
entity classes write to a Graphical Display Interface (GDI), which is
a logical device layer. This decouples the controller and entity
classes from actual physical input and output devices. Such decoupling
allows a controller, and entity classes to get input and output from a
nearly infinite variety of specific physical display systems,
printers, pointers, mice, keyboards, etc.

SCREENS, LIBRARIES AND APPLICATIONS
The various architectural concepts discussed to this point may be
given life, and tied together by an example given by Bjarne
Stroustrup, the creator of C++ OO programming language. The example is
about layers, and "writing a program for geometric shapes on a
screen".[13]
Stroustrup states that such a program would "naturally consist of
three parts", or layers:
Screen Manager
Shape Library
Application program
He notes that "Typically, the parts are written by different people in
different organizations and at different times. The parts are
typically written in the order they are presented, with the added
complication that the designers of a lower level have no precise idea
of what their code will eventually be used for." [14]
This underscores the key point that the existence, and operation of
system layers should not depend on the existence, and operation of
higher layers. A system layer is able to compile, and link
independently of higher system layers when the elements of the layer
depend only upon the same or lower, not higher, layers to compile and
link. If we allowed layers to rely on higher layers, to compile and
link, as some advocate, we would not be able to build and operate
lower layers before the higher ones were constructed. This is clearly
against the spirit of what Stroustrup is getting at above.
While lower layers can exist regardless of the physical existence of
higher level modules, or clients, it's often the case that the
external interface as well as the internal implementation of a lower
layer primarily depends on the responsibilities the lower layer must
fulfill to serve the operational needs of levels that will, or
currently do exist above it. Lower layers are often constructed in
anticipation of being used by higher layer clients; generally that
will be clients in the higher layer immediately adjacent to the lower
layer.
A major reason lower levels are built in a way which does not depend
on the physical existence of any one specific higher level to execute
is so that they may support a variety of higher level clients - each
lower level has the widest scope possible for reuse. If a lower layer
physically only depends on lower levels to execute, it makes it
possible for the layer to serve a broader array of upper level needs
and applications. For example a Screen Manager layer should be able to
provide services for a Text writing library, as well as a Mathematical
formula writing library, and not solely a Shape library.
One reason lower levels should typically have abstract interfaces is
to be able to accommodate a variety of future clients which the lower
level often has no specific knowledge about. Using an abstract
interfaces makes it easier to change a lower level to accommodate new
higher layer clients - generally by adding new classes under the
abstract interface. In addition to the fact that in some languages,
abstract interfaces[15] help to reduce compilation and linking
(physical) dependencies between higher and lower system layers.
Having each layer use abstract interfaces, and be physically dependent
only on lower layers to execute encourages the reuse of higher layers.
Doing these things allows for the existence of many independent,
pre-existing lower level foundations upon which we can place our high
level policy. Reuse of higher layer code can be realized even while
lower layers are able to execute independent of higher layers.

LOW LEVELS, HIGH LEVELS AND ABSTRACT INTERFACES
Here we will deal with an area of misunderstanding. Some claim that
classes which are abstract interfaces are a system's "higher level
policy" and that classes which implement abstract interfaces are "low
level details".[16] In large part this stems from their practice of
doing one or more of the following:
Failure to address a software project in a holistic manner
Primarily approaching system design from the inside out - from the
inside out of a layer, class, object, and groups of same.
Primarily approaching system design from the bottom up
It is true that abstract interfaces specify "what" (policy) as opposed
to "how" (implementation). Yet from an overall layered system
perspective, not all abstract interfaces specify the policy of the
system's highest layers, and not all implementation classes implement
the system's lowest layers.
Abstract interfaces exist in an architecture's lowest levels and these
can hardly be said to be the higher level policy of a system. That is
if higher level policy is taken as the overall nature, sweep, and
thrust of how the system is used as a complete totality. For example
we may accept that the highest level policy of a spread***
application is bond trading.
High level bond trading policy is built on the basis of the layered
architecture of the spread*** as a complete software system, which
includes its having a number crunching engine. While the layer the
number crunching engine resides in may have an abstract interface,
that abstract interface is not the "high level" policy of bond
trading.
While it is true that for every layer, abstract interfaces will be the
interface of that layer to layers above it, and are therefore in a
kind of sub-layer above implementation classes of that layer, it is
also the case that viewing the system as a whole, it is actually the
next and further higher up layers that are "high level" with respect
to a layer. What is "higher level" to a layer is not the sub-layer of
abstract interfaces within it, but the totally different layers above
it in the system architecture.
To equate abstract interfaces with "high level policy" is to
mistakenly equate policy only with the highest levels of a layered
architecture. It is ignoring the fact that policy and implementation
exist within every level of a layered system.
In fact, abstract interfaces of a layer can be viewed as phantoms of
the layer's concrete implementation classes. Whereas concrete classes
actually do something, abstract interfaces represent the commonality
of a related group, or set of concrete "doer" classes within the same
layer.
Abstract interfaces concentrate the common essence of a hierarchy of
concrete server classes, and reside in the same layer, or level as the
concrete "doer" classes. Such concentration of commonality exists in
"all" levels of a system's architecture. The purpose of the abstract
interface class is to allow a single service to have many, or variable
forms of implementation. Among other things this allows different
kinds of clients to access the same service point in a layer.
Every layer has its concrete "doer" classes as well as "concentrator"
abstract interfaces. So the inheritance dependency of concrete server
classes on abstract interfaces is not "lower level details" depending
on "higher level policy", but simply same level "how", or "doer"
classes depending on same level "what", or "concentrator" classes for
interface adherence. "Higher level policy" from an overall bond
trading system perspective is a policy like when to sell, not the
policy that every abstract interface has at every layer in the bond
trading system from top to bottom. While the number crunching engine
has an abstract interface, that interface is not like the high level
policy having to do with when to sell bonds.

LAYERS AND PACKAGES
Layers (which while they may include other subsystems are subsystems
themselves) are usually made up of one, or more packages of classes. A
package of classes has logically, highly cohesive responsibilities.
The responsibilities of each package are determined by system and
subsystem architects. Some systems have a one-to-one correspondence
between subsystems and packages, while in others a subsystem may
contain a number of packages. Each subsystem is generally a single
layer, level (or vertical slice) in a system.
It is desired that logical class packages match packages of domain
abstractions. Many packages do so, but often there are others which do
not directly reflect the semantics of the system application domain.
Yet all packages have project responsibility and functionality based
on logical project cohesiveness, and the needs of system architecture
approached holistically.
In other words, each package's logical function ultimately derives
from the overall, logical, top down architecture of the system as a
whole. For each iterative and incremental cycle of a project (which
includes feedback), the architects' logical system, and subsystem
decomposition plans should determine what packages are created, as
well as the logical responsibilities, and functionalities of each
package.
Packages should also reflect the real world at their level of
abstraction. If packages do not express real world domain abstractions
at their level of abstraction that would be throwing away the
excellent modelling ability of object-orientation. If packages do not
have a functionality based on the needs, and semantics of the level of
abstraction they reside in, why does the package exist there in the
first place?

CLASSES, DEPENDENCY AND LAYERS
While we want to eliminate the physical dependency of a class in a
lower layer upon a class in a higher layer, acyclic physical
dependency between classes within a grouping of classes is acceptable
and the best to be hoped for. For layers with multiple groupings it is
also acceptable for classes in one of the groupings to be acyclicly,
physically dependent upon classes in other groupings in the same
layer.
When classes in a layer depend only upon classes in the same or lower
layers it is possible to construct lower layers that can exist
independently of higher layers. This is the basis of being able to
construct and re-use components, and pre-existing architectures to
build user applications.

SYNERGY AND EMERGENT BEHAVIOR
An architectural system, as with any system, is an entity where the
parts work for the whole. Parts work for whole, yet the whole can
feedback on the parts. Just as cells provide the basis for the mind,
the overall emotional state of the mind helps, or hinders the
operation of cells. Likewise although a software project's
architecture is made up of layered subsystems, the system as a whole
has properties and behaviors which result from the synergy of the
subsystems working together in the system as a whole. These are
emergent properties and behaviors. And such emergent properties may
then powerfully affect each of the parts that have worked together to
give rise to it.
Emergent properties is a concept that an intrepid, prescient group
from within the OO community periodically remind us of. While the
larger concept of synergy underlies it, the concept of emergent
properties and behavior is a useful one that should be designed for,
and taken advantage of, when developing OO systems, based on the
positive experience of some.

VERTICAL SLICES
Although most subsystems will be horizontally layered there typically
are also subsystems which make a vertical slice across the system.
They are vertical in that 2, or more horizontal layers have direct
contact with them. Processes in horizontal layers do not have to use
lower horizontal layers to communicate with a vertical subsystem. For
example it's often the case that the MS Windows logical display
subsystem (the GDI mentioned above) is accessible by all layers. It
communicates mostly with the various horizontal layer's controller and
entity classes, and then transmits that data to a physical display
subsystem, or layer.

FACADES AND SAP's
Project architecture should make clear how the various subsystems of
the project interact. The interfaces between subsystems should be
based on a formal contract between each subsystem as a client, or
server. The Facade design pattern is one way of packaging the
interface to a subsystem as a single tractable entity, for other
subsystems to interact with. The Z/Z++ language is helpful in
specifying contractual interfaces between subsystem interfaces
Jacobson has put forward the concept of System of Interconnected
Systems (SIS) . This is "a set of (subordinate) systems that are
interconnected by means of Facades and Contracts. In this context, a
layered system is one of a special type of a SIS. A SIS, however, is a
more formal and generalized way to define a system architecture."[17]
In this context it is instructive to remember that objects, packages,
layers, and an application as a whole are all systems.
Subsystem interfaces may be thought of as Open System Interconnect
(OSI) Service Access Points (SAP's). Processes in higher layers
utilize SAP's to obtain services from lower layers. In addition,
clients residing in the same layer with a SAP often use the SAP to
procure services. SAP's may be viewed as well defined ports, or
gateways, to a layer.
A SAP is frequently a pointer to an abstract polymorphic interface in
the same, or next layer below where the client resides. The pointer
usually points to (references) the abstract root class of a hierarchy
of server classes. Pointers can be assigned any one of the objects
instantiated from the concrete implementation classes in the server
hierarchy[18].
As much as possible we should try to minimize the surface area of the
interface for each layer, or subsystem. Facades help us to do this. In
order to be able to dynamically substitute one subsystem for another,
in statically typed languages like C++, and Eiffel, it is often useful
to make subsystem interfaces abstract. Facades also reduce the number
of classes subject to cascading recompilation.

DELAYING IMPLEMENTATION
Most in the software engineering arena appear to agree that delaying
implementation is generally a thing to be desired. At every level, and
in every way each particular level or context should "put off" as much
as possible implementation of the decisions of that level's or
context's policy aspect. This seems to afford the greatest flexibility
within the level or context which makes a policy decision as well as
the greatest flexibility within the lower levels, and subsidiary
contexts which carry out implementation of that decision.
This makes the greatest amount of sense when it is realized that the
difference between analysis, physical design and implementation is a
matter of progressive, drill down "specification, and refining"[19].

VIEWS ARE MORE THAN VIEWS
Before we finish with a number of interesting advanced layer concepts,
here are some thoughts on the "4+1" notion of system architecture[20].
"4+1" is said to refer to 5 different views of a software system. The
"4" are the logical, process, development, and physical views. The
"+1" is the scenario (use case) view. The scenario view is "+1"
because it is considered to be the primary and unifying view. The
proponents of the "4+1" idea of system architecture claim that each
view is an alternate way of looking at a single software system
architecture.
In reality one of the so-called views is not a part of the software
system at all and the other so-called views are a number of separate
architectures, not a single system architecture.
What is considered to be the development view of a software system is
not an element of the software system at all, per se. Development
activity of a software project while managing and coordinating
creation, and modification of the software system, is nevertheless not
the same as the software system itself.
The so-called scenario, logical, process, and physical views in
actuality are individually separate architectures in their own right.
While these architectures standalone separately, they are related to,
and work with one another. The scenario architecture ties the other
individual architectures together in order to achieve the fulfillment
of various scenarios, or use cases.

ADVANCED SUBSYSTEM AND LAYER CONCEPTS
As Jacobson et al point out, most large scale, horizontally layered
systems have 3 to 4 layers. The application layer, the underlying
domain layer, a layer under that which implements the domain layer in
logical primitives, and a yet lower layer which physically implements
logical primitives.
Generally, clients in the same, or higher layers will instantiate
their own server objects in the same, or lower layers. The various
instantiated servers may all be operational at the same time. Properly
designed static classes may be instantiated as objects to
simultaneously serve multiple higher layer clients. In this case
access to a single operational SAP will be multiplexed amongst the
clients. In other words, multiple clients may hold a pointer to, and
gain services from, the same operational SAP (abstract or concrete).
The fact that a SAP may serve more than one same level, or upper
layer, client at a time, indicates that layered systems may have many
tops. The uppermost layer of layered software systems can have many
processes operating within it. These multiple processes may be
embedded within the uppermost layer, vertically structured in parallel
with each other, or both. And as stated above, each of these processes
may simultaneously access the same operational SAP's, if the SAP's are
so designed.
Often there are inverse pyramids working simultaneously in
architectural structures. For a number of high level application
processes the number of lower level processes associated with the high
level process increases at each layer as one goes from higher to lower
levels. Conversely, there are low level processes which involve an
increasing number of processes for each level as one goes from lower
to higher levels.
Each horizontal layer of a system generally implements 1 to 3 entity
patterns of class, and object interaction. In addition, inter-layer
interaction, and communication is often based on 1 to 3 paradigms.
There are not just layers of analysis, and design, there is also
nested analysis, and design within the same layer of an architectural
system. There are nested use cases, and nested levels of state within
the same layer of a system.
The architecture of large systems tends to diverge from user
perception more so than smaller systems. Typically this is because
large systems have differing layers, or levels of abstraction. Each
horizontal layer in a large program is usually grounded in its
relevant domain vocabulary, generally reflecting the key objects and
relationships in that domain. But what makes system architecture
diverge from user perception is that the vertical construction of the
system as a whole is frequently not aligned with the semantics of each
horizontal layer.
A software layer's interaction with other software layers may, or may
not, be logically similar to how that layer's real world counterpart
interacts with other real world layers. In addition, the nature of
interlayer relationships sometimes logically mirrors the nature of the
application's topmost layer and at other times it doesn't.

_______________
1 "Computerized physical models", Madsen, Moller-Pederson, Nygaard
2 "One main point of use case is to deliberately model meeting users
goals, and to deliberately "not" address implementations up front."
Greg Gibson, comp.object, 12/96.
3 Via architects users have an interest in a system architecture which
is easy, and economical to maintain. It is the users who will pay in
training, time, and money for the maintenance, and enhancement of new
applications down the line.
4 Each vertical layer must be utilized ultimately to fulfill the
highest layer use cases. The entities and relationships of a system
may not always be physically present or they may be shared with other
systems. The point of system architecture is to posit a logical
framework for development achievement of use cases even though various
physical parts of the system exist, or are only accessible, fleetingly
as needed.
5 See various Stroustrup publications, and John Lakos' book Large
Scale C++ Design for more on this.
6 These micro-applications may also involve processes which occur
across more than one layer.
7 The dynamic signified by a parametrizing lower level versus the
leading role played by a higher level is a manifestation of a new kind
of dialectic relationship exhibited between some objects that I have
discovered. The dialectic relationship is between on the one hand a
leading, or engaging factor as opposed to on the other hand a
parametric. The leading factor is the side of the relationship that
primarily moves the relationship forward. The opposite or parametric
side constrains, or guides the leading factor. It is frequently found
that the leading side, or aspect of the relationship is the side which
people "engage", or use to partially, or fully, control how the
relationship operates. For example, in the development of software
architecture, while lower levels constrain and set the basis for
higher levels, it is higher levels which should lead and determine the
nature of the system as a whole. It is also important to note that the
roles may be reversed temporarily, or permanently and that is
frequently the case. What was formerly the leading factor may become
the parametric and vice versa.
8 Gibson
9 prototyping in software engineering has typically meant both testing
high risks (proof of concept testing), as noted in the main text, as
well as user requirements elicitation.
10 Booch, Object-Oriented Analysis and Design
11 This is similar to Mike Barnhouse's customer involved process. See
page 138ff of the book Exploiting Chaos: Cashing in on the Realities
of Software Development by Dave Olson (VNR, '93 NYC).
12 Jacobson et al, Object-Oriented Software Engineering
13 Stroustrup, Bjarne, C++ Programming Language pg 193ff
14 Stroustrup, Bjarne, C++ Programming Language pg 193ff
15 In C++ an abstract base class (ABC), or polymorphic interface (PI).
16 This is mixed in with their thinking that abstract interface
classes are the only abstractions in a software system. Really all
classes which represent domain entities are abstractions. They are
abstractions in that rather than being an actual item in the domain,
they represent the essence of the domain item as we are interested in
it for our program. They are relevantly pared down notions of domain
entities, which is the same thing as a model. Each such class is a
mini-model compared with the model of each layer and the system as
whole.
17 Jacobson, Object July 1996.
18 Such pointers are also known as abstract polymorphic interface, or
simply polymorphic interface (PI). A term I coined in the early 1990's
and now in the C++ standard.
19 This practice has been made explicit and formalized in the
Catalysis methodology.
20 Philippe Kruchten, "The 4+1 View Model of Architecture", IEEE
Software, November 1995.
____________
Copyright Elliott Coates, 1998.

-- 
Theory Leads, Practice Verifies
 Profiteer US Out of Iraq Now!

Quantcast