# Clean Architecture Principles
# Agenda 1. What is clean architecture? 3. The evolution of programming paradigms. 4. Principles.
## `$ whoami`  github.com/howard
What is clean architecture?
## The two values of software 
## The two ~values of~ requirements for software 
## Who is the architect? 
## Clean architecture is cheaper 
## Who is it for? 
## Who can live without it? 
# The evolution of programming paradigms
## Structured programming * `if` statements. * Loops. * Function calls.
## Structured programming >Structured programming imposes discipline on the direct transfer of control. --Uncle Bob
## Structured programming No example. There is no `goto` in TypeScript.
## Object-oriented programming (OOP) * Encapsulation. * Inheritance. * Polymorphism.
## OOP >Object-oriented programming imposes discipline on the *indirect* transfer of control. --Uncle Bob
## OOP - problem ``` class AnalyticsTracker { track(event: string): void { /* ... */ } } class PiwikTracker { track(event: string): void { /* ... */ } } const analytics = new AnalyticsTracker(); const piwik = new PiwikTracker(); ``` ``` // Whenever tracking something in the code: if (window.ga && analytics != null) { analyticsTracker.track('foo'); } else if (window._paq && piwik != null) { piwikTracker.track('foo'); } else { /* Nothing to do. */ } ```
## OOP - solution ``` abstract class Tracker { abstract track(event: string); static getInstance(window: Window): Tracker { if (window.ga) { return new AnalyticsTracker(); } else if (window._paq) { return new PiwikTracker(); } else { return new DummyTracker(); } } } class AnalyticsTracker extends Tracker { /* ... */ } class PiwikTracker extends Tracker { /* ... */ } class DummyTracker extends Tracker { /* ... */ } ``` ``` const tracker: Tracker = Tracker.getInstance(window); // Whenever tracking something in the code: tracker.track('foo'); ```
## Functional programming * Pure functions. * Higher order functions. * Immutability.
## Functional programming >Functional programming imposes discipline upon assignment. --Uncle Bob
## Functional programming - problem ``` let sensorData: number = readSensor(); sensorData = (sensorData - MIN_SENSOR_VALUE) / SENSOR_RANGE; sensorData = sensorData * 100; // Some stuff happening... console.log(sensorData); ```
## Functional programming - solution ``` const sensorData: number = readSensor(); const normalizedSensorData = sensorData - MIN_SENSOR_VALUE; const sensorDataRatio = normalizedSensorData / SENSOR_RANGE; const sensorDataPercent = sensorDataRatio * 100; // Some stuff happening... console.log(sensorData); ```
# Principles
## SOLID * **S**ingle responsibility principle. * **O**pen/closed principle. * **L**iskov substitution principle. * **I**nterface segregation principle. * **D**ependency inversion principle.
## Single responsibility principle (SRP) >A module should be responsible to one, and only one actor. --Uncle Bob
## SRP - problem ``` class Employee { constructor(public employeeData: object) {} // Needed by accounting: calculatePay(): number { /* ... */ } // Needed by HR: calculateHours(): number { /* ... */ } // Needed by manager: fire(): void { /* ... */ } } ```
## SRP - solution ``` class Employee { constructor(public employeeData: object) {} } class PayrollCalculator { calculatePay(employee: Employee): number { /* ... */ } } class HoursReporter { calculateHours(employee: Employee): number { /* ... */ } } class StaffDirectory { fire(employee: Employee): void { /* ... */ } } ```
## Open/closed principle (OCP) >A software artifact should be open for extension but closed for modification. --Uncle Bob
## OCP - problem (1) ``` class Logger { log(line: string): void { console.log(line); } } const logger = new Logger(); logger.log('This is pretty terrific!'); ``` The developer proceeds to instrument the whole project with loggers.
## OCP - problem (2) Boss says: *Let's also log to files!* ``` class Logger { log(line: string): void { console.log(line); fs.appendFileSync('app.log', line); } } const logger = new Logger(); logger.log('It goes into files. Oh my!'); ``` The frontend team had started to use `Logger` in the meantime. Their code is now broken.
## OCP - solution (1) ``` interface LogAppender { append(line: string): void; } class ConsoleAppender { append(line: string): void { console.log(line); } } class FileAppender { constructor(private path: string) {} append(line: string): void { fs.appendFileSync(this.path, line); } } ```
## OCP - solution (2) ``` class Logger { constructor(public appenders: LogAppender[]) {} log(line: string): void { this.appenders.forEach(appender => appender.append(line)); } } const logger = new Logger([new ConsoleAppender()]); logger.log('Works for the web team again.'); logger.appenders.push(new FileAppender('app.log')); logger.log('Now the backend team manager is happy too.'); ```
## Liskov substitution principle (LSP) >If S is declared subtype of T, objects of type S should behave as objects of type T are >expected to behave, if they are treated as objects of type T.
## LSP - violation (1) ``` class Rectangle { constructor(public width: number, public height: number) {} getArea() { return this.width * this.height; } } class Square extends Rectangle { constructor(public side: number) { super(side, side); } } ```
## LSP - violation (2) ``` const shape: Rectangle = new Square(2); console.log(shape.getArea()); // => 4 shape.height = 10; console.log(shape.getArea()); // => 20 (!) ```
## LSP - solution ``` interface Shape { getArea(): number; } class Rectangle implements Shape { // Same as before. } class Square implements Shape { constructor(public side: number) {} getArea(): number { return this.side * this.side; } } ```
## Interface segregation principle (ISP) >To each user of a shared component their own interface.
## ISP * More of an issue for compiled languages. * Shared interface changes -> need to recompile all dependents.
## Dependency inversion principle (DIP) * Don't refer to volatile concrete classes. * Don't derive from volatile concrete classes. * Don't override concrete functions. * Never mention the name of anything concrete and volatile.
## DIP - example [This sounds familiar...](#/3/6)
## DIP - Angular (1) ``` interface HelloService { sayHello(): string; } @Injectable({providedIn: 'root'}) class ProdHelloService implements HelloService { sayHello(): string { return 'Hello, production!'; } } @Injectable({providedIn: 'root'}) class DevHelloService implements HelloService { sayHello(): string { return 'Hello, development!'; } } ```
## DIP - Angular (2) ``` @Component({ providers: [ { provide: HelloService, useFactory: () => { if (environment.production) { return new ProdHelloService(); } else { return new DevHelloService(); } } } ] }) class AppComponent { /* ... */ } ```
# Recap * Everybody is an architect. * Favor changeable and partially functioning over fully functioning, but barely changeable. * **S**ingle responsibility principle. * **O**pen/closed principle. * **L**iskov substitution principle. * **I**nterface segregation principle. * **D**ependency inversion principle. * So much more!
# Further reading  