Slaying Sacred Cows:
Deconstructing
Dependency Injection
Tomer Gabel
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
Semantics
When I say “dependency injection”, you’re
probably thinking of this:
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
}
}
So did I.
1. THE “D” IN SOLID
Image: Peter von Bagh, “Just Frozen Water” via Flickr (CC0 1.0 Public Domain)
Back to Basics
• Single responsibility
• Open/closed
• Liskov substitution principle
• Interface segregation
• Dependency inversion
Image: Michael Feathers via MozaicWorks
Back to Basics
• Single responsibility
• Open/closed
• Liskov substitution principle
• Interface segregation
• Dependency inversion
Image: Michael Feathers via MozaicWorks
Dependency Inversion
• A simple idea
• Given a dependency:
– A must not depend on B
directly
OAuthProvider
MysqlUserStore
“A”
“B”
Dependency Inversion
• A simple idea
• Given a dependency:
– A must not depend on B
directly
– Instead, A depends on
an abstraction of B
– B depends on the
same abstraction
OAuthProvider
MysqlUserStore
UserStore
class
class
interface
“A”
“B”
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 of
one century is the
common sense
of the next.”
-- Henry Ward
Beecher
Image: Mathew Brady, “Henry Ward Beecher” via Library of Congress (Public Domain)
2. DECOUPLE
ME SOFTLY
Image: Mark Menzies, “Le Chav Sportif” via Flickr (CC-BY-NC-SA 2.0)
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;
}
}
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) {
// ...
}
}
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)
3. THINGS
GET HAIRY
Image: Matt Acevedo, “Alpaca” via Flickr (CC-BY 2.0)
Inversion of Control
• IoC is not a pattern
• It’s a design principle
• Traditionally:
– “Main” flow calls into
components
– Control flows back to
the “main” flow
Main (entry point)
• Configuration
• Bootstrapping
Event loop
• Dequeue
• Dispatch
Event handler
• Act on event
• Done
Inversion of Control
• IoC is not a pattern
• It’s a design principle
• With IoC:
– Control is surrendered
to a container
– Container calls into
components
Main (entry point)
• Setup
IoC container
• Bootstrapping/wiring
• Event loop
Event handler
• Act on event
• Done
Inversion of Control
• IoC means many things
– Servlet containers
– Plugin systems
– Stream computing
– “DI” containers
• We’ll focus on the latter
IoC & DI
• Consider Spring/Guice
– A runtime container
– Manages components
– … including lifecycle
– … and automatic wiring
Image: ImgFlip
Perceived Benefits
• Why use a container?
– Simplify wiring
– Simplify testing
– Dynamic configuration
– Support for AOP
• Let’s consider each
Image: ImgFlip
Perceived Benefits
• Why use a container?
– Simplify wiring
– Simplify testing
– Dynamic configuration
– Support for AOP
• Let’s consider each
Image: ImgFlip
Simplified Wiring
class 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);
}
Simplified Wiring
class 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);
}
}
Simplified Wiring
class 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);
}
}
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)
Simplify Testing
• Proponents will tell you:
1. Bring up a container
2. Swap out components
3. Bob’s your uncle
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.
Simplify Testing
• Congratulations!
• You’re doing
deconstructive
testing
Wha-huh?
Constructive testing Deconstructive testing
MysqlEventStore
DataSource
SiteService
MysqlEventStore
DataSource
MysqlSnapshotStore
DataSource
Clock
Wha-huh?
Constructive testing Deconstructive testing
MysqlEventStore
DataSource
SiteService
MysqlEventStore
DataSource
MockSnapshotStore
Fixed
Clock
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)
IN SUMMARY…
IoC is a solution in
search of a problem
… except …
• With huge codebases
– Read: “Monoliths”
– Read: “Enterprise”
• Enables a tradeoff
– Developer discipline
– Code coherence,
simplicity, navigability
… 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.
QUESTIONS?
Thank you for listening
tomer@tomergabel.com
@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.

