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

Sharper Better Faster Dagger ‡ (Droidcon SF 2016)

Sharper Better Faster Dagger ‡ (Droidcon SF 2016)

For the past 3 years, Square Register Android has leveraged Dagger † to wire up Java objects. However, the app scope hierarchy and complexity increased and we started having bugs and crashes related to scoping errors.

This talk will show how to structure an app around Dagger2 ‡ and present a strategy for incrementally migrating from Dagger1 to Dagger2.

Co-Presented with @Piwai.

Video: https://www.youtube.com/watch?v=7mVRZqsozPw

John Rodriguez

March 17, 2016
Tweet

More Decks by John Rodriguez

Other Decks in Programming

Transcript

  1. @Module(
 injects = { MainActivity.class }, addsTo = AppModule.class
 )


    public final class ApiModule {
 @Provides GithubService provideGithubService(Retrofit retrofit) {
 return retrofit.create(GithubService.class);
 }
 
 @Provides Retrofit provideRetrofit(@Endpoint String url) {
 return new Retrofit.Builder()
 .baseUrl(url)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  2. @Module(
 injects = { MainActivity.class }, addsTo = AppModule.class
 )


    public final class ApiModule {
 @Provides GithubService provideGithubService(Retrofit retrofit) {
 return retrofit.create(GithubService.class);
 }
 
 @Provides Retrofit provideRetrofit(@Endpoint String url) {
 return new Retrofit.Builder()
 .baseUrl(url)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  3. @Module(
 injects = { MainActivity.class }, addsTo = AppModule.class
 )


    public final class ApiModule {
 @Provides GithubService provideGithubService(Retrofit retrofit) {
 return retrofit.create(GithubService.class);
 }
 
 @Provides Retrofit provideRetrofit(@Endpoint String url) {
 return new Retrofit.Builder()
 .baseUrl(url)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  4. public class MainActivity extends Activity {
 @Inject GithubService githubService;
 


    @Override protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 ObjectGraph graph = getObjectGraph();
 objectGraph.inject(this);
  5. public class MainActivity extends Activity {
 @Inject GithubService githubService;
 


    @Override protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 ObjectGraph graph = getObjectGraph();
 objectGraph.inject(this);
  6. @Module(
 injects = { MainActivity.class }, addsTo = AppModule.class
 )


    public final class ApiModule {
 @Provides GithubService provideGithubService(Retrofit retrofit) {
 return retrofit.create(GithubService.class);
 }
 
 @Provides Retrofit provideRetrofit(@Endpoint String url) {
 return new Retrofit.Builder()
 .baseUrl(url)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  7. @Module
 public final class ApiModule {
 @Provides GithubService provideGithubService(Retrofit retrofit)

    {
 return retrofit.create(GithubService.class);
 }
 
 @Provides Retrofit provideRetrofit(@Endpoint String url) {
 return new Retrofit.Builder()
 .baseUrl(url)
 .addConverterFactory(GsonConverterFactory.create())
 .build();
 }
 }
  8. @Component(
 modules = ApiModule.class
 )
 public interface ApiComponent { Foo

    getFoo();
 void inject(MainActivity activity);
 }
  9. public class MainActivity extends Activity {
 @Inject GithubService githubService;
 


    @Override protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 
 ApiComponent apiComponent = DaggerApiComponent.builder().build();
 apiComponent.inject(this); …
  10. public class MainActivity extends Activity {
 @Inject GithubService githubService;
 


    @Override protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 
 ApiComponent apiComponent = DaggerApiComponent.builder().build();
 apiComponent.inject(this); …
  11. @Module class AndroidModule {…} @Module class HttpModule {…}
 @Module class

    ApiModule {…}
 
 @Module class LoggedInModule {…} @Module class PaymentModule {…} @Module class TransactionLedgerModule {…} …
  12. @Module 
 public class AppModule {
 // ...
 @Provides OkHttpClient

    provideOkHttpClient() {
 return new OkHttpClient();
 }
 
 @Provides Client provideRetrofitClient(OkHttpClient client) {
 return new OkClient(client);
 }
 // ...
 }
  13. @Module 
 public class AppModule {
 // ...
 @Provides OkHttpClient

    provideOkHttpClient() {
 return new OkHttpClient();
 }
 
 @Provides Client provideRetrofitClient(OkHttpClient client) {
 return new OkClient(client);
 }
 // ...
 } @Module2 
 public class AppModule2 {
 
 }
  14. @Module 
 public class AppModule {
 // ...
 
 }

    @Module2 
 public class AppModule2 { @Provides2 OkHttpClient provideOkHttpClient() {
 return new OkHttpClient();
 }
 
 @Provides2 Client provideRetrofitClient(OkHttpClient client) {
 return new OkClient(client);
 } }
  15. @Module2 
 public class AppModule2 { @Provides2 OkHttpClient provideOkHttpClient() {


    return new OkHttpClient();
 }
 
 @Provides2 Client provideRetrofitClient(OkHttpClient client) {
 return new OkClient(client);
 } } @Component(modules = AppModule2.class)
 public interface AppComponent {
 
 }
  16. @Module2 
 public class AppModule2 { @Provides2 OkHttpClient provideOkHttpClient() {


    return new OkHttpClient();
 }
 
 @Provides2 Client provideRetrofitClient(OkHttpClient client) {
 return new OkClient(client);
 } } @Component(modules = AppModule2.class)
 public interface AppComponent {
 Client retrofitClient();
 OkHttpClient okHttpClient();
 }
  17. @Component(modules = AppModule2.class)
 public interface AppComponent {
 AppComponentFacade dagger2Facade();
 }

    @Subcomponent
 public interface AppComponentFacade {
 Client retrofitClient();
 OkHttpClient okHttpClient();
 }
  18. @Subcomponent
 public interface AppComponentFacade {
 Client retrofitClient();
 OkHttpClient okHttpClient();
 }

    @Module
 public class BridgeModule {
 private final AppComponentFacade facade;
 
 public BridgeModule(AppComponentFacade facade) {
 this.facade = facade;
 }
 
 @Provides Client retrofitClient() {
 return facade.retrofitClient();
 }
 
 @Provides OkHttpClient okHttpClient() {
 return facade.okHttpClient();
 }
 }
  19. @Subcomponent
 public interface AppComponentFacade {
 Client retrofitClient();
 OkHttpClient okHttpClient();
 }

    @Module
 public class BridgeModule implements AppComponentFacade {
 private final AppComponentFacade facade;
 
 public BridgeModule(AppComponentFacade facade) {
 this.facade = facade;
 }
 
 @Override @Provides public Client retrofitClient() {
 return facade.retrofitClient();
 }
 
 @Override @Provides public OkHttpClient okHttpClient() {
 return facade.okHttpClient();
 }
 }
  20. AppComponent component = DaggerAppComponent.builder().create();
 AppComponentFacade facade = component.dagger2Facade();
 BridgeModule bridge

    = new BridgeModule(facade);
 ObjectGraph objectGraph = ObjectGraph.create(AppModule.class);
  21. AppComponent component = DaggerAppComponent.builder().create();
 AppComponentFacade facade = component.dagger2Facade();
 BridgeModule bridge

    = new BridgeModule(facade);
 ObjectGraph objectGraph = ObjectGraph.create(bridge, AppModule.class);
  22. @Module2 
 public class AppModule2 { @Provides2 OkHttpClient provideOkHttpClient() {


    return new OkHttpClient();
 }
 
 @Provides2 Client provideRetrofitClient(OkHttpClient client) {
 return new OkClient(client);
 } }
  23. @Module2
 public class AppModule2 {
 @Provides2 OkHttpClient provideOkHttpClient(HostnameVerifier verifier) {


    OkHttpClient okHttpClient = new OkHttpClient();
 okHttpClient.setHostnameVerifier(verifier);
 return okHttpClient;
 }
 
 @Provides2 Client provideRetrofitClient(OkHttpClient client) {
 return new OkClient(client);
 }
 }
  24. @Module(injects = HostnameVerifier.class)
 public class AppModule {
 // ...
 }

    @Module2
 public class GoldenGateModule {
 private ObjectGraph objectGraph;
 
 @Provides2 public HostnameVerifier provideHostnameVerifier() {
 return objectGraph.get(HostnameVerifier.class);
 }
 
 public void setObjectGraph(ObjectGraph objectGraph) {
 this.objectGraph = objectGraph;
 }
 }
  25. @Module2
 public class GoldenGateModule {
 private ObjectGraph objectGraph;
 
 @Provides2

    public HostnameVerifier provideHostnameVerifier() {
 return objectGraph.get(HostnameVerifier.class);
 }
 
 public void setObjectGraph(ObjectGraph objectGraph) {
 this.objectGraph = objectGraph;
 }
 } @Module2 public class AppModule2 {
 
 // ...
 
 }
  26. @Module2
 public class GoldenGateModule {
 private ObjectGraph objectGraph;
 
 @Provides2

    public HostnameVerifier provideHostnameVerifier() {
 return objectGraph.get(HostnameVerifier.class);
 }
 
 public void setObjectGraph(ObjectGraph objectGraph) {
 this.objectGraph = objectGraph;
 }
 } @Module2(includes = GoldenGateModule.class)
 public class AppModule2 {
 
 // ...
 
 }
  27. 
 AppComponent component = DaggerAppComponent.builder().create();
 
 
 AppComponentFacade facade =

    component.dagger2Facade();
 BridgeModule bridge = new BridgeModule(facade);
 ObjectGraph objectGraph = ObjectGraph.create(bridge, AppModule.class);

  28. GoldenGateModule goldenGateModule = new GoldenGateModule();
 AppComponent component = DaggerAppComponent.builder().create();
 


    
 AppComponentFacade facade = component.dagger2Facade();
 BridgeModule bridge = new BridgeModule(facade);
 ObjectGraph objectGraph = ObjectGraph.create(bridge, AppModule.class);

  29. GoldenGateModule goldenGateModule = new GoldenGateModule();
 AppComponent component = DaggerAppComponent.builder()
 .goldenGateModule(goldenGateModule)


    .create();
 AppComponentFacade facade = component.dagger2Facade();
 BridgeModule bridge = new BridgeModule(facade);
 ObjectGraph objectGraph = ObjectGraph.create(bridge, AppModule.class);

  30. GoldenGateModule goldenGateModule = new GoldenGateModule();
 AppComponent component = DaggerAppComponent.builder()
 .goldenGateModule(goldenGateModule)


    .create();
 AppComponentFacade facade = component.dagger2Facade();
 BridgeModule bridge = new BridgeModule(facade);
 ObjectGraph objectGraph = ObjectGraph.create(bridge, AppModule.class);
 goldenGateModule.setObjectGraph(objectGraph);
  31. Guice Dagger Dagger2 better than Spring…? improved linking + instance

    creation improved linking more (1x per graph)
  32. @Override public ObjectGraph plus(Object... modules) {
 linkEverything();
 return makeGraph(this, plugin,

    modules);
 } private Map<String, Binding<?>> linkEverything() {
 … return linker.linkAll();
 } public Map<String, Binding<?>> linkAll() { …
 linkRequested();
 return Collections.unmodifiableMap(bindings);
 }
  33. public void linkRequested() { … while ((binding = toLink.poll()) !=

    null) {
 if (binding instanceof DeferredBinding) { … = createBinding(…, deferred.classLoader, …);
 }
 }
 } private Binding<?> createBinding(…, ClassLoader classLoader, …) {
 … … = instantiate(className.concat(INJECT_ADAPTER_SUFFIX), classLoader); … } protected <T> T instantiate(String name, ClassLoader classLoader) {
 Class<?> generatedClass = loadClass(classLoader, name);
 if (generatedClass == Void.class) return null;
 return (T) generatedClass.newInstance(); }
  34. public void linkRequested() { … while ((binding = toLink.poll()) !=

    null) {
 if (binding instanceof DeferredBinding) { … = createBinding(…, deferred.classLoader, …);
 }
 }
 } private Binding<?> createBinding(…, ClassLoader classLoader, …) {
 … … = instantiate(className.concat(INJECT_ADAPTER_SUFFIX), classLoader); … } protected <T> T instantiate(String name, ClassLoader classLoader) {
 Class<?> generatedClass = loadClass(classLoader, name);
 if (generatedClass == Void.class) return null;
 return (T) generatedClass.newInstance(); }
  35. @Module 
 class DripCoffeeModule {
 @Provides static Heater provideHeater(Executor executor)

    {
 return new CpuHeater(executor);
 }
 } [ERROR] COMPILATION ERROR : [ERROR] error: java.util.concurrent.Executor cannot be provided without an @Provides-annotated method.
  36. public class TransactionHandler {
 
 private final Analytics analytics;
 private

    final TaxCache taxCache;
 
 private Payment paymentInFlight;
 private Order order;
 
 @Inject TransactionHandler(Analytics analytics, TaxCache taxCache) {
 this.analytics = analytics;
 this.taxCache = taxCache;
 }
 
 // ...
 }
  37. public class TransactionHandler {
 
 private final Analytics analytics;
 private

    final TaxCache taxCache;
 
 private Payment paymentInFlight;
 private Order order;
 
 @Inject TransactionHandler(Analytics analytics, TaxCache taxCache) {
 this.analytics = analytics;
 this.taxCache = taxCache;
 }
 
 // ...
 }
  38. @Singleton public class TransactionHandler {
 
 private final Analytics analytics;


    private final TaxCache taxCache;
 
 private Payment paymentInFlight;
 private Order order;
 
 @Inject TransactionHandler(Analytics analytics, TaxCache taxCache) {
 this.analytics = analytics;
 this.taxCache = taxCache;
 }
 
 // ...
 }
  39. public class Linker {
 
 final Map<Class<?>, Binding<?>> bindings =

    new HashMap<>();
 
 <T> Binding<T> requestBinding(Class<T> key) {
 return (Binding<T>) bindings.get(key);
 }
 }
  40. public class Linker {
 
 final Map<Class<?>, Binding<?>> bindings =

    new HashMap<>();
 
 <T> Binding<T> requestBinding(Class<T> key) {
 return (Binding<T>) bindings.get(key);
 }
 }
  41. public abstract class Binding<T> {
 
 final boolean singleton;
 


    protected Binding(boolean singleton) {
 this.singleton = singleton;
 }
 
 abstract void attach(Linker linker);
 
 abstract T get();
 }
  42. public abstract class Binding<T> {
 
 final boolean singleton;
 


    protected Binding(boolean singleton) {
 this.singleton = singleton;
 }
 
 abstract void attach(Linker linker);
 
 abstract T get();
 }
  43. public abstract class Binding<T> {
 
 final boolean singleton;
 


    protected Binding(boolean singleton) {
 this.singleton = singleton;
 }
 
 abstract void attach(Linker linker);
 
 abstract T get();
 }
  44. public abstract class Binding<T> {
 
 final boolean singleton;
 


    protected Binding(boolean singleton) {
 this.singleton = singleton;
 }
 
 abstract void attach(Linker linker);
 
 abstract T get();
 }
  45. public class TransactionHandler$$InjectAdapter extends Binding<TransactionHandler> {
 
 private Binding<TaxCache> taxCache;


    private Binding<Analytics> analytics;
 
 public TransactionHandler$$InjectAdapter() {
 super(IS_SINGLETON);
 }
 
 @Override public void attach(Linker linker) {
 analytics = linker.requestBinding(Analytics.class);
 taxCache = linker.requestBinding(TaxCache.class);
 }
 
 @Override public TransactionHandler get() {
 return new TransactionHandler(analytics.get(), taxCache.get());
 }
 }
  46. public class TransactionHandler$$InjectAdapter extends Binding<TransactionHandler> {
 
 private Binding<TaxCache> taxCache;


    private Binding<Analytics> analytics;
 
 public TransactionHandler$$InjectAdapter() {
 super(IS_SINGLETON);
 }
 
 @Override public void attach(Linker linker) {
 analytics = linker.requestBinding(Analytics.class);
 taxCache = linker.requestBinding(TaxCache.class);
 }
 
 @Override public TransactionHandler get() {
 return new TransactionHandler(analytics.get(), taxCache.get());
 }
 }
  47. public class TransactionHandler$$InjectAdapter extends Binding<TransactionHandler> {
 
 private Binding<TaxCache> taxCache;


    private Binding<Analytics> analytics;
 
 public TransactionHandler$$InjectAdapter() {
 super(IS_SINGLETON);
 }
 
 @Override public void attach(Linker linker) {
 analytics = linker.requestBinding(Analytics.class);
 taxCache = linker.requestBinding(TaxCache.class);
 }
 
 @Override public TransactionHandler get() {
 return new TransactionHandler(analytics.get(), taxCache.get());
 }
 }
  48. public class SingletonBinding<T> extends Binding<T> {
 final Binding<T> delegate;
 


    T instance;
 
 SingletonBinding(Binding<T> delegate) {
 this.delegate = delegate;
 }
 
 @Override void attach(Linker linker) {
 delegate.attach(linker);
 }
 
 @Override T get() {
 if (instance == null) {
 instance = delegate.get();
 }
 return instance;
 }
 }
  49. public class SingletonBinding<T> extends Binding<T> {
 final Binding<T> delegate;
 


    T instance;
 
 SingletonBinding(Binding<T> delegate) {
 this.delegate = delegate;
 }
 
 @Override void attach(Linker linker) {
 delegate.attach(linker);
 }
 
 @Override T get() {
 if (instance == null) {
 instance = delegate.get();
 }
 return instance;
 }
 }
  50. @Singleton public class TransactionHandler {
 
 private final Analytics analytics;


    private final TaxCache taxCache;
 
 private Payment paymentInFlight;
 private Order order;
 
 @Inject TransactionHandler(Analytics analytics, TaxCache taxCache) {
 this.analytics = analytics;
 this.taxCache = taxCache;
 }
 
 // ...
 }
  51. public class TransactionFlow {
 
 private final TransactionHandler transactionHandler;
 


    @Inject TransactionFlow(TransactionHandler transactionHandler) {
 this.transactionHandler = transactionHandler;
 }
 // ...
 }
  52. public class TransactionFlow {
 
 private final TransactionHandler transactionHandler;
 


    @Inject TransactionFlow(TransactionHandler transactionHandler) {
 this.transactionHandler = transactionHandler;
 }
 // ...
 } public class TransactionFlowLayout extends FrameLayout {
 
 public TransactionFlowLayout(Context context, AttributeSet attrs) {
 super(context, attrs);
 TransactionFlow transactionFlow = ???;
 // ...
 }
 }
  53. ObjectGraph transactionGraph = appGraph.plus(TransactionModule.class); @Module(injects = TransactionFlow.class)
 class TransactionModule {


    } public class TransactionFlowLayout extends FrameLayout {
 
 public TransactionFlowLayout(Context context, AttributeSet attrs) {
 super(context, attrs); ObjectGraph graph = getTransactionGraph();
 TransactionFlow transactionFlow = graph.get(TransactionFlow.class);
 // ...
 }
 }
  54. public class CartPresenter {
 
 private final TransactionHandler transactionHandler;
 


    @Inject CartPresenter(TransactionHandler transactionHandler) {
 this.transactionHandler = transactionHandler;
 }
 // ...
 }
  55. public class CartPresenter {
 
 private final TransactionHandler transactionHandler;
 


    @Inject CartPresenter(TransactionHandler transactionHandler) {
 this.transactionHandler = transactionHandler;
 }
 // ...
 } public class CartView extends CoordinatorLayout {
 
 public CartView(Context context, AttributeSet attrs) {
 super(context, attrs);
 ObjectGraph cartGraph = getCartGraph();
 CartPresenter cartPresenter = cartGraph.get(CartPresenter.class);
 // ...
 }
 }
  56. public class CartPresenter {
 
 private final TransactionHandler transactionHandler;
 


    @Inject CartPresenter(TransactionHandler transactionHandler) {
 this.transactionHandler = transactionHandler;
 }
 // ...
 } public class CartView extends CoordinatorLayout {
 
 public CartView(Context context, AttributeSet attrs) {
 super(context, attrs);
 ObjectGraph cartGraph = getCartGraph();
 CartPresenter cartPresenter = cartGraph.get(CartPresenter.class);
 // ...
 }
 }
  57. ObjectGraph transactionGraph = appGraph.plus(TransactionModule.class); 
 ObjectGraph cartGraph = transactionGraph.plus(CartScreenModule.class); @Module(injects

    = TransactionFlow.class)
 class TransactionModule {
 } @Module(injects = CartPresenter.class)
 class CartScreenModule {
 }
  58. public class ObjectGraph {
 
 Linker linker;
 
 public <T>

    T get(Class<T> key) {
 Binding<T> binding = linker.requestBinding(key);
 return binding.get();
 }
 }
  59. public class ObjectGraph {
 
 Linker linker;
 ObjectGraph parentGraph;
 


    public <T> T get(Class<T> key) {
 if (parentGraph != null) {
 T instance = parentGraph.get(key);
 if (instance != null) {
 return instance;
 }
 }
 Binding<T> binding = linker.requestBinding(key);
 return binding.get();
 }
 }
  60. public class Linker {
 
 final Map<Class<?>, Binding<?>> bindings =

    new HashMap<>();
 
 <T> Binding<T> requestBinding(Class<T> key) {
 return (Binding<T>) bindings.get(key);
 }
 }
  61. public class Linker {
 
 final Map<Class<?>, Binding<?>> bindings =

    new HashMap<>();
 
 <T> Binding<T> requestBinding(Class<T> key) {
 Binding<T> binding = (Binding<T>) bindings.get(key);
 if (binding == null) {
 binding = loadGeneratedBinding(key);
 if (binding.singleton) {
 binding = new SingletonBinding<>(binding);
 }
 bindings.put(key, binding);
 }
 return binding;
 }
 }
  62. com.squareup.Transaction binding found in scopes marked with '*' (if any).

    SCOPE RegisterRootScope +-SCOPE com.squareup.dagger.LoggedIn | `-SCOPE com.squareup.ui.root.RootActivity | `-SCOPE * com.squareup.ui.root.RootFlow | `-SCOPE com.squareup.ui.seller.SellerFlow | `-SCOPE com.squareup.ui.home.HomeScreen `-SCOPE com.squareup.ui.PaymentActivity
  63. @Module(injects = TransactionFlow.class)
 class TransactionModule {
 
 @Provides @Singleton TransactionHandler

    transactionHandler(Analytics analytics,
 TaxCache taxCache) {
 return new TransactionHandler(analytics, taxCache);
 }
 } ObjectGraph transactionGraph = appGraph.plus(TransactionModule.class);
  64. @Singleton public class TransactionHandler {...} @Module(injects = TransactionFlow.class)
 public class

    TransactionModule {} @Scope public @interface SingleInTransaction {}
  65. @SingleInTransaction public class TransactionHandler {...} @Module(injects = TransactionFlow.class)
 public class

    TransactionModule {} @Scope public @interface SingleInTransaction {} 
 @Component 
 interface TransactionComponent {
 }
  66. @SingleInTransaction public class TransactionHandler {...} @Module(injects = TransactionFlow.class)
 public class

    TransactionModule {} @Scope public @interface SingleInTransaction {} @SingleInTransaction
 @Component 
 interface TransactionComponent {
 }
  67. @SingleInTransaction public class TransactionHandler {...} @Module(injects = TransactionFlow.class)
 public class

    TransactionModule {} @Scope public @interface SingleInTransaction {} @SingleInTransaction
 @Component(modules = TransactionModule.class) 
 interface TransactionComponent {
 }
  68. @SingleInTransaction public class TransactionHandler {...} @Module
 public class TransactionModule {}

    @Scope public @interface SingleInTransaction {} @SingleInTransaction
 @Component(modules = TransactionModule.class) 
 interface TransactionComponent {
 TransactionFlow transactionFlow();
 }
  69. @SingleInTransaction public class TransactionHandler {...} @Module
 public class TransactionModule {}

    @Scope public @interface SingleInTransaction {} @SingleInTransaction
 @Component(modules = TransactionModule.class) 
 interface TransactionComponent {
 TransactionFlow transactionFlow();
 }
  70. @SingleInTransaction public class TransactionHandler {...} @Module
 public class TransactionModule {}

    @Scope public @interface SingleIn { Class<?> value(); } @SingleInTransaction
 @Component(modules = TransactionModule.class) 
 interface TransactionComponent {
 TransactionFlow transactionFlow();
 }
  71. @SingleIn(TransactionComponent.class) public class TransactionHandler {...} @Module
 public class TransactionModule {}

    @Scope public @interface SingleIn { Class<?> value(); } @SingleInTransaction
 @Component(modules = TransactionModule.class) 
 interface TransactionComponent {
 TransactionFlow transactionFlow();
 }
  72. @SingleIn(TransactionComponent.class) public class TransactionHandler {...} @Module
 public class TransactionModule {}

    @Scope public @interface SingleIn { Class<?> value(); } @SingleIn(TransactionComponent.class)
 @Component(modules = TransactionModule.class) 
 interface TransactionComponent {
 TransactionFlow transactionFlow();
 }
  73. public interface CartScreenDependencies {
 TransactionHandler transactionHandler();
 } @Component(modules = CartScreenModule.class,

    dependencies = CartScreenDependencies.class)
 public interface CartScreenComponent {
 CartPresenter cartPresenter();
 }