Architector Framework Overview
- Architector Framework is an automated system that reconstructs Java architectures by formalizing dependencies and aligning source code with layered conceptual models.
- The framework employs search-based heuristics, including greedy clustering and hill-climbing, to optimize fitness metrics and reduce architectural violations.
- Architector supports precise source-level refactorings such as MoveMethod, MoveConstant, and ExcludeParameter to systematically reverse architectural erosion.
The Architector Framework is an automatic system for software architecture reconstruction and source code refactoring, aimed at mitigating or reversing architectural erosion in non-trivial Java systems. By formally modeling conceptual software architecture, extracting dependency structures, and supporting a set of source-level migration operations, Architector enables the alignment of physical system architecture with a desirable layered conceptual model. Its prototype implementation demonstrates the use of clustering, hill-climbing, and search-based source transformation to reconstruct and realign software architectures, facilitating the reduction of architectural violations and preservation of maintainability, testability, and development velocity (Schmidt et al., 2014).
1. Formal Definition of Conceptual Architecture
Architector models a system’s architecture as a tuple where is the set of implementation units (Java classes, interfaces, modules), is an ordered set of architectural layers, assigns each implementation unit to exactly one layer, and orders the layers.
A set of static dependencies (including method calls, field accesses, inheritance, parameter passing) is extracted from the source code. A conceptual architecture is one in which the following property holds ideally: for all , either or , i.e., dependencies do not point from a lower to a higher layer.
Internally, architectures are represented as directed, layered graphs with edge weights set to —the count of distinct dependencies from to . Architector’s metamodel is a domain-specific EMF-style metamodel with Layer and ImplementationUnit classes, a dependsOn reference, and a layerIndex attribute. For visualization and validation, UML component diagrams and JHotDraw-based node-link views (swimlane layouts) are exported.
2. Algorithms for Architecture Reconstruction
Architector performs architecture reconstruction using a three-stage, search-based heuristic process:
(1) Parsing & Dependency Extraction: Java source is parsed using RECODER to build an abstract syntax tree (AST) and extract dependencies with counts (calls, accesses, etc.). is computed for coupling quantification.
(2) Initial Greedy Clustering: Starting with layers (increasing up to as warranted), implementation units are greedily assigned to layers to maximize local cohesion and minimize inter-layer coupling.
(3) Steepest-Ascent Hill-Climbing: Single units are iteratively moved between layers to optimize a global fitness function (described below) until no single move yields improvement.
The framework employs the following metrics:
- Cohesion: , where .
- Coupling: for .
- Solution Quality (SQ):
with violations partitioned into resolvable () and unresolvable (), and
Higher indicates fewer/less severe violations and more layers.
The reconstruction algorithm can be summarized as:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function reconstructArchitecture(I, Dep):
bestμ ← greedyInitial(I, Dep)
bestScore ← SQ(bestμ)
improved ← true
while improved:
improved ← false
for each i in I, for each layer ℓ' ≠ bestμ(i):
μ' ← bestμ with μ'(i)=ℓ'
score' ← SQ(μ')
if score' > bestScore:
bestμ ← μ'; bestScore ← score'; improved ← true
break
return (bestμ, bestScore) |
3. Source Code Refactoring and Migration
Architector supports three atomic source-level transformations, defined for classes , method , and parameter :
- MoveMethod: Relocates from to if preconditions (no name conflict, only statics/parameters referenced) are met. All call sites and related imports/qualifiers are updated.
- MoveConstant: Semantics and constraints are analogous to MoveMethod.
- ExcludeParameter: Removes from if not essential to API and relocates statements using to call sites or applies observer patterns.
Refactoring is orchestrated as an iterative search: for each generation, all single-step refactoring variants are produced, their violation counts and SQs are calculated, and the variant with minimal violations (or, on tie, maximal SQ) is selected. This process repeats until no further improvement is possible or a generation budget is exhausted.
The primary cost is:
with secondary cost . Optimization proceeds as greedy steepest descent on the cost vector.
4. Framework Structure and Operational Workflow
Architector consists of the following high-level modules:
- Parser & Dependency Analyzer: Reads all Java sources, constructs the AST, and extracts the dependency graph with weights.
- Conceptual Reconstruction Engine: Performs clustering and hill-climbing to generate the reflexion model .
- Refactoring/Migration Engine: Applies supported refactorings and evaluates the resulting code variants.
- User Interface: Provides visualization of layers, implementation units, and violation statistics; user controls enable freezing assignments or adjusting layer counts.
The operational workflow is as follows:
- The user specifies a Java project root.
- All source files are scanned; the dependency graph is constructed.
- Reconstruction yields the initial layered model, .
- The UI displays proposed layers and violations; user may intervene.
- The migration engine enumerates and applies single refactorings.
- Each variant is re-analyzed; violation counts and recomputed.
- The best variant is chosen for the next generation; steps (5)–(7) are repeated.
- Output is the refactored source tree with architecture aligned as closely as possible to the conceptual model.
5. Empirical Assessment and Case Studies
Architector’s evaluation utilized controlled experiments with a 15-class Java MVC system (5 Model, 5 View, 5 Controller). The system’s ideal SQ (perfect 3-layered architecture, no erosion) is 0.66185. Erosion was modeled by injecting violations (e.g., misplaced methods, parameters, constants) across runs. Observed trends are summarized:
| N | Layers | Misplaced Units | Detected Violations | SQ |
|---|---|---|---|---|
| 1 | 3 | 0 | 1 | 0.64835 |
| 3 | 3 | 1 | 2 | 0.55833 |
| 6 | 3 | 2 | 4 | 0.42652 |
| 11 | 2 | 5 | 8 | 0.52345 |
SQ degrades with growing erosion, though reconstructions preserve meaningful groupings (Model/View/Controller) until maximal deterioration causes reduction to two layers. In source code migration experiments with the most eroded instance (10 violations, fixed at 2 layers), violations were reduced rapidly within three to four generational refining steps (from 10 to 3 violations). SQ fluctuates but overall improves with successive generations.
6. Illustrative Example on a Java Mini-System
A minimal User system with classes UserController, UserModel, and UserView is mapped as:
- Layer₁: {UserController} (Controllers)
- Layer₂: {UserModel, DbConnector} (Models)
- Layer₃: {UserView} (Views)
With only legal dependencies (Controller → Model/View, Model → DbConnector), Architector’s clustering reconstructs the intended layering in one step; no violations. If a violation is introduced (e.g., UserModel invoking UserView.render), this unity is detected as a lower-to-higher dependency violation.
Refactoring options are proposed:
- Move
auditmethod fromUserModeltoUserController - Exclude parameter and inject via Controller
- Move
renderfromUserViewtoUserModel(semantically undesirable)
Architector’s refactoring engine moves the offending method, eliminating violations and improving solution quality. The final structure exhibits only directional dependencies respecting the conceptual architecture; violations are reduced to zero, and the architecture is realigned.
This comprehensive exposition details Architector’s mathematical model, reconstruction and refactoring processes, the system’s modular composition and workflow, empirical evaluation results, and an end-to-end Java example, as delineated in (Schmidt et al., 2014).