Architecture / SOLID Principles Simulator

"Interactive simulations demonstrating the architectural shift from fragile, tightly-coupled code to resilient, extensible systems."

1. Single Responsibility (SRP)
2. Open/Closed (OCP)
3. Liskov Substitution (LSP)
4. Interface Segregation (ISP)
5. Dependency Inversion (DIP)
Violated (Fragile) Refactored (Resilient)

class UserDashboard

connect_database()
execute_sql_query()
calculate_user_metrics()
generate_html_view()
send_http_response()

UserRepository

fetch_data()

UserMetricsService

calculate()

UserController

render_view()

Single Responsibility Violated

The Problem: The `UserDashboard` class handles database access, business logic, and UI rendering.

Move the slider below to simulate changing the "Business Logic" requirements. Notice how modifying the logic risks breaking the database and UI components because they are entangled in the same file.

The Solution: The class is split into Repository (Data), Service (Logic), and Controller (UI).

Move the slider now. Modifying the business requirements only affects the `UserMetricsService`. The database and UI layers remain perfectly isolated and safe from unintended side effects.

Drag to inject new requirement complexity.

class AIChatEngine

process_prompt(model, text)
if model == 'gemini':
  return init_gemini(text)

Open/Closed Violated

The Problem: The core engine uses a massive `if/else` statement. Every time we add a new AI model, we have to physically open and modify the `AIChatEngine` source code. This risks breaking the existing Gemini integration.

The Solution: The `AIChatEngine` is closed for modification. It relies on a generic `IAIPlugin` interface. New models are added as standalone plugins that hook into the system without touching the core code.

🚗

Runtime Exception

TypeError: ToyElectricCar has no attribute 'engine_fuel'

Liskov Substitution Violated

The Problem: We have a function expecting a `Vehicle` base class. It calls `start_engine()` and checks `fuel_level`. We substitute a subclass `ToyCar`. Because `ToyCar` doesn't have fuel, it breaks the expected semantic behavior of the base class, causing a crash.

The Solution: `ToyCar` should not inherit from `Vehicle` if it cannot substitute it completely. Instead, we redesign the hierarchy so the execution context relies on a proper interface (e.g., `IMovable`), ensuring safe substitutions.

interface IMultiMachine

print_document()
scan_document()
fax_document()
IPrinter { print() }
IScanner { scan() }
IFax { fax() }
Basic Printer Implements: print()
Office Scanner Implements: scan()

Interface Segregation Violated

The Problem: Both devices are forced to implement a fat `IMultiMachine` interface. If we change the signature of `scan_document()`, the Basic Printer (which doesn't even use scanning) gets recompiled or throws an error due to the changed contract.

The Solution: The fat interface is segregated into `IPrinter`, `IScanner`, etc. Now, changing the scanner interface only affects the Scanner client. The Printer is blissfully unaware.

Simulates adding a new parameter to the scan method.

OrderProcessor

High-Level Business Logic

interface IDatabase { save() }

MySQLConnection

Low-Level Details

Dependency Inversion Violated

The Problem: The high-level `OrderProcessor` directly imports and instantiates the concrete `MySQLConnection`. If we want to switch to PostgreSQL, we have to rewrite the high-level business logic, which is risky.

The Solution: Both layers now depend on an abstraction (`IDatabase`). The high-level module just talks to the interface. We can swap the underlying database seamlessly via Dependency Injection without touching the business logic.