Slaying Sacred Cows: Deconstructing Dependency Injection

  • 1.
  • 2.
    Full Disclosure • Iwas 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
  • 3.
    Semantics When I say“dependency injection”, you’re probably thinking of this: public class BillingModule extends AbstractModule { @Override protected void configure() { bind(TransactionLog.class).to(DatabaseTransactionLog.class); bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class); } } So did I.
  • 4.
    1. THE “D”IN SOLID Image: Peter von Bagh, “Just Frozen Water” via Flickr (CC0 1.0 Public Domain)
  • 5.
    Back to Basics •Single responsibility • Open/closed • Liskov substitution principle • Interface segregation • Dependency inversion Image: Michael Feathers via MozaicWorks
  • 6.
    Back to Basics •Single responsibility • Open/closed • Liskov substitution principle • Interface segregation • Dependency inversion Image: Michael Feathers via MozaicWorks
  • 7.
    Dependency Inversion • Asimple idea • Given a dependency: – A must not depend on B directly OAuthProvider MysqlUserStore “A” “B”
  • 8.
    Dependency Inversion • Asimple idea • Given a dependency: – A must not depend on B directly – Instead, A depends on an abstraction of B – B depends on the same abstraction OAuthProvider MysqlUserStore UserStore class class interface “A” “B”
  • 9.
    The Verdict • Dependency inversionis old hat – Seems obvious now – First postulated by Uncle Bob in 1994 (!) • We’ve come a long way since! “The philosophy of one century is the common sense of the next.” -- Henry Ward Beecher Image: Mathew Brady, “Henry Ward Beecher” via Library of Congress (Public Domain)
  • 10.
    2. DECOUPLE ME SOFTLY Image:Mark Menzies, “Le Chav Sportif” via Flickr (CC-BY-NC-SA 2.0)
  • 11.
    Dependency Injection • Let’sassume 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; } }
  • 12.
    Dependency Injection • DIstipulates: – 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) { // ... } }
  • 13.
    The Verdict • Dependency injectionis 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)
  • 14.
    3. THINGS GET HAIRY Image:Matt Acevedo, “Alpaca” via Flickr (CC-BY 2.0)
  • 15.
    Inversion of Control •IoC is not a pattern • It’s a design principle • Traditionally: – “Main” flow calls into components – Control flows back to the “main” flow Main (entry point) • Configuration • Bootstrapping Event loop • Dequeue • Dispatch Event handler • Act on event • Done
  • 16.
    Inversion of Control •IoC is not a pattern • It’s a design principle • With IoC: – Control is surrendered to a container – Container calls into components Main (entry point) • Setup IoC container • Bootstrapping/wiring • Event loop Event handler • Act on event • Done
  • 17.
    Inversion of Control •IoC means many things – Servlet containers – Plugin systems – Stream computing – “DI” containers • We’ll focus on the latter
  • 18.
    IoC & DI •Consider Spring/Guice – A runtime container – Manages components – … including lifecycle – … and automatic wiring Image: ImgFlip
  • 19.
    Perceived Benefits • Whyuse a container? – Simplify wiring – Simplify testing – Dynamic configuration – Support for AOP • Let’s consider each Image: ImgFlip
  • 20.
    Perceived Benefits • Whyuse a container? – Simplify wiring – Simplify testing – Dynamic configuration – Support for AOP • Let’s consider each Image: ImgFlip
  • 21.
    Simplified Wiring class 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); }
  • 22.
    Simplified Wiring class 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); } }
  • 23.
    Simplified Wiring class 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); } }
  • 24.
    Simplified Wiring • Notangible 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)
  • 25.
    Simplify Testing • Proponentswill tell you: 1. Bring up a container 2. Swap out components 3. Bob’s your uncle
  • 26.
    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.
  • 27.
    Simplify Testing • Congratulations! •You’re doing deconstructive testing
  • 28.
    Wha-huh? Constructive testing Deconstructivetesting MysqlEventStore DataSource SiteService MysqlEventStore DataSource MysqlSnapshotStore DataSource Clock
  • 29.
    Wha-huh? Constructive testing Deconstructivetesting MysqlEventStore DataSource SiteService MysqlEventStore DataSource MockSnapshotStore Fixed Clock
  • 30.
    Simplify Testing • Thisis 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)
  • 31.
    IN SUMMARY… IoC isa solution in search of a problem
  • 32.
    … except … •With huge codebases – Read: “Monoliths” – Read: “Enterprise” • Enables a tradeoff – Developer discipline – Code coherence, simplicity, navigability
  • 33.
    … 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.
  • 34.
    QUESTIONS? Thank you forlistening tomer@tomergabel.com @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.