slaying sacred cows: deconstructing dependency injection
TRANSCRIPT
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 {@Overrideprotected void configure() {bind(TransactionLog.class).to(DatabaseTransactionLog.class);bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
}}
So did I.
1. THE “D” IN SOLIDImage: 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 onan abstraction of B
– B depends on thesame 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 ofone century is the
common senseof the next.”
-- Henry Ward Beecher
Image: Mathew Brady, “Henry Ward Beecher” via Library of Congress (Public Domain)
2. DECOUPLEME 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 intocomponents
– 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 intocomponents
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 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);
}
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);
}
}
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);
}
}
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
MockSnapshotStoreFixedClock
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
@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.