Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Slaying Sacred Cows: Deconstructing Dependency Injection

Slaying Sacred Cows: Deconstructing Dependency Injection

A talk given at GeeCON 2017 in Prague, Czech Republic.

This talk revisits dependency injection, and attempts to answer a single question honestly, or at least while pointing out and acknowledging the biases at play: "is dependency injection a good thing?"

Dependency injection has fast established itself as a major design pattern in modern software. No longer the province of server-side and enterprise software, it is now a fundamental component of frameworks from Spring to Angular.js.

With such widespread success, the time is ripe to take a fresh look at dependency injection if we are to understand it better. After all, DI is instrumental in building large systems that are loosely coupled, and it cleanly separates your tests from implementation... or does it?

Tomer Gabel

October 19, 2017
Tweet

More Decks by Tomer Gabel

Other Decks in Programming

Transcript

  1. 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
  2. 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.
  3. 1. THE “D” IN SOLID Image: Peter von Bagh, “Just

    Frozen Water” via Flickr (CC0 1.0 Public Domain)
  4. Back to Basics • Single responsibility • Open/closed • Liskov

    substitution principle • Interface segregation • Dependency inversion Image: Michael Feathers via MozaicWorks
  5. Back to Basics • Single responsibility • Open/closed • Liskov

    substitution principle • Interface segregation • Dependency inversion Image: Michael Feathers via MozaicWorks
  6. Dependency Inversion • A simple idea • Given a dependency:

    – A must not depend on B directly OAuthProvider MysqlUserStore “A” “B”
  7. 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”
  8. 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)
  9. 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; } }
  10. 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) { // ... } }
  11. 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)
  12. 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
  13. 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
  14. Inversion of Control • IoC means many things – Servlet

    containers – Plugin systems – Stream computing – “DI” containers • We’ll focus on the latter
  15. IoC & DI • Consider Spring/Guice – A runtime container

    – Manages components – … including lifecycle – … and automatic wiring Image: ImgFlip
  16. Perceived Benefits • Why use a container? – Simplify wiring

    – Simplify testing – Dynamic configuration – Support for AOP • Let’s consider each Image: ImgFlip
  17. Perceived Benefits • Why use a container? – Simplify wiring

    – Simplify testing – Dynamic configuration – Support for AOP • Let’s consider each Image: ImgFlip
  18. 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); }
  19. 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); } }
  20. 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); } }
  21. 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)
  22. Simplify Testing • Proponents will tell you: 1. Bring up

    a container 2. Swap out components 3. Bob’s your uncle
  23. 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.
  24. 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)
  25. … except … • With huge codebases – Read: “Monoliths”

    – Read: “Enterprise” • Enables a tradeoff – Developer discipline – Code coherence, simplicity, navigability
  26. … 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.
  27. 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.