slaying sacred cows: deconstructing dependency injection

34
Slaying Sacred Cows: Deconstructing Dependency Injection Tomer Gabel

Upload: tomer-gabel

Post on 21-Jan-2018

570 views

Category:

Engineering


2 download

TRANSCRIPT

Page 1: Slaying Sacred Cows: Deconstructing Dependency Injection

Slaying Sacred Cows:Deconstructing

Dependency Injection

Tomer Gabel

Page 2: Slaying Sacred Cows: Deconstructing Dependency Injection

Full Disclosure

• I was never a fan

• I tried researching this properly…

– Read a ton of material

– Interviewed people

– Sat and thought

• Still turned out a rant

Image: ImgFlip

Page 3: Slaying Sacred Cows: Deconstructing Dependency Injection

Semantics

When I say “dependency injection”, you’re probably thinking of this:

public class BillingModule extends AbstractModule {@Overrideprotected void configure() {bind(TransactionLog.class).to(DatabaseTransactionLog.class);bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);

}}

So did I.

Page 4: Slaying Sacred Cows: Deconstructing Dependency Injection

1. THE “D” IN SOLIDImage: Peter von Bagh, “Just Frozen Water” via Flickr (CC0 1.0 Public Domain)

Page 5: Slaying Sacred Cows: Deconstructing Dependency Injection

Back to Basics

• Single responsibility

• Open/closed

• Liskov substitution principle

• Interface segregation

• Dependency inversion

Image: Michael Feathers via MozaicWorks

Page 6: Slaying Sacred Cows: Deconstructing Dependency Injection

Back to Basics

• Single responsibility

• Open/closed

• Liskov substitution principle

• Interface segregation

• Dependency inversion

Image: Michael Feathers via MozaicWorks

Page 7: Slaying Sacred Cows: Deconstructing Dependency Injection

Dependency Inversion

• A simple idea

• Given a dependency:– A must not depend on B

directly

OAuthProvider

MysqlUserStore

“A”

“B”

Page 8: Slaying Sacred Cows: Deconstructing Dependency Injection

Dependency Inversion

• A simple idea

• Given a dependency:– A must not depend on B

directly

– Instead, A depends onan abstraction of B

– B depends on thesame abstraction

OAuthProvider

MysqlUserStore

UserStore

class

class

interface

“A”

“B”

Page 9: Slaying Sacred Cows: Deconstructing Dependency Injection

The Verdict

• Dependency inversion is old hat

– Seems obvious now

– First postulated by Uncle Bob in 1994 (!)

• We’ve come a long way since!

“The philosophy ofone century is the

common senseof the next.”

-- Henry Ward Beecher

Image: Mathew Brady, “Henry Ward Beecher” via Library of Congress (Public Domain)

Page 10: Slaying Sacred Cows: Deconstructing Dependency Injection

2. DECOUPLEME SOFTLY

Image: Mark Menzies, “Le Chav Sportif” via Flickr (CC-BY-NC-SA 2.0)

Page 11: Slaying Sacred Cows: Deconstructing Dependency Injection

Dependency Injection

• Let’s assume SOLID…

• Given a dependency:

– Who owns it?

– What is the lifecycle?

• Traditionally:

– The depending service

manages everything

class UserService {

private UserStore store =

new MysqlUserStore(Config.JDBC_URL);

bool authenticate(String userToken) {

UserContext user =

store.lookup(userToken);

return user != null

? user.isActive()

: false;

}

}

Page 12: Slaying Sacred Cows: Deconstructing Dependency Injection

Dependency Injection

• DI stipulates:

– Services should not

build dependencies

– But instead receive them

– Dependencies are state

• It does not stipulate

how to implement this

class UserService {

private UserStore store;

public UserService(UserStore store) {

this.store = store;

}

bool authenticate(String userToken) {

// ...

}

}

Page 13: Slaying Sacred Cows: Deconstructing Dependency Injection

The Verdict

• Dependency injection is good

• If taken at face value:

– No frameworks

– No containers

– No reflection

– Simply common sense

Image: Tomas Catelazo via Wikimedia Commons (CC-BY-SA 4.0)

Page 14: Slaying Sacred Cows: Deconstructing Dependency Injection

3. THINGS GET HAIRY

Image: Matt Acevedo, “Alpaca” via Flickr (CC-BY 2.0)

Page 15: Slaying Sacred Cows: Deconstructing Dependency Injection

Inversion of Control

• IoC is not a pattern

• It’s a design principle

• Traditionally:

– “Main” flow calls intocomponents

– Control flows back to the “main” flow

Main (entry point)

• Configuration

• Bootstrapping

Event loop

• Dequeue

• Dispatch

Event handler

• Act on event

• Done

Page 16: Slaying Sacred Cows: Deconstructing Dependency Injection

Inversion of Control

• IoC is not a pattern

• It’s a design principle

• With IoC:

– Control is surrendered to a container

– Container calls intocomponents

Main (entry point)

• Setup

IoC container

• Bootstrapping/wiring

• Event loop

Event handler

• Act on event

• Done

Page 17: Slaying Sacred Cows: Deconstructing Dependency Injection

Inversion of Control

• IoC means many things

– Servlet containers

– Plugin systems

– Stream computing

– “DI” containers

