Reuse of development artifacts can come by design or as an afterthought. While in the latter case artifacts may have been originally devised for specific contexts and purposes, in the former case they would have been originated by shared concerns and designed according architectural constraints and mechanisms.
Architectures for their part are about stable and sound assets and mechanisms meant to support activities which, by nature, must be adaptable to changing concerns. That is precisely what reusable assets should be looking for, and that may clarify the rationale supporting models and languages:
- Why models: to describe shared (i.e reused) artifacts along development processes.
- Why non specific languages: to support the sharing of models across business domains and organizational units.
- Why model layers: to manage reusable development assets according architectural concerns.
Reuse Perspective: Business Domains vs Development Artifacts
As already noted, software artifacts incorporate contents from two perspectives:
- Domain models describe business objects and processes independently of the way they are supported by systems.
- Development models describe how to design and implement system components.
As illustrated by agile methods and domain specific languages, that distinction can be ignored when applications are self-contained and projects ownership is shared. In that case reusable assets are managed along business domains, functional architectures are masked, and technical ones are managed by development tools.
Otherwise, reusable assets would be meaningless, even counterproductive, without being associated with clearly defined objectives:
- Domain models and business processes are meant to deal with business objectives, for instance, how to assess insurance premiums or compute missile trajectory.
- System functionalities lend a hand in solving business problems. Use cases are widely used to describe how systems are to support business processes, and system functionalities are combined to realize use cases.
- System components provide technical solutions as they achieve targeted functionalities for different users, within distributed locations, under economic constraints on performances and resources.
Whatever the basis, design or afterthought, reusing an artifact comes as a solution to a specific problem: how to support business requirements, how to specify system functionalities, how to implement system components. Describing problems and solutions along architecture layers should therefore be the backbone of reusable assets management.
Model and Architecture Layers
According model driven architecture principles, models should be organized around three layers depending on contents:
- Computation independent models (CIMs) describe business objects and processes independently of the way they are supported by system functionalities. Contents are business specific that can be reused when functional architectures are modified (a). Business specific contents (e.g business rules) can also be reused when changes do not affect functional architectures and may therefore be directly applied to platform specific models (c).
- Platform independent models (PIMs) describe system functionalities independently of supporting platforms. They are reused to design new supporting platforms (b).
- Platform specific models (PSMs) describe software components. They are used to implement software components to be deployed on platforms (d).
Not by chance, invariants within model layers can also be associated with corresponding architectures:
- Enterprise architecture (as described by CIMs) deals with objectives, assets and organization associated with the continuity of corporate identity and business capabilities within a regulatory and market environment.
- Functional architecture (as described by PIMs) deals with the continuity of systems functionalities and mechanisms supporting business processes.
- Technical architecture (as described by PSMs) deals with the feasibility, interoperability, efficiency and economics of systems operations.
That makes architecture invariants the candidates of choice for reusable assets.
Enterprise Architecture Assets
Systems context and purposes are set by enterprise architecture. From an engineering perspective reusable assets (aka knowledge) must include domains, business objects, activities, and processes.
- Domains are used to describe the format and semantics of elementary features independently of objects and activities.
- Business objects identity and consistency must be maintained along time independently of supporting systems. That’s not the case for features and rules which can be modified or extended.
- Activities (and associated roles) describe how business objects are to be processed. Semantics and records have to be maintained along time but details of operations can change.
- Business processes and events describe how activities are performed.
As far as enterprise architecture is concerned, structure and semantics of reusable assets should be described independently of system modeling methods.
Combining Object and Aspect Oriented principles, reuse of enterprise architecture assets should distinguish between identities and structures on one hand, semantics on the other hand.
With regard to business activities, semantics are set by targets:
- Processing of physical objects.
- Processing of notional objects.
- Agents decisions.
- Processing of events.
- Control of processes execution.
Regarding business objects, semantics are set by what is represented:
- State of physical objects.
- State of notional objects.
- History of roles.
- Execution states.
Enterprise assets are managed according identification, structure, and semantics, as defined along a business perspective. When reused as development artifacts the same attributes will have to be mapped to an engineering perspective.
Use Cases: A bridge between Enterprise and System Architectures
Systems are supposed to support the continuity and consistency of business processes independently of platforms technologies. For that purpose two conditions must be fulfilled:
- Identification continuity of business domains: objects identities are kept in sync with their system representations all along their life-cycle, independently of changes in business processes.
- Semantic continuity of functional architectures: the history of system representations can be traced back to associated business operations.
Hence, it is first necessary to anchor requirements objects and activities to persistency and functional execution units.
Once identities and semantics are properly secured, requirements can be analyzed along standard architecture levels: boundaries (transient objects, local execution), controls (transient objects, shared execution), entities (persistent objects, shared execution).
The main objective at this stage is to identify shared functionalities whose specification should be factored out as candidates for reuse. Three criteria are to be considered:
- System boundaries: no reusable assets can stand across systems boundaries. For instance, were billing outsourced the corresponding activity would have to be hid behind a role.
- Architecture level: no reusable assets can stand across architecture levels. For instance, the shared operations for staff interface will have to be regrouped at boundary level.
- Coupling: no reusable asset can support different synchronization constraint. For instance, checking in and out are bound to external events while room updates and billing are not.
It’s worth to note that the objectives of requirements analysis do not depend on the specifics of projects or methods:
- Requirements are to be anchored to objects identities and activities semantics either through use cases or directly.
- Functionalities are to be consolidated either within new requirements and/or with existing applications.
The Cases for Reuse
As noted above, models and non specific languages are pivotal when new requirements are to be fully or partially supported by existing system functionalities. That may be done by simple reuse of current assets or may call for the consolidation of existing and new artifacts. In any case, reusable assets must be managed along system boundaries, architecture levels, and execution coupling.
For instance, a Clean Room use case goes like: the cleaning staff manages a list of rooms to clean, checks details for status, cleans the room (non supported), and updates lists and room status.
Its realization entails different kinds of reuse:
- Existing persistency functionality, new business feature: providing a cleaning status is added to the Room entity, Check details can be reused directly (a).
- Consolidated control functionality and delegation: a generic list manager could be applied to customers and rooms and used by cleaning and reservation use cases (b).
- Specialized boundary functionality: staff interfaces can be composed of a mandatory header with optional panels respectively for check I/O and cleaning (c).
Reuse and Functional Architecture
Once business requirements taken into account, the problem is how to reuse existing system functionalities to support new functional requirements. Beyond the various approaches and terminologies, there is a broad consensus about the three basic functional levels, usually labelled as model, view, controller (aka MVC):
- Model: shared and a life-cycle independent of business processes. The continuity and consistency of business objects representation must be guaranteed independently of the applications using them.
- Control: shared with a life-cycle set by a business process. The continuity and consistency of representations is managed independently of the persistency of business objects and interactions with external agents or devices.
- View: what is not shared with a life-cycle set by user session. The continuity and consistency of representations is managed locally (interactions with external agents or devices independently of targeted applications.
- Service: what is shared with no life-cycle.
Assuming that functional assets are managed along those levels, reuse can be achieved by domains, delegation, specialization, or generalization:
- Semantic domains: shared features (addresses, prices, etc) should reuse descriptions set at business level.
- Delegation: part of a new functionality (+) can be supported by an existing one (=).
- Specialization: a new functionality is introduced as an extension (+) of an existing one (=).
- Generalization: a new functionality is introduced (+) and consolidated with existing ones (~) by factoring out shared features (/).
It must be noted that while reuse by delegation operates at instance level and may directly affect coupling constraints on functional architectures, that’s not the case for specialization and generalization which are set at type level and whose impact can be dealt with by technical architectures.
Those options can also be mapped to agile development principles as defined by R.C. Martin:
- Single-Responsibility Principle (SRP) : software artifacts should have only one reason to change.
- Open-Closed Principle (OCP) : software artifacts should be open for extension, but closed for modification.
- Liskov Substitution Principle (LSP): Subtypes must be substitutable for their base types. In other words a given set of instances must be equally mapped to types whatever the level of abstraction.
- Dependency-Inversion principle (DIP): high level functionalities should not depend on low level ones. Both should depend on abstract interfaces.
- Interface-Segregation Principle (ISP): client software artifacts should not be forced to depend on methods that they do not use.
Reuse by Delegation
Delegation should be considered when different responsibilities are mixed that could be set apart. That will clearly foster more cohesive responsibilities and may also bring about abstract (i.e functional) descriptions of low level (i.e technical) operations.
Reuse may be actual (the targeted asset is already defined) or forthcoming (the targeted asset has to be created). Service Oriented Architectures are the archetypal realization of reuse by delegation.
Since it operates at instance level, reuse by delegation may overlap functional layers and therefore introduce coupling constraints on data or control flows that could not be supported by targeted architectures.
Reuse by Specialization
Specialization is to be considered when a subset of objects has some additional features. Assuming base functionalities are not affected, specialization fulfills the open-closed principle. And being introduced for a subset of the base population it will also guarantee the Liskov substitution principle.
Reuse may be actual (a base type already exists) or forthcoming (base and subtype are created simultaneously).
Since it operates at type level, reuse by specialization is supposed to be dealt with by technical architectures. As a corollary, it should not overlap functional layers.
Reuse by Generalization
Generalization should be considered when different sets of objects share a subset of features. Contrary to delegation and specialization, it does affect existing functionalities and may therefore introduce adverse outcomes. While pitfalls may be avoided (or their consequences curbed) for boundary artifacts whose execution is self-contained, that’s more difficult for control and persistency ones, which are meant to support multiple execution within shared address spaces.
When artifacts are used to create transient objects run in self-contained contexts, generalization is straightforward and the factoring out of shared features (a) will clearly further artifacts reuse .
Yet, through its side-effects, generalization may also undermine the design of the whole, for instance:
- The open-closed principle may be at risk because when part of a given functionality is factored out, its original semantics are meant to be modified in order to be reused by siblings. That would be the case if authorize() was to be modified for initial screen subtypes as a consequence of reusing the base screen for a new manager screen (b).
- Reuse by generalization may also conflict with single-responsibility and interface-segregation principles when a specialized functionality is made to reuse a base one designed for its new siblings. For instance, if the standard reservation screen is adjusted to make room for manager screen it may take into account methods specific to managers (c).
Those problems may be compounded when reuse is applied to control and persistency artifacts: when a generic facility handler and the corresponding record are specialized for a new reservation targeting cars, they both reuse instantiation mechanisms and methods supporting multiple execution within shared address spaces; that is not the case for generalization as the new roots for facility handler and reservation cannot be achieved without modifying existing handler and recording of room reservations.
Since reuse through abstraction is based on inheritance mechanisms, that’s where the cases for reuse are to be examined.
Reuse by Inheritance
As noted above, reuse by generalization may undermine the design of boundaries, control, and persistency artifacts. While risks for boundaries are by nature local and limited to static descriptions, at control and persistency layers they affect instantiation mechanisms and shared execution at system level. And those those pitfalls can be circumscribed by a distinction between objects and aspects.
- Object types describe set of identified instances. In that case reuse by generalization means that objects targeted by new artifact must be identified and structured according the base descriptions whose reuse is under consideration. From a programming perspective object types will be eventually implemented as concrete classes.
- Aspect types describe behaviors or functionalities independently of the objects supporting them. Reuse of aspects can be understood as inheritance or composition. From a programming perspective they will be eventually implemented as interfaces or abstract classes.
Unfettered by programming languages constraints, generalization can be given consistent and unambiguous semantics. As a consequence, reuse by generalization can be introduced selectively to structures and aspects, with single inheritance for the former, multiple for the latter.
Not by chance, that distinction can be directly mapped to the taxonomy of design patterns proposed by the Gang of Four:
- Creational designs deal with the instanciation of objects.
- Structural designs deal with the building of structures.
- Behavioral designs deal with the functionalities supported by objects.
Applied to boundary artifacts, the distinction broadly coincides with the one between main windows (e.g Java Frames) on one hand, other graphical user interface components on the other hand, with the former identifying users sessions. For example, screens will be composed of a common header and specialized with components for managers and staffs. Support for reservation or cleaning activities will be achieved by inheriting corresponding aspects.
Freed from single inheritance constraints, the granularity of functionalities can be set independently of structures. Combined with selective inheritance, that will directly benefit open-closed, single-responsibility and interface-segregation principles.
The distinction between identifying structures on one hand, aspects on the other hand, is still more critical for artifacts supporting control functionalities as they must guarantee multiple execution within shared address spaces. In other words reuse of control artifacts should first and foremost be about managing identities and conflicting behaviors. And that can be best achieved when instantiation, structures, and aspects are designed independently:
- Whatever the targeted facility, a session must be created for, and identified by, each user request (#). Yet, since reservations cannot be processed independently, they must be managed under a single control (aka authority) within a single address space.
- That’s not the case for the consultation of details which can therefore be supported by artifacts whose identification is not bound to sessions.
Extensions, e.g for flights, will reuse creation and identification mechanisms along strong (binding) inheritance links; generalization will be safer as it will focus on clearly defined operations. Reuse of aspects will be managed separately along weak (non binding) inheritance links.
Reuse of control artifacts through selective inheritance may be especially useful with regard to dependency-inversion principle as it will facilitate the distinction between policy, mechanism, and utility layers.
Regarding artifacts supporting persistency, the main challenge is about domains consistency, best addressed by the Liskov substitution principle. According to that principle, a given set of instances should be equivalently represented independently of the level of abstraction. For example, the same instances of facilities should be represented identically as such or according their types. Clearly that will not be possible with overlapping subsets as the number of instances will differ depending on the level of abstraction.
But taxonomies being business driven, they usually overlap when the same objects are targeted by different business domains, as could be the case if reservations were targeting transport and lodging services while facility providers were managing actual resources with overlapping services. With selective inheritance it will be possible to reuse aspects without contradicting the substitution principle.
Reuse across Functional Architecture Layers
Contrary to reuse by delegation, which relates to instances, reuse by abstraction relates to types and should not be applied across functional architecture layers lest it would break the separation of concerns. Hence the importance of the distinction between reuse of structures, which may impact on identification, and the reuse of aspects, which doesn’t.
Given that reuse of development artifacts is to be governed along architecture levels (enterprise, system functionalities, platform technologies) on one hand, and functional layers (boundaries, controls, persistency) on the other hand, some principles must be set regarding eligible mechanisms.
Two mechanisms are available for type reuse across architecture levels:
- Semantics domains are defined by enterprise architecture and can be directly reused by functionalities.
- Design patterns enable the transformation of functional assets into technical ones.
Otherwise reuse policies must follow functional layers:
- Base entities are first anchored to business objects (1), with possible subsequent specialization (1b). Generalization must distinguish between structures and aspects lest to break continuity and consistency of representations.
- Base controls are anchored to business activities and may reuse entities (2). They may be specialized (2b). Generalization must distinguish between structures and aspects lest to break continuity and consistency of business processes.
- Base boundaries are anchored to roles and may reuse controls (3). They may be specialized (3b). Generalization must distinguish between structures and aspects lest to break continuity and consistency of sessions.