Robust Contracts

LSP keeps subtypes honest. ISP keeps interfaces small enough that clients only depend on what they use. DIP keeps high-level policy from depending on low-level detail.

If a child class throws on a base method, the contract was too broad or the hierarchy was wrong. If a service constructor hardcodes a concrete database client, the dependency graph is too rigid to test cleanly.

Minimal Notes

  • LSP demands substitutable subtypes.
  • ISP removes unused methods from client-facing interfaces.
  • DIP moves concrete choices out of high-level modules and into injected abstractions.

Lab 2.1: Fixing Broken Hierarchy Contracts

Split read and write contracts so ReadOnlyReplica does not inherit an operation it cannot honor.

Substitution Contract Runner

Exercise 2.1: Fixing Broken Hierarchy Contracts

Analyzer idle

Split read and write contracts so read-only replicas are never forced to fake write support.

Architecture Signals
Base contractread + write fused
ReadOnlyReplica.writethrows at runtime
Validation Trace
  1. Edit the code, then run the architecture check.

Lab 2.2: Decoupling Hardcoded Adapters

Inject IDatabaseAdapter into AnalyticsDashboard, then test it with a thin mock instead of a live PostgreSQLClient.

Adapter Dependency Scanner

Exercise 2.2: Decoupling Hardcoded Adapters

Analyzer idle

Inject an IDatabaseAdapter into AnalyticsDashboard and test with a mock client.

Architecture Signals
Constructornew PostgreSQLClient
Test pathrequires real database
Validation Trace
  1. Edit the code, then run the architecture check.