• We’ll focus on the latter

Page 18: Slaying Sacred Cows: Deconstructing Dependency Injection

IoC & DI

• Consider Spring/Guice

– A runtime container

– Manages components

– … including lifecycle

– … and automatic wiring

Image: ImgFlip

Page 19: Slaying Sacred Cows: Deconstructing Dependency Injection

Perceived Benefits

• Why use a container?

– Simplify wiring

– Simplify testing

– Dynamic configuration

– Support for AOP

• Let’s consider each

Image: ImgFlip

Page 20: Slaying Sacred Cows: Deconstructing Dependency Injection

Perceived Benefits

• Why use a container?

– Simplify wiring

– Simplify testing

– Dynamic configuration

– Support for AOP

• Let’s consider each

Image: ImgFlip

Page 21: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplified Wiringclass MyApp {

DBI db = new DBIFactory().build(...);

EventStore eventStore =

new MysqlEventStore(db);

SnapshotStore snapshotStore =

new MysqlSnapshotStore(db);

Clock clock =

Clock.systemUTC();

SiteService siteService =

new DefaultSiteService(

eventStore, snapshotStore, clock);

}

Page 22: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplified Wiringclass MyApp {

DBI db = new DBIFactory().build(...);

EventStore eventStore =

new MysqlEventStore(db);

SnapshotStore snapshotStore =

new MysqlSnapshotStore(db);

Clock clock =

Clock.systemUTC();

SiteService siteService =

new DefaultSiteService(

eventStore, snapshotStore, clock);

}

class MyAppModule extends AbstractModule {

@Override

protected void configure() {

bind(DBI.class).toProvider(...);

bind(EventStore.class)

.to(MysqlEventStore.class);

bind(SnapshotStore.class)

.to(MysqlSnapshotStore.class);

bind(Clock.class)

.toProvider(Clock::systemUTC);

bind(SiteService.class)

.to(DefaultSiteService.class);

}

}

Page 23: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplified Wiringclass MyApp {

DBI db = new DBIFactory().build(...);

EventStore eventStore =

new MysqlEventStore(db);

SnapshotStore snapshotStore =

new MysqlSnapshotStore(db);

Clock clock =

Clock.systemUTC();

SiteService siteService =

new DefaultSiteService(

eventStore, snapshotStore, clock);

}

class MyAppModule extends AbstractModule {

@Override

protected void configure() {

bind(DBI.class).toProvider(...);

bind(EventStore.class)

.to(MysqlEventStore.class);

bind(SnapshotStore.class)

.to(MysqlSnapshotStore.class);

bind(Clock.class)

.toProvider(Clock::systemUTC);

bind(SiteService.class)

.to(DefaultSiteService.class);

}

}

Page 24: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplified Wiring

• No tangible benefit!

– Wiring is trivial

• Real, tangible downsides

– Startup time

– Code navigability

– Dynamic/reflective magic

Image: André Nordstrand, “Loss of common sense” via Flickr (CC-BY-NC 2.0)

Page 25: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplify Testing

• Proponents will tell you:

1. Bring up a container

2. Swap out components

3. Bob’s your uncle

Page 26: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplify Testing

deconstruction (source: dictionary.com)

Noun

1. a technique of literary analysis that regards meaning as resulting from the differences between words rather than their reference to the things they stand for.

Page 27: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplify Testing

• Congratulations!

• You’re doing

deconstructive

testing

Page 28: Slaying Sacred Cows: Deconstructing Dependency Injection

Wha-huh?

Constructive testing Deconstructive testing

MysqlEventStore

DataSource

SiteService

MysqlEventStore

DataSource

MysqlSnapshotStore

DataSource

Clock

Page 29: Slaying Sacred Cows: Deconstructing Dependency Injection

Wha-huh?

Constructive testing Deconstructive testing

MysqlEventStore

DataSource

SiteService

MysqlEventStore

DataSource

MockSnapshotStoreFixedClock

Page 30: Slaying Sacred Cows: Deconstructing Dependency Injection

Simplify Testing

• This is a bad idea– Hard to reason about

– Have to deal with subtle interactions

– Does not reflect your unit structure

• Most importantly…– Leads to poor design!

Image: Viewminder, “Strange Bedfellows” via Flickr (CC-BY-NC-ND 2.0)

Page 31: Slaying Sacred Cows: Deconstructing Dependency Injection

IN SUMMARY…

IoC is a solution in search of a problem

Page 32: Slaying Sacred Cows: Deconstructing Dependency Injection

… except …

• With huge codebases

– Read: “Monoliths”

– Read: “Enterprise”

• Enables a tradeoff

– Developer discipline

– Code coherence, simplicity, navigability

Page 33: Slaying Sacred Cows: Deconstructing Dependency Injection

… except …

• With huge codebases

– Read: “Monoliths”

– Read: “Enterprise”

• Enables a tradeoff

– Developer discipline

– Code coherence, simplicity, navigability

Corollary:

If you’re seeing benefit from IoC, your codebase is already out of control.

Page 34: Slaying Sacred Cows: Deconstructing Dependency Injection

QUESTIONS?Thank you for listening

[email protected]

@tomerg

http://engineering.wix.com

Sample Project:

http://tinyurl.com/event-sourcing-sample

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0

International License.