This document describes the refactored architecture of the Universe Explorer application, which has been redesigned following SOLID principles to improve maintainability, extensibility, and code organization.
The original app.js file was a monolithic class of 2,907 lines that violated multiple SOLID principles:
- Single Responsibility Principle (SRP): The
UniverseExplorerclass handled rendering, UI, physics, textures, animations, and model creation - Open/Closed Principle (OCP): Adding new models required modifying the main class
- Interface Segregation Principle (ISP): Large interfaces with methods not relevant to all consumers
- Dependency Inversion Principle (DIP): Tight coupling to specific implementations
The refactored architecture follows SOLID principles and separates concerns into focused, maintainable components:
js/
├── core/ # Core application components
│ ├── UniverseExplorer.js # Main orchestrator (follows SRP)
│ ├── SceneRenderer.js # 3D rendering and lighting (SRP)
│ ├── TextureManager.js # Texture loading and generation (SRP)
│ ├── UIController.js # User interface management (SRP)
│ ├── AnimationSystem.js # Animation calculations (SRP)
│ └── PhysicsEngine.js # Physics simulation (SRP)
├── models/ # Universe model implementations
│ ├── BaseModel.js # Abstract base class (Template Method)
│ ├── ModelFactory.js # Factory pattern (OCP)
│ ├── AristotleModel.js # Geocentric model
│ ├── PtolemaicModel.js # Epicycle model
│ ├── CopernicanModel.js # Heliocentric model
│ ├── GalileanModel.js # With Jupiter's moons
│ ├── KeplerModel.js # Elliptical orbits
│ └── NewtonianModel.js # Physics simulation
└── app-refactored.js # New entry point
Each class now has a single, well-defined responsibility:
- SceneRenderer: Only handles 3D scene setup, lighting, and rendering
- TextureManager: Only manages texture loading and procedural generation
- UIController: Only handles user interface interactions and state
- AnimationSystem: Only calculates celestial body animations
- PhysicsEngine: Only handles physics simulation and collision detection
The system is now open for extension but closed for modification:
// Adding a new model doesn't require changing existing code
export class CustomModel extends BaseModel {
async createCelestialBodies() {
// Implement your model here
}
}
// Register the new model
modelFactory.registerModel('custom', CustomModel);All model classes properly extend BaseModel and can be used interchangeably:
// All models follow the same interface
const model = await modelFactory.createModel(modelName, scene, textureManager);
model.updateAnimations(time, speed);Components only depend on the interfaces they actually use:
// TextureManager only provides texture-related methods
textureManager.getTexture(name);
textureManager.hasTexture(name);
// SceneRenderer only provides rendering methods
renderer.updateLighting(settings);
renderer.setVisualElementVisible(type, visible);High-level modules depend on abstractions, not concrete implementations:
// UniverseExplorer depends on abstractions
this.renderer = new SceneRenderer(); // Abstraction
this.modelFactory = new ModelFactory(); // Abstraction
// Models depend on the abstract BaseModel
export class NewModel extends BaseModel { // Depends on abstraction
// Implementation details...
}BaseModel defines the algorithm for model creation:
// Template method defines the steps
async create() {
await this.createCelestialBodies(); // Abstract - must implement
this.setupOrbits(); // Optional override
this.setupAdditionalElements(); // Optional override
}ModelFactory creates models without exposing instantiation logic:
// Factory encapsulates object creation
async createModel(modelName, scene, textureManager) {
const ModelClass = this.modelRegistry[modelName];
const model = new ModelClass(modelName, scene, textureManager);
await model.create();
return model;
}UI events are handled through callbacks:
// UI notifies other components of changes
this.uiController.onModelChange = (modelName) => {
this.switchModel(modelName);
};To add a new universe model, follow these steps:
// js/models/MyCustomModel.js
import { BaseModel } from './BaseModel.js';
export class MyCustomModel extends BaseModel {
async createCelestialBodies() {
// Create your celestial bodies
const centralBody = this.createCelestialBody('Center', 2, 0xff0000, 0, 0, 0);
this.celestialBodies.center = { object: centralBody };
// Add planets, moons, etc.
const planet = this.createCelestialBody('Planet', 1, 0x0000ff, 10, 0, 0);
this.celestialBodies.planet = {
object: planet,
distance: 10,
speed: 1.0
};
}
updateAnimations(time, speed) {
// Override default animation behavior if needed
super.updateDefaultAnimations(time, speed);
// Add custom animation logic
}
}// js/models/ModelFactory.js
import { MyCustomModel } from './MyCustomModel.js';
export class ModelFactory {
constructor() {
this.modelRegistry = {
// ... existing models
custom: MyCustomModel, // Add your model here
};
}
}Update the HTML to include your new model in the UI:
<button class="model-btn" data-model="custom">My Custom Model</button>- Focused classes: Each class has a single responsibility
- Clear separation: Easy to locate and modify specific functionality
- Reduced complexity: Smaller, more manageable code files
- Easy model addition: New models require minimal changes to existing code
- Plugin architecture: Components can be easily replaced or extended
- Future-proof: Architecture supports adding new features without breaking existing code
- Isolated components: Each component can be tested independently
- Mockable dependencies: Easy to create mocks for unit testing
- Clear interfaces: Well-defined inputs and outputs for testing
- Modular components: Components can be reused in other projects
- Abstract base classes: Common functionality is shared through inheritance
- Utility methods: Shared utilities are centralized and reusable
The refactored architecture maintains the same performance characteristics as the original while providing better organization:
- Lazy loading: Components are only initialized when needed
- Efficient memory management: Proper cleanup and disposal methods
- Optimized rendering: Scene management is centralized in SceneRenderer
- Texture caching: TextureManager handles efficient texture reuse
To migrate from the original to the refactored architecture:
Change the script reference in your HTML:
<!-- Old -->
<script type="module" src="js/app.js"></script>
<!-- New -->
<script type="module" src="js/app-refactored.js"></script>The refactored version maintains the same external interface and UI, so no other changes are needed.
The new architecture makes several future enhancements easier to implement:
- Tycho Brahe's Model: Hybrid geocentric-heliocentric system
- Modern Cosmological Models: Dark matter, dark energy visualization
- Exoplanet Systems: Multiple star systems with discovered exoplanets
- Gravitational Waves: Visualization of spacetime distortions
- Relativistic Effects: Time dilation and length contraction
- N-body Simulation: More complex gravitational interactions
- Guided Tours: Step-by-step explanations of each model
- Interactive Experiments: What-if scenarios and parameter modification
- Assessment Tools: Quizzes and knowledge checks
- WebGL 2.0: Enhanced graphics capabilities
- Web Workers: Background physics calculations
- Progressive Loading: Improved startup performance
The refactored architecture transforms a monolithic 2,907-line class into a well-organized, maintainable system that follows SOLID principles. This improves code quality, makes the system easier to understand and modify, and provides a solid foundation for future enhancements.
Key improvements:
- ✅ Single Responsibility: Each class has one clear purpose
- ✅ Open/Closed: Easy to add new models without modifying existing code
- ✅ Better Organization: Logical file structure and separation of concerns
- ✅ Enhanced Maintainability: Smaller, focused classes are easier to maintain
- ✅ Future-Proof: Architecture supports easy extension and modification
The new architecture maintains all existing functionality while providing a much better foundation for future development and maintenance.