diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift
index 2b2d10391ee64f2b70f06b607c67437e78f3d55a..e49750ca16702861be964a5991472e4ff186b9fb 100644
--- a/Examples/xx-messenger/Package.swift
+++ b/Examples/xx-messenger/Package.swift
@@ -38,7 +38,7 @@ let package = Package(
     ),
     .package(
       url: "https://github.com/pointfreeco/swift-composable-architecture.git",
-      .upToNextMajor(from: "0.40.2")
+      .upToNextMajor(from: "0.43.0")
     ),
     .package(
       url: "https://git.xx.network/elixxir/client-ios-db.git",
@@ -46,15 +46,15 @@ let package = Package(
     ),
     .package(
       url: "https://github.com/darrarski/swift-composable-presentation.git",
-      .upToNextMajor(from: "0.5.3")
+      .upToNextMajor(from: "0.6.0")
     ),
     .package(
       url: "https://github.com/pointfreeco/xctest-dynamic-overlay.git",
-      .upToNextMajor(from: "0.4.1")
+      .upToNextMajor(from: "0.5.0")
     ),
     .package(
       url: "https://github.com/pointfreeco/swift-custom-dump.git",
-      .upToNextMajor(from: "0.5.2")
+      .upToNextMajor(from: "0.6.0")
     ),
     .package(
       url: "https://github.com/apple/swift-log.git",
@@ -62,13 +62,14 @@ let package = Package(
     ),
     .package(
       url: "https://github.com/kean/Pulse.git",
-      .upToNextMajor(from: "2.1.2")
+      .upToNextMajor(from: "2.1.3")
     ),
   ],
   targets: [
     .target(
       name: "AppCore",
       dependencies: [
+        .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
         .product(name: "Logging", package: "swift-log"),
         .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
         .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
@@ -127,6 +128,7 @@ let package = Package(
     .target(
       name: "BackupFeature",
       dependencies: [
+        .target(name: "AppCore"),
         .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
         .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
       ],
@@ -376,6 +378,7 @@ let package = Package(
     .target(
       name: "UserSearchFeature",
       dependencies: [
+        .target(name: "AppCore"),
         .target(name: "ContactFeature"),
         .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
         .product(name: "ComposablePresentation", package: "swift-composable-presentation"),
diff --git a/Examples/xx-messenger/Sources/AppCore/AppDependecies.swift b/Examples/xx-messenger/Sources/AppCore/AppDependecies.swift
new file mode 100644
index 0000000000000000000000000000000000000000..fcb72dc0ee649c5e096f6397396bbaad4e1d6194
--- /dev/null
+++ b/Examples/xx-messenger/Sources/AppCore/AppDependecies.swift
@@ -0,0 +1,96 @@
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXMessengerClient
+
+public struct AppDependencies {
+  public var dbManager: DBManager
+  public var messenger: Messenger
+  public var authHandler: AuthCallbackHandler
+  public var backupStorage: BackupStorage
+  public var mainQueue: AnySchedulerOf<DispatchQueue>
+  public var bgQueue: AnySchedulerOf<DispatchQueue>
+  public var now: () -> Date
+  public var sendMessage: SendMessage
+  public var sendImage: SendImage
+  public var messageListener: MessageListenerHandler
+  public var receiveFileHandler: ReceiveFileHandler
+  public var log: Logger
+  public var loadData: URLDataLoader
+}
+
+extension AppDependencies {
+  public static func live() -> AppDependencies {
+    let dbManager = DBManager.live()
+    let messengerEnv = MessengerEnvironment.live()
+    let messenger = Messenger.live(messengerEnv)
+    let now: () -> Date = Date.init
+
+    return AppDependencies(
+      dbManager: dbManager,
+      messenger: messenger,
+      authHandler: .live(
+        messenger: messenger,
+        handleRequest: .live(db: dbManager.getDB, now: now),
+        handleConfirm: .live(db: dbManager.getDB),
+        handleReset: .live(db: dbManager.getDB)
+      ),
+      backupStorage: .onDisk(),
+      mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
+      bgQueue: DispatchQueue.global(qos: .background).eraseToAnyScheduler(),
+      now: now,
+      sendMessage: .live(
+        messenger: messenger,
+        db: dbManager.getDB,
+        now: now
+      ),
+      sendImage: .live(
+        messenger: messenger,
+        db: dbManager.getDB,
+        now: now
+      ),
+      messageListener: .live(
+        messenger: messenger,
+        db: dbManager.getDB
+      ),
+      receiveFileHandler: .live(
+        messenger: messenger,
+        db: dbManager.getDB,
+        now: now
+      ),
+      log: .live(),
+      loadData: .live
+    )
+  }
+
+  public static let unimplemented = AppDependencies(
+    dbManager: .unimplemented,
+    messenger: .unimplemented,
+    authHandler: .unimplemented,
+    backupStorage: .unimplemented,
+    mainQueue: .unimplemented,
+    bgQueue: .unimplemented,
+    now: XCTestDynamicOverlay.unimplemented(
+      "\(Self.self)",
+      placeholder: Date(timeIntervalSince1970: 0)
+    ),
+    sendMessage: .unimplemented,
+    sendImage: .unimplemented,
+    messageListener: .unimplemented,
+    receiveFileHandler: .unimplemented,
+    log: .unimplemented,
+    loadData: .unimplemented
+  )
+}
+
+private enum AppDependenciesKey: DependencyKey {
+  static let liveValue: AppDependencies = .live()
+  static let testValue: AppDependencies = .unimplemented
+}
+
+extension DependencyValues {
+  public var app: AppDependencies {
+    get { self[AppDependenciesKey.self] }
+    set { self[AppDependenciesKey.self] = newValue }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/RestoreFeature/URLDataLoader.swift b/Examples/xx-messenger/Sources/AppCore/URLDataLoader/URLDataLoader.swift
similarity index 100%
rename from Examples/xx-messenger/Sources/RestoreFeature/URLDataLoader.swift
rename to Examples/xx-messenger/Sources/AppCore/URLDataLoader/URLDataLoader.swift
diff --git a/Examples/xx-messenger/Sources/AppFeature/App.swift b/Examples/xx-messenger/Sources/AppFeature/App.swift
index 7c3f0e82b5b00ee3aa4ff601b50556dbc8a148d2..c395f2ad0b8ac415bdea844055d5f85ca7ad9672 100644
--- a/Examples/xx-messenger/Sources/AppFeature/App.swift
+++ b/Examples/xx-messenger/Sources/AppFeature/App.swift
@@ -7,15 +7,17 @@ import SwiftUI
 struct App: SwiftUI.App {
   init() {
     LoggingSystem.bootstrap(PersistentLogHandler.init)
+    ViewStore(store.stateless).send(.setupLogging)
   }
 
+  let store = Store(
+    initialState: AppComponent.State(),
+    reducer: AppComponent()
+  )
+
   var body: some Scene {
     WindowGroup {
-      AppView(store: Store(
-        initialState: AppState(),
-        reducer: appReducer,
-        environment: .live()
-      ))
+      AppView(store: store)
     }
   }
 }
diff --git a/Examples/xx-messenger/Sources/AppFeature/AppComponent.swift b/Examples/xx-messenger/Sources/AppFeature/AppComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..d092a69e57ac781372297df8de4cdcce02a43d36
--- /dev/null
+++ b/Examples/xx-messenger/Sources/AppFeature/AppComponent.swift
@@ -0,0 +1,159 @@
+import AppCore
+import Combine
+import ComposableArchitecture
+import ComposablePresentation
+import Foundation
+import HomeFeature
+import RestoreFeature
+import WelcomeFeature
+import XXClient
+import XXMessengerClient
+
+struct AppComponent: ReducerProtocol {
+  struct State: Equatable {
+    enum Screen: Equatable {
+      case loading
+      case welcome(WelcomeComponent.State)
+      case restore(RestoreComponent.State)
+      case home(HomeComponent.State)
+      case failure(String)
+    }
+
+    @BindableState var screen: Screen = .loading
+  }
+
+  enum Action: Equatable, BindableAction {
+    case setupLogging
+    case start
+    case stop
+    case binding(BindingAction<State>)
+    case welcome(WelcomeComponent.Action)
+    case restore(RestoreComponent.Action)
+    case home(HomeComponent.Action)
+  }
+
+  @Dependency(\.app.dbManager) var dbManager: DBManager
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.authHandler) var authHandler: AuthCallbackHandler
+  @Dependency(\.app.messageListener) var messageListener: MessageListenerHandler
+  @Dependency(\.app.receiveFileHandler) var receiveFileHandler: ReceiveFileHandler
+  @Dependency(\.app.backupStorage) var backupStorage: BackupStorage
+  @Dependency(\.app.log) var log: Logger
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      enum EffectId {}
+
+      let dbManager = self.dbManager
+      let messenger = self.messenger
+      let authHandler = self.authHandler
+      let messageListener = self.messageListener
+      let receiveFileHandler = self.receiveFileHandler
+      let backupStorage = self.backupStorage
+      let log = self.log
+
+      switch action {
+      case .setupLogging:
+        _ = try! messenger.setLogLevel(.debug)
+        messenger.startLogging()
+        return .none
+
+      case .start, .welcome(.finished), .restore(.finished), .home(.deleteAccount(.success)):
+        state.screen = .loading
+        return Effect.run { subscriber in
+          var cancellables: [XXClient.Cancellable] = []
+
+          do {
+            if dbManager.hasDB() == false {
+              try dbManager.makeDB()
+            }
+
+            cancellables.append(authHandler(onError: { error in
+              log(.error(error as NSError))
+            }))
+            cancellables.append(messageListener(onError: { error in
+              log(.error(error as NSError))
+            }))
+            cancellables.append(receiveFileHandler(onError: { error in
+              log(.error(error as NSError))
+            }))
+
+            cancellables.append(messenger.registerBackupCallback(.init { data in
+              try? backupStorage.store(data)
+            }))
+
+            let isLoaded = messenger.isLoaded()
+            let isCreated = messenger.isCreated()
+
+            if !isLoaded, !isCreated {
+              subscriber.send(.set(\.$screen, .welcome(WelcomeComponent.State())))
+            } else if !isLoaded {
+              try messenger.load()
+              subscriber.send(.set(\.$screen, .home(HomeComponent.State())))
+            } else {
+              subscriber.send(.set(\.$screen, .home(HomeComponent.State())))
+            }
+          } catch {
+            subscriber.send(.set(\.$screen, .failure(error.localizedDescription)))
+          }
+
+          return AnyCancellable { cancellables.forEach { $0.cancel() } }
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+        .cancellable(id: EffectId.self, cancelInFlight: true)
+
+      case .stop:
+        return .cancel(id: EffectId.self)
+
+      case .welcome(.restoreTapped):
+        state.screen = .restore(RestoreComponent.State())
+        return .none
+
+      case .welcome(.failed(let failure)):
+        state.screen = .failure(failure)
+        return .none
+
+      case .binding(_), .welcome(_), .restore(_), .home(_):
+        return .none
+      }
+    }
+    .presenting(
+      state: .keyPath(\.screen.asWelcome),
+      id: .notNil(),
+      action: /Action.welcome,
+      presented: { WelcomeComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.screen.asRestore),
+      id: .notNil(),
+      action: /Action.restore,
+      presented: { RestoreComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.screen.asHome),
+      id: .notNil(),
+      action: /Action.home,
+      presented: { HomeComponent() }
+    )
+  }
+}
+
+extension AppComponent.State.Screen {
+  var asWelcome: WelcomeComponent.State? {
+    get { (/AppComponent.State.Screen.welcome).extract(from: self) }
+    set { if let newValue = newValue { self = .welcome(newValue) } }
+  }
+  var asRestore: RestoreComponent.State? {
+    get { (/AppComponent.State.Screen.restore).extract(from: self) }
+    set { if let state = newValue { self = .restore(state) } }
+  }
+  var asHome: HomeComponent.State? {
+    get { (/AppComponent.State.Screen.home).extract(from: self) }
+    set { if let newValue = newValue { self = .home(newValue) } }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
deleted file mode 100644
index 2b1f9964c4b900bee5596eb7fee1e1f59ef4f7c8..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift
+++ /dev/null
@@ -1,199 +0,0 @@
-import AppCore
-import BackupFeature
-import ChatFeature
-import CheckContactAuthFeature
-import ConfirmRequestFeature
-import ContactFeature
-import ContactLookupFeature
-import ContactsFeature
-import Foundation
-import HomeFeature
-import MyContactFeature
-import RegisterFeature
-import ResetAuthFeature
-import RestoreFeature
-import SendRequestFeature
-import UserSearchFeature
-import VerifyContactFeature
-import WelcomeFeature
-import XXMessengerClient
-import XXModels
-
-extension AppEnvironment {
-  static func live() -> AppEnvironment {
-    let dbManager = DBManager.live()
-    let messengerEnv = MessengerEnvironment.live()
-    let messenger = Messenger.live(messengerEnv)
-    let authHandler = AuthCallbackHandler.live(
-      messenger: messenger,
-      handleRequest: .live(db: dbManager.getDB, now: Date.init),
-      handleConfirm: .live(db: dbManager.getDB),
-      handleReset: .live(db: dbManager.getDB)
-    )
-    let backupStorage = BackupStorage.onDisk()
-    let mainQueue = DispatchQueue.main.eraseToAnyScheduler()
-    let bgQueue = DispatchQueue.global(qos: .background).eraseToAnyScheduler()
-
-    defer {
-      _ = try! messenger.setLogLevel(.debug)
-      messenger.startLogging()
-    }
-
-    let contactEnvironment = ContactEnvironment(
-      messenger: messenger,
-      db: dbManager.getDB,
-      mainQueue: mainQueue,
-      bgQueue: bgQueue,
-      lookup: {
-        ContactLookupEnvironment(
-          messenger: messenger,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      sendRequest: {
-        SendRequestEnvironment(
-          messenger: messenger,
-          db: dbManager.getDB,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      verifyContact: {
-        VerifyContactEnvironment(
-          messenger: messenger,
-          db: dbManager.getDB,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      confirmRequest: {
-        ConfirmRequestEnvironment(
-          messenger: messenger,
-          db: dbManager.getDB,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      checkAuth: {
-        CheckContactAuthEnvironment(
-          messenger: messenger,
-          db: dbManager.getDB,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      resetAuth: {
-        ResetAuthEnvironment(
-          messenger: messenger,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      chat: {
-        ChatEnvironment(
-          messenger: messenger,
-          db: dbManager.getDB,
-          sendMessage: .live(
-            messenger: messenger,
-            db: dbManager.getDB,
-            now: Date.init
-          ),
-          sendImage: .live(
-            messenger: messenger,
-            db: dbManager.getDB,
-            now: Date.init
-          ),
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      }
-    )
-
-    return AppEnvironment(
-      dbManager: dbManager,
-      messenger: messenger,
-      authHandler: authHandler,
-      messageListener: .live(
-        messenger: messenger,
-        db: dbManager.getDB
-      ),
-      receiveFileHandler: .live(
-        messenger: messenger,
-        db: dbManager.getDB,
-        now: Date.init
-      ),
-      backupStorage: backupStorage,
-      log: .live(),
-      mainQueue: mainQueue,
-      bgQueue: bgQueue,
-      welcome: {
-        WelcomeEnvironment(
-          messenger: messenger,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      restore: {
-        RestoreEnvironment(
-          messenger: messenger,
-          db: dbManager.getDB,
-          loadData: .live,
-          now: Date.init,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue
-        )
-      },
-      home: {
-        HomeEnvironment(
-          messenger: messenger,
-          dbManager: dbManager,
-          mainQueue: mainQueue,
-          bgQueue: bgQueue,
-          register: {
-            RegisterEnvironment(
-              messenger: messenger,
-              db: dbManager.getDB,
-              now: Date.init,
-              mainQueue: mainQueue,
-              bgQueue: bgQueue
-            )
-          },
-          contacts: {
-            ContactsEnvironment(
-              messenger: messenger,
-              db: dbManager.getDB,
-              mainQueue: mainQueue,
-              bgQueue: bgQueue,
-              contact: { contactEnvironment },
-              myContact: {
-                MyContactEnvironment(
-                  messenger: messenger,
-                  db: dbManager.getDB,
-                  mainQueue: mainQueue,
-                  bgQueue: bgQueue
-                )
-              }
-            )
-          },
-          userSearch: {
-            UserSearchEnvironment(
-              messenger: messenger,
-              mainQueue: mainQueue,
-              bgQueue: bgQueue,
-              contact: { contactEnvironment }
-            )
-          },
-          backup: {
-            BackupEnvironment(
-              messenger: messenger,
-              backupStorage: backupStorage,
-              mainQueue: mainQueue,
-              bgQueue: bgQueue
-            )
-          }
-        )
-      }
-    )
-  }
-}
diff --git a/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift b/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift
deleted file mode 100644
index 56f692c6c1b1cb83f010ec479fb2d82058f4b25e..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift
+++ /dev/null
@@ -1,169 +0,0 @@
-import AppCore
-import Combine
-import ComposableArchitecture
-import ComposablePresentation
-import Foundation
-import HomeFeature
-import RestoreFeature
-import WelcomeFeature
-import XXClient
-import XXMessengerClient
-
-struct AppState: Equatable {
-  enum Screen: Equatable {
-    case loading
-    case welcome(WelcomeState)
-    case restore(RestoreState)
-    case home(HomeState)
-    case failure(String)
-  }
-
-  @BindableState var screen: Screen = .loading
-}
-
-extension AppState.Screen {
-  var asWelcome: WelcomeState? {
-    get { (/AppState.Screen.welcome).extract(from: self) }
-    set { if let newValue = newValue { self = .welcome(newValue) } }
-  }
-  var asRestore: RestoreState? {
-    get { (/AppState.Screen.restore).extract(from: self) }
-    set { if let state = newValue { self = .restore(state) } }
-  }
-  var asHome: HomeState? {
-    get { (/AppState.Screen.home).extract(from: self) }
-    set { if let newValue = newValue { self = .home(newValue) } }
-  }
-}
-
-enum AppAction: Equatable, BindableAction {
-  case start
-  case stop
-  case binding(BindingAction<AppState>)
-  case welcome(WelcomeAction)
-  case restore(RestoreAction)
-  case home(HomeAction)
-}
-
-struct AppEnvironment {
-  var dbManager: DBManager
-  var messenger: Messenger
-  var authHandler: AuthCallbackHandler
-  var messageListener: MessageListenerHandler
-  var receiveFileHandler: ReceiveFileHandler
-  var backupStorage: BackupStorage
-  var log: Logger
-  var mainQueue: AnySchedulerOf<DispatchQueue>
-  var bgQueue: AnySchedulerOf<DispatchQueue>
-  var welcome: () -> WelcomeEnvironment
-  var restore: () -> RestoreEnvironment
-  var home: () -> HomeEnvironment
-}
-
-#if DEBUG
-extension AppEnvironment {
-  static let unimplemented = AppEnvironment(
-    dbManager: .unimplemented,
-    messenger: .unimplemented,
-    authHandler: .unimplemented,
-    messageListener: .unimplemented,
-    receiveFileHandler: .unimplemented,
-    backupStorage: .unimplemented,
-    log: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented,
-    welcome: { .unimplemented },
-    restore: { .unimplemented },
-    home: { .unimplemented }
-  )
-}
-#endif
-
-let appReducer = Reducer<AppState, AppAction, AppEnvironment>
-{ state, action, env in
-  enum EffectId {}
-
-  switch action {
-  case .start, .welcome(.finished), .restore(.finished), .home(.deleteAccount(.success)):
-    state.screen = .loading
-    return Effect.run { subscriber in
-      var cancellables: [XXClient.Cancellable] = []
-
-      do {
-        if env.dbManager.hasDB() == false {
-          try env.dbManager.makeDB()
-        }
-
-        cancellables.append(env.authHandler(onError: { error in
-          env.log(.error(error as NSError))
-        }))
-        cancellables.append(env.messageListener(onError: { error in
-          env.log(.error(error as NSError))
-        }))
-        cancellables.append(env.receiveFileHandler(onError: { error in
-          env.log(.error(error as NSError))
-        }))
-
-        cancellables.append(env.messenger.registerBackupCallback(.init { data in
-          try? env.backupStorage.store(data)
-        }))
-
-        let isLoaded = env.messenger.isLoaded()
-        let isCreated = env.messenger.isCreated()
-
-        if !isLoaded, !isCreated {
-          subscriber.send(.set(\.$screen, .welcome(WelcomeState())))
-        } else if !isLoaded {
-          try env.messenger.load()
-          subscriber.send(.set(\.$screen, .home(HomeState())))
-        } else {
-          subscriber.send(.set(\.$screen, .home(HomeState())))
-        }
-      } catch {
-        subscriber.send(.set(\.$screen, .failure(error.localizedDescription)))
-      }
-
-      return AnyCancellable { cancellables.forEach { $0.cancel() } }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-    .cancellable(id: EffectId.self, cancelInFlight: true)
-
-  case .stop:
-    return .cancel(id: EffectId.self)
-
-  case .welcome(.restoreTapped):
-    state.screen = .restore(RestoreState())
-    return .none
-
-  case .welcome(.failed(let failure)):
-    state.screen = .failure(failure)
-    return .none
-
-  case .binding(_), .welcome(_), .restore(_), .home(_):
-    return .none
-  }
-}
-.binding()
-.presenting(
-  welcomeReducer,
-  state: .keyPath(\.screen.asWelcome),
-  id: .notNil(),
-  action: /AppAction.welcome,
-  environment: { $0.welcome() }
-)
-.presenting(
-  restoreReducer,
-  state: .keyPath(\.screen.asRestore),
-  id: .notNil(),
-  action: /AppAction.restore,
-  environment: { $0.restore() }
-)
-.presenting(
-  homeReducer,
-  state: .keyPath(\.screen.asHome),
-  id: .notNil(),
-  action: /AppAction.home,
-  environment: { $0.home() }
-)
diff --git a/Examples/xx-messenger/Sources/AppFeature/AppView.swift b/Examples/xx-messenger/Sources/AppFeature/AppView.swift
index 57983b1dd0b826321f3dac8c120637736ff64b60..e0ffb3425c2f4cfcc96499384789676d8b42ae4b 100644
--- a/Examples/xx-messenger/Sources/AppFeature/AppView.swift
+++ b/Examples/xx-messenger/Sources/AppFeature/AppView.swift
@@ -6,7 +6,7 @@ import SwiftUI
 import WelcomeFeature
 
 struct AppView: View {
-  let store: Store<AppState, AppAction>
+  let store: StoreOf<AppComponent>
   @State var isPresentingPulse = false
 
   enum ViewState: Equatable {
@@ -16,7 +16,7 @@ struct AppView: View {
     case home
     case failure(String)
 
-    init(_ state: AppState) {
+    init(_ state: AppComponent.State) {
       switch state.screen {
       case .loading: self = .loading
       case .welcome(_): self = .welcome
@@ -42,8 +42,8 @@ struct AppView: View {
         case .welcome:
           IfLetStore(
             store.scope(
-              state: { (/AppState.Screen.welcome).extract(from: $0.screen) },
-              action: AppAction.welcome
+              state: { (/AppComponent.State.Screen.welcome).extract(from: $0.screen) },
+              action: AppComponent.Action.welcome
             ),
             then: { store in
               WelcomeView(store: store)
@@ -58,8 +58,8 @@ struct AppView: View {
         case .restore:
           IfLetStore(
             store.scope(
-              state: { (/AppState.Screen.restore).extract(from: $0.screen) },
-              action: AppAction.restore
+              state: { (/AppComponent.State.Screen.restore).extract(from: $0.screen) },
+              action: AppComponent.Action.restore
             ),
             then: { store in
               RestoreView(store: store)
@@ -74,8 +74,8 @@ struct AppView: View {
         case .home:
           IfLetStore(
             store.scope(
-              state: { (/AppState.Screen.home).extract(from: $0.screen) },
-              action: AppAction.home
+              state: { (/AppComponent.State.Screen.home).extract(from: $0.screen) },
+              action: AppComponent.Action.home
             ),
             then: { store in
               HomeView(store: store)
@@ -137,9 +137,8 @@ struct AppView: View {
 struct AppView_Previews: PreviewProvider {
   static var previews: some View {
     AppView(store: Store(
-      initialState: AppState(),
-      reducer: .empty,
-      environment: ()
+      initialState: AppComponent.State(),
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/BackupFeature/Alerts.swift b/Examples/xx-messenger/Sources/BackupFeature/Alerts.swift
index 2bd95f770ff5ce1be9e808284d1d50f3d111af1d..e261adeba5b042c931817ffa7f0d4a6d849a2671 100644
--- a/Examples/xx-messenger/Sources/BackupFeature/Alerts.swift
+++ b/Examples/xx-messenger/Sources/BackupFeature/Alerts.swift
@@ -1,8 +1,8 @@
 import ComposableArchitecture
 
-extension AlertState where Action == BackupAction {
-  public static func error(_ error: Error) -> AlertState<BackupAction> {
-    AlertState(
+extension AlertState where Action == BackupComponent.Action {
+  public static func error(_ error: Error) -> AlertState<BackupComponent.Action> {
+    AlertState<BackupComponent.Action>(
       title: TextState("Error"),
       message: TextState(error.localizedDescription)
     )
diff --git a/Examples/xx-messenger/Sources/BackupFeature/BackupComponent.swift b/Examples/xx-messenger/Sources/BackupFeature/BackupComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..af3e3c34a5728a73a084c307b918fab128cce7b8
--- /dev/null
+++ b/Examples/xx-messenger/Sources/BackupFeature/BackupComponent.swift
@@ -0,0 +1,209 @@
+import AppCore
+import Combine
+import ComposableArchitecture
+import Foundation
+import XXClient
+import XXMessengerClient
+
+public struct BackupComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Field: String, Hashable {
+      case passphrase
+    }
+
+    public enum Error: String, Swift.Error, Equatable {
+      case contactUsernameMissing
+    }
+
+    public init(
+      isRunning: Bool = false,
+      isStarting: Bool = false,
+      isResuming: Bool = false,
+      isStopping: Bool = false,
+      backup: BackupStorage.Backup? = nil,
+      alert: AlertState<Action>? = nil,
+      focusedField: Field? = nil,
+      passphrase: String = "",
+      isExporting: Bool = false,
+      exportData: Data? = nil
+    ) {
+      self.isRunning = isRunning
+      self.isStarting = isStarting
+      self.isResuming = isResuming
+      self.isStopping = isStopping
+      self.backup = backup
+      self.alert = alert
+      self.focusedField = focusedField
+      self.passphrase = passphrase
+      self.isExporting = isExporting
+      self.exportData = exportData
+    }
+
+    public var isRunning: Bool
+    public var isStarting: Bool
+    public var isResuming: Bool
+    public var isStopping: Bool
+    public var backup: BackupStorage.Backup?
+    public var alert: AlertState<Action>?
+    @BindableState public var focusedField: Field?
+    @BindableState public var passphrase: String
+    @BindableState public var isExporting: Bool
+    public var exportData: Data?
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case task
+    case cancelTask
+    case startTapped
+    case resumeTapped
+    case stopTapped
+    case exportTapped
+    case alertDismissed
+    case backupUpdated(BackupStorage.Backup?)
+    case didStart(failure: NSError?)
+    case didResume(failure: NSError?)
+    case didStop(failure: NSError?)
+    case didExport(failure: NSError?)
+    case binding(BindingAction<State>)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.backupStorage) var backupStorage: BackupStorage
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      enum TaskEffectId {}
+
+      switch action {
+      case .task:
+        state.isRunning = messenger.isBackupRunning()
+        return Effect.run { subscriber in
+          subscriber.send(.backupUpdated(backupStorage.stored()))
+          let cancellable = backupStorage.observe { backup in
+            subscriber.send(.backupUpdated(backup))
+          }
+          return AnyCancellable { cancellable.cancel() }
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+        .cancellable(id: TaskEffectId.self, cancelInFlight: true)
+
+      case .cancelTask:
+        return .cancel(id: TaskEffectId.self)
+
+      case .startTapped:
+        state.isStarting = true
+        state.focusedField = nil
+        return Effect.run { [state] subscriber in
+          do {
+            let contact = try messenger.myContact(includeFacts: .types([.username]))
+            guard let username = try contact.getFact(.username)?.value else {
+              throw State.Error.contactUsernameMissing
+            }
+            try messenger.startBackup(
+              password: state.passphrase,
+              params: BackupParams(username: username)
+            )
+            subscriber.send(.didStart(failure: nil))
+          } catch {
+            subscriber.send(.didStart(failure: error as NSError))
+          }
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .resumeTapped:
+        state.isResuming = true
+        return Effect.run { subscriber in
+          do {
+            try messenger.resumeBackup()
+            subscriber.send(.didResume(failure: nil))
+          } catch {
+            subscriber.send(.didResume(failure: error as NSError))
+          }
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .stopTapped:
+        state.isStopping = true
+        return Effect.run { subscriber in
+          do {
+            try messenger.stopBackup()
+            try backupStorage.remove()
+            subscriber.send(.didStop(failure: nil))
+          } catch {
+            subscriber.send(.didStop(failure: error as NSError))
+          }
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .exportTapped:
+        state.isExporting = true
+        state.exportData = state.backup?.data
+        return .none
+
+      case .alertDismissed:
+        state.alert = nil
+        return .none
+
+      case .backupUpdated(let backup):
+        state.backup = backup
+        return .none
+
+      case .didStart(let failure):
+        state.isRunning = messenger.isBackupRunning()
+        state.isStarting = false
+        if let failure {
+          state.alert = .error(failure)
+        } else {
+          state.passphrase = ""
+        }
+        return .none
+
+      case .didResume(let failure):
+        state.isRunning = messenger.isBackupRunning()
+        state.isResuming = false
+        if let failure {
+          state.alert = .error(failure)
+        }
+        return .none
+
+      case .didStop(let failure):
+        state.isRunning = messenger.isBackupRunning()
+        state.isStopping = false
+        if let failure {
+          state.alert = .error(failure)
+        }
+        return .none
+
+      case .didExport(let failure):
+        state.isExporting = false
+        state.exportData = nil
+        if let failure {
+          state.alert = .error(failure)
+        }
+        return .none
+
+      case .binding(_):
+        return .none
+      }
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift b/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift
deleted file mode 100644
index 8010f17d1d0727c4621841dd124a93dcda0e0b5d..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/BackupFeature/BackupFeature.swift
+++ /dev/null
@@ -1,228 +0,0 @@
-import Combine
-import ComposableArchitecture
-import Foundation
-import XXClient
-import XXMessengerClient
-
-public struct BackupState: Equatable {
-  public enum Field: String, Hashable {
-    case passphrase
-  }
-
-  public enum Error: String, Swift.Error, Equatable {
-    case contactUsernameMissing
-  }
-
-  public init(
-    isRunning: Bool = false,
-    isStarting: Bool = false,
-    isResuming: Bool = false,
-    isStopping: Bool = false,
-    backup: BackupStorage.Backup? = nil,
-    alert: AlertState<BackupAction>? = nil,
-    focusedField: Field? = nil,
-    passphrase: String = "",
-    isExporting: Bool = false,
-    exportData: Data? = nil
-  ) {
-    self.isRunning = isRunning
-    self.isStarting = isStarting
-    self.isResuming = isResuming
-    self.isStopping = isStopping
-    self.backup = backup
-    self.alert = alert
-    self.focusedField = focusedField
-    self.passphrase = passphrase
-    self.isExporting = isExporting
-    self.exportData = exportData
-  }
-
-  public var isRunning: Bool
-  public var isStarting: Bool
-  public var isResuming: Bool
-  public var isStopping: Bool
-  public var backup: BackupStorage.Backup?
-  public var alert: AlertState<BackupAction>?
-  @BindableState public var focusedField: Field?
-  @BindableState public var passphrase: String
-  @BindableState public var isExporting: Bool
-  public var exportData: Data?
-}
-
-public enum BackupAction: Equatable, BindableAction {
-  case task
-  case cancelTask
-  case startTapped
-  case resumeTapped
-  case stopTapped
-  case exportTapped
-  case alertDismissed
-  case backupUpdated(BackupStorage.Backup?)
-  case didStart(failure: NSError?)
-  case didResume(failure: NSError?)
-  case didStop(failure: NSError?)
-  case didExport(failure: NSError?)
-  case binding(BindingAction<BackupState>)
-}
-
-public struct BackupEnvironment {
-  public init(
-    messenger: Messenger,
-    backupStorage: BackupStorage,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.backupStorage = backupStorage
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var backupStorage: BackupStorage
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension BackupEnvironment {
-  public static let unimplemented = BackupEnvironment(
-    messenger: .unimplemented,
-    backupStorage: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let backupReducer = Reducer<BackupState, BackupAction, BackupEnvironment>
-{ state, action, env in
-  enum TaskEffectId {}
-
-  switch action {
-  case .task:
-    state.isRunning = env.messenger.isBackupRunning()
-    return Effect.run { subscriber in
-      subscriber.send(.backupUpdated(env.backupStorage.stored()))
-      let cancellable = env.backupStorage.observe { backup in
-        subscriber.send(.backupUpdated(backup))
-      }
-      return AnyCancellable { cancellable.cancel() }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-    .cancellable(id: TaskEffectId.self, cancelInFlight: true)
-
-  case .cancelTask:
-    return .cancel(id: TaskEffectId.self)
-
-  case .startTapped:
-    state.isStarting = true
-    state.focusedField = nil
-    return Effect.run { [state] subscriber in
-      do {
-        let contact = try env.messenger.myContact(includeFacts: .types([.username]))
-        guard let username = try contact.getFact(.username)?.value else {
-          throw BackupState.Error.contactUsernameMissing
-        }
-        try env.messenger.startBackup(
-          password: state.passphrase,
-          params: BackupParams(username: username)
-        )
-        subscriber.send(.didStart(failure: nil))
-      } catch {
-        subscriber.send(.didStart(failure: error as NSError))
-      }
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .resumeTapped:
-    state.isResuming = true
-    return Effect.run { subscriber in
-      do {
-        try env.messenger.resumeBackup()
-        subscriber.send(.didResume(failure: nil))
-      } catch {
-        subscriber.send(.didResume(failure: error as NSError))
-      }
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .stopTapped:
-    state.isStopping = true
-    return Effect.run { subscriber in
-      do {
-        try env.messenger.stopBackup()
-        try env.backupStorage.remove()
-        subscriber.send(.didStop(failure: nil))
-      } catch {
-        subscriber.send(.didStop(failure: error as NSError))
-      }
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .exportTapped:
-    state.isExporting = true
-    state.exportData = state.backup?.data
-    return .none
-
-  case .alertDismissed:
-    state.alert = nil
-    return .none
-
-  case .backupUpdated(let backup):
-    state.backup = backup
-    return .none
-
-  case .didStart(let failure):
-    state.isRunning = env.messenger.isBackupRunning()
-    state.isStarting = false
-    if let failure {
-      state.alert = .error(failure)
-    } else {
-      state.passphrase = ""
-    }
-    return .none
-
-  case .didResume(let failure):
-    state.isRunning = env.messenger.isBackupRunning()
-    state.isResuming = false
-    if let failure {
-      state.alert = .error(failure)
-    }
-    return .none
-
-  case .didStop(let failure):
-    state.isRunning = env.messenger.isBackupRunning()
-    state.isStopping = false
-    if let failure {
-      state.alert = .error(failure)
-    }
-    return .none
-
-  case .didExport(let failure):
-    state.isExporting = false
-    state.exportData = nil
-    if let failure {
-      state.alert = .error(failure)
-    }
-    return .none
-
-  case .binding(_):
-    return .none
-  }
-}
-.binding()
diff --git a/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift b/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift
index 89510b2fbf2c993afa1438fd62408dc925d1c30e..78805e66acd18a8200d461f803243673d509bdac 100644
--- a/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift
+++ b/Examples/xx-messenger/Sources/BackupFeature/BackupView.swift
@@ -3,12 +3,12 @@ import SwiftUI
 import UniformTypeIdentifiers
 
 public struct BackupView: View {
-  public init(store: Store<BackupState, BackupAction>) {
+  public init(store: StoreOf<BackupComponent>) {
     self.store = store
   }
 
-  let store: Store<BackupState, BackupAction>
-  @FocusState var focusedField: BackupState.Field?
+  let store: StoreOf<BackupComponent>
+  @FocusState var focusedField: BackupComponent.State.Field?
 
   struct ViewState: Equatable {
     struct Backup: Equatable {
@@ -16,7 +16,7 @@ public struct BackupView: View {
       var size: Int
     }
 
-    init(state: BackupState) {
+    init(state: BackupComponent.State) {
       isRunning = state.isRunning
       isStarting = state.isStarting
       isResuming = state.isResuming
@@ -36,7 +36,7 @@ public struct BackupView: View {
     var isStopping: Bool
     var isLoading: Bool { isStarting || isResuming || isStopping }
     var backup: Backup?
-    var focusedField: BackupState.Field?
+    var focusedField: BackupComponent.State.Field?
     var passphrase: String
     var isExporting: Bool
     var exportData: Data?
@@ -67,7 +67,7 @@ public struct BackupView: View {
   }
 
   @ViewBuilder func newBackupSection(
-    _ viewStore: ViewStore<ViewState, BackupAction>
+    _ viewStore: ViewStore<ViewState, BackupComponent.Action>
   ) -> some View {
     Section {
       SecureField(
@@ -103,7 +103,7 @@ public struct BackupView: View {
   }
 
   @ViewBuilder func backupSection(
-    _ viewStore: ViewStore<ViewState, BackupAction>
+    _ viewStore: ViewStore<ViewState, BackupComponent.Action>
   ) -> some View {
     Section {
       backupView(viewStore)
@@ -115,7 +115,7 @@ public struct BackupView: View {
   }
 
   @ViewBuilder func backupView(
-    _ viewStore: ViewStore<ViewState, BackupAction>
+    _ viewStore: ViewStore<ViewState, BackupComponent.Action>
   ) -> some View {
     if let backup = viewStore.backup {
       HStack {
@@ -165,7 +165,7 @@ public struct BackupView: View {
   }
 
   @ViewBuilder func stopView(
-    _ viewStore: ViewStore<ViewState, BackupAction>
+    _ viewStore: ViewStore<ViewState, BackupComponent.Action>
   ) -> some View {
     if viewStore.isRunning {
       Button {
@@ -185,7 +185,7 @@ public struct BackupView: View {
   }
 
   @ViewBuilder func resumeView(
-    _ viewStore: ViewStore<ViewState, BackupAction>
+    _ viewStore: ViewStore<ViewState, BackupComponent.Action>
   ) -> some View {
     if !viewStore.isRunning, viewStore.backup != nil {
       Button {
@@ -240,9 +240,8 @@ public struct BackupView_Previews: PreviewProvider {
   public static var previews: some View {
     NavigationView {
       BackupView(store: Store(
-        initialState: BackupState(),
-        reducer: .empty,
-        environment: ()
+        initialState: BackupComponent.State(),
+        reducer: EmptyReducer()
       ))
     }
   }
diff --git a/Examples/xx-messenger/Sources/ChatFeature/ChatComponent.swift b/Examples/xx-messenger/Sources/ChatFeature/ChatComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..6c71789dbbb201122cfda709999ac36984363b55
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ChatFeature/ChatComponent.swift
@@ -0,0 +1,208 @@
+import AppCore
+import Combine
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct ChatComponent: ReducerProtocol {
+  public struct State: Equatable, Identifiable {
+    public enum ID: Equatable, Hashable {
+      case contact(Data)
+    }
+
+    public struct Message: Equatable, Identifiable {
+      public init(
+        id: Int64,
+        date: Date,
+        senderId: Data,
+        text: String,
+        status: XXModels.Message.Status,
+        fileTransfer: XXModels.FileTransfer? = nil
+      ) {
+        self.id = id
+        self.date = date
+        self.senderId = senderId
+        self.text = text
+        self.status = status
+        self.fileTransfer = fileTransfer
+      }
+
+      public var id: Int64
+      public var date: Date
+      public var senderId: Data
+      public var text: String
+      public var status: XXModels.Message.Status
+      public var fileTransfer: XXModels.FileTransfer?
+    }
+
+    public init(
+      id: ID,
+      myContactId: Data? = nil,
+      messages: IdentifiedArrayOf<Message> = [],
+      failure: String? = nil,
+      sendFailure: String? = nil,
+      text: String = ""
+    ) {
+      self.id = id
+      self.myContactId = myContactId
+      self.messages = messages
+      self.failure = failure
+      self.sendFailure = sendFailure
+      self.text = text
+    }
+
+    public var id: ID
+    public var myContactId: Data?
+    public var messages: IdentifiedArrayOf<Message>
+    public var failure: String?
+    public var sendFailure: String?
+    @BindableState public var text: String
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case start
+    case didFetchMessages(IdentifiedArrayOf<State.Message>)
+    case sendTapped
+    case sendFailed(String)
+    case imagePicked(Data)
+    case dismissSendFailureTapped
+    case binding(BindingAction<State>)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.sendMessage) var sendMessage: SendMessage
+  @Dependency(\.app.sendImage) var sendImage: SendImage
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      enum FetchEffectId {}
+
+      switch action {
+      case .start:
+        state.failure = nil
+        do {
+          let myContactId = try messenger.e2e.tryGet().getContact().getId()
+          state.myContactId = myContactId
+          let queryChat: XXModels.Message.Query.Chat
+          let receivedFileTransfersQuery: XXModels.FileTransfer.Query
+          let sentFileTransfersQuery: XXModels.FileTransfer.Query
+          switch state.id {
+          case .contact(let contactId):
+            queryChat = .direct(myContactId, contactId)
+            receivedFileTransfersQuery = .init(
+              contactId: contactId,
+              isIncoming: true
+            )
+            sentFileTransfersQuery = .init(
+              contactId: myContactId,
+              isIncoming: false
+            )
+          }
+          let messagesQuery = XXModels.Message.Query(chat: queryChat)
+          return Publishers.CombineLatest3(
+            try db().fetchMessagesPublisher(messagesQuery),
+            try db().fetchFileTransfersPublisher(receivedFileTransfersQuery),
+            try db().fetchFileTransfersPublisher(sentFileTransfersQuery)
+          )
+          .map { messages, receivedFileTransfers, sentFileTransfers in
+            (messages, receivedFileTransfers + sentFileTransfers)
+          }
+          .assertNoFailure()
+          .map { messages, fileTransfers in
+            messages.compactMap { message in
+              guard let id = message.id else { return nil }
+              return State.Message(
+                id: id,
+                date: message.date,
+                senderId: message.senderId,
+                text: message.text,
+                status: message.status,
+                fileTransfer: fileTransfers.first { $0.id == message.fileTransferId }
+              )
+            }
+          }
+          .removeDuplicates()
+          .map { IdentifiedArrayOf<State.Message>(uniqueElements: $0) }
+          .map(Action.didFetchMessages)
+          .subscribe(on: bgQueue)
+          .receive(on: mainQueue)
+          .eraseToEffect()
+          .cancellable(id: FetchEffectId.self, cancelInFlight: true)
+        } catch {
+          state.failure = error.localizedDescription
+          return .none
+        }
+
+      case .didFetchMessages(let messages):
+        state.messages = messages
+        return .none
+
+      case .sendTapped:
+        let text = state.text
+        let chatId = state.id
+        state.text = ""
+        return Effect.run { subscriber in
+          switch chatId {
+          case .contact(let recipientId):
+            sendMessage(
+              text: text,
+              to: recipientId,
+              onError: { error in
+                subscriber.send(.sendFailed(error.localizedDescription))
+              },
+              completion: {
+                subscriber.send(completion: .finished)
+              }
+            )
+          }
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .sendFailed(let failure):
+        state.sendFailure = failure
+        return .none
+
+      case .imagePicked(let data):
+        let chatId = state.id
+        return Effect.run { subscriber in
+          switch chatId {
+          case .contact(let recipientId):
+            sendImage(
+              data,
+              to: recipientId,
+              onError: { error in
+                subscriber.send(.sendFailed(error.localizedDescription))
+              },
+              completion: {
+                subscriber.send(completion: .finished)
+              }
+            )
+          }
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .dismissSendFailureTapped:
+        state.sendFailure = nil
+        return .none
+
+      case .binding(_):
+        return .none
+      }
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ChatFeature/ChatFeature.swift b/Examples/xx-messenger/Sources/ChatFeature/ChatFeature.swift
deleted file mode 100644
index 3538bc2536f23e493a298f2761fd3614711f77da..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/ChatFeature/ChatFeature.swift
+++ /dev/null
@@ -1,234 +0,0 @@
-import AppCore
-import Combine
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct ChatState: Equatable, Identifiable {
-  public enum ID: Equatable, Hashable {
-    case contact(Data)
-  }
-
-  public struct Message: Equatable, Identifiable {
-    public init(
-      id: Int64,
-      date: Date,
-      senderId: Data,
-      text: String,
-      status: XXModels.Message.Status,
-      fileTransfer: XXModels.FileTransfer? = nil
-    ) {
-      self.id = id
-      self.date = date
-      self.senderId = senderId
-      self.text = text
-      self.status = status
-      self.fileTransfer = fileTransfer
-    }
-
-    public var id: Int64
-    public var date: Date
-    public var senderId: Data
-    public var text: String
-    public var status: XXModels.Message.Status
-    public var fileTransfer: XXModels.FileTransfer?
-  }
-
-  public init(
-    id: ID,
-    myContactId: Data? = nil,
-    messages: IdentifiedArrayOf<Message> = [],
-    failure: String? = nil,
-    sendFailure: String? = nil,
-    text: String = ""
-  ) {
-    self.id = id
-    self.myContactId = myContactId
-    self.messages = messages
-    self.failure = failure
-    self.sendFailure = sendFailure
-    self.text = text
-  }
-
-  public var id: ID
-  public var myContactId: Data?
-  public var messages: IdentifiedArrayOf<Message>
-  public var failure: String?
-  public var sendFailure: String?
-  @BindableState public var text: String
-}
-
-public enum ChatAction: Equatable, BindableAction {
-  case start
-  case didFetchMessages(IdentifiedArrayOf<ChatState.Message>)
-  case sendTapped
-  case sendFailed(String)
-  case imagePicked(Data)
-  case dismissSendFailureTapped
-  case binding(BindingAction<ChatState>)
-}
-
-public struct ChatEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    sendMessage: SendMessage,
-    sendImage: SendImage,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.sendMessage = sendMessage
-    self.sendImage = sendImage
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var sendMessage: SendMessage
-  public var sendImage: SendImage
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension ChatEnvironment {
-  public static let unimplemented = ChatEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    sendMessage: .unimplemented,
-    sendImage: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let chatReducer = Reducer<ChatState, ChatAction, ChatEnvironment>
-{ state, action, env in
-  enum FetchEffectId {}
-
-  switch action {
-  case .start:
-    state.failure = nil
-    do {
-      let myContactId = try env.messenger.e2e.tryGet().getContact().getId()
-      state.myContactId = myContactId
-      let queryChat: XXModels.Message.Query.Chat
-      let receivedFileTransfersQuery: XXModels.FileTransfer.Query
-      let sentFileTransfersQuery: XXModels.FileTransfer.Query
-      switch state.id {
-      case .contact(let contactId):
-        queryChat = .direct(myContactId, contactId)
-        receivedFileTransfersQuery = .init(
-          contactId: contactId,
-          isIncoming: true
-        )
-        sentFileTransfersQuery = .init(
-          contactId: myContactId,
-          isIncoming: false
-        )
-      }
-      let messagesQuery = XXModels.Message.Query(chat: queryChat)
-      return Publishers.CombineLatest3(
-        try env.db().fetchMessagesPublisher(messagesQuery),
-        try env.db().fetchFileTransfersPublisher(receivedFileTransfersQuery),
-        try env.db().fetchFileTransfersPublisher(sentFileTransfersQuery)
-      )
-      .map { messages, receivedFileTransfers, sentFileTransfers in
-        (messages, receivedFileTransfers + sentFileTransfers)
-      }
-      .assertNoFailure()
-      .map { messages, fileTransfers in
-        messages.compactMap { message in
-          guard let id = message.id else { return nil }
-          return ChatState.Message(
-            id: id,
-            date: message.date,
-            senderId: message.senderId,
-            text: message.text,
-            status: message.status,
-            fileTransfer: fileTransfers.first { $0.id == message.fileTransferId }
-          )
-        }
-      }
-      .removeDuplicates()
-      .map { IdentifiedArrayOf<ChatState.Message>(uniqueElements: $0) }
-      .map(ChatAction.didFetchMessages)
-      .subscribe(on: env.bgQueue)
-      .receive(on: env.mainQueue)
-      .eraseToEffect()
-      .cancellable(id: FetchEffectId.self, cancelInFlight: true)
-    } catch {
-      state.failure = error.localizedDescription
-      return .none
-    }
-
-  case .didFetchMessages(let messages):
-    state.messages = messages
-    return .none
-
-  case .sendTapped:
-    let text = state.text
-    let chatId = state.id
-    state.text = ""
-    return Effect.run { subscriber in
-      switch chatId {
-      case .contact(let recipientId):
-        env.sendMessage(
-          text: text,
-          to: recipientId,
-          onError: { error in
-            subscriber.send(.sendFailed(error.localizedDescription))
-          },
-          completion: {
-            subscriber.send(completion: .finished)
-          }
-        )
-      }
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .sendFailed(let failure):
-    state.sendFailure = failure
-    return .none
-
-  case .imagePicked(let data):
-    let chatId = state.id
-    return Effect.run { subscriber in
-      switch chatId {
-      case .contact(let recipientId):
-        env.sendImage(
-          data,
-          to: recipientId,
-          onError: { error in
-            subscriber.send(.sendFailed(error.localizedDescription))
-          },
-          completion: {
-            subscriber.send(completion: .finished)
-          }
-        )
-      }
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .dismissSendFailureTapped:
-    state.sendFailure = nil
-    return .none
-
-  case .binding(_):
-    return .none
-  }
-}
-.binding()
diff --git a/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift b/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift
index 6058b61bc86fe9c54f25483e7de2a05c5a35dc9d..7ac107f479b1bf8dee80c566043997caa7644cb0 100644
--- a/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift
+++ b/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift
@@ -3,21 +3,21 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct ChatView: View {
-  public init(store: Store<ChatState, ChatAction>) {
+  public init(store: StoreOf<ChatComponent>) {
     self.store = store
   }
 
-  let store: Store<ChatState, ChatAction>
+  let store: StoreOf<ChatComponent>
   @State var isPresentingImagePicker = false
 
   struct ViewState: Equatable {
     var myContactId: Data?
-    var messages: IdentifiedArrayOf<ChatState.Message>
+    var messages: IdentifiedArrayOf<ChatComponent.State.Message>
     var failure: String?
     var sendFailure: String?
     var text: String
 
-    init(state: ChatState) {
+    init(state: ChatComponent.State) {
       myContactId = state.myContactId
       messages = state.messages
       failure = state.failure
@@ -84,7 +84,7 @@ public struct ChatView: View {
           HStack {
             TextField("Text", text: viewStore.binding(
               get: \.text,
-              send: { ChatAction.set(\.$text, $0) }
+              send: { ChatComponent.Action.set(\.$text, $0) }
             ))
             .textFieldStyle(.roundedBorder)
 
@@ -122,7 +122,7 @@ public struct ChatView: View {
   }
 
   struct MessageView: View {
-    var message: ChatState.Message
+    var message: ChatComponent.State.Message
     var myContactId: Data?
 
     var alignment: Alignment {
@@ -199,7 +199,7 @@ public struct ChatView_Previews: PreviewProvider {
   public static var previews: some View {
     NavigationView {
       ChatView(store: Store(
-        initialState: ChatState(
+        initialState: ChatComponent.State(
           id: .contact("contact-id".data(using: .utf8)!),
           myContactId: "my-contact-id".data(using: .utf8)!,
           messages: [
@@ -262,8 +262,7 @@ public struct ChatView_Previews: PreviewProvider {
           failure: "Something went wrong when fetching messages from database.",
           sendFailure: "Something went wrong when sending message."
         ),
-        reducer: .empty,
-        environment: ()
+        reducer: EmptyReducer()
       ))
     }
   }
diff --git a/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthComponent.swift b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5c62fa8f2a7c9ba928d19fa5f29adac127d791d4
--- /dev/null
+++ b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthComponent.swift
@@ -0,0 +1,72 @@
+import AppCore
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct CheckContactAuthComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Result: Equatable {
+      case success(Bool)
+      case failure(String)
+    }
+
+    public init(
+      contact: XXClient.Contact,
+      isChecking: Bool = false,
+      result: Result? = nil
+    ) {
+      self.contact = contact
+      self.isChecking = isChecking
+      self.result = result
+    }
+
+    public var contact: XXClient.Contact
+    public var isChecking: Bool
+    public var result: Result?
+  }
+
+  public enum Action: Equatable {
+    case checkTapped
+    case didCheck(State.Result)
+  }
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public init() {}
+
+  public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
+    switch action {
+    case .checkTapped:
+      state.isChecking = true
+      state.result = nil
+      return Effect.result { [state] in
+        do {
+          let e2e = try messenger.e2e.tryGet()
+          let contactId = try state.contact.getId()
+          let result = try e2e.hasAuthenticatedChannel(partnerId: contactId)
+          try db().bulkUpdateContacts.callAsFunction(
+            .init(id: [contactId]),
+            .init(authStatus: result ? .friend : .stranger)
+          )
+          return .success(.didCheck(.success(result)))
+        } catch {
+          return .success(.didCheck(.failure(error.localizedDescription)))
+        }
+      }
+      .subscribe(on: bgQueue)
+      .receive(on: mainQueue)
+      .eraseToEffect()
+
+    case .didCheck(let result):
+      state.isChecking = false
+      state.result = result
+      return .none
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthFeature.swift b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthFeature.swift
deleted file mode 100644
index 1f768be8d1cba4f20c1f2db65253788358a66023..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthFeature.swift
+++ /dev/null
@@ -1,94 +0,0 @@
-import AppCore
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct CheckContactAuthState: Equatable {
-  public enum Result: Equatable {
-    case success(Bool)
-    case failure(String)
-  }
-
-  public init(
-    contact: XXClient.Contact,
-    isChecking: Bool = false,
-    result: Result? = nil
-  ) {
-    self.contact = contact
-    self.isChecking = isChecking
-    self.result = result
-  }
-
-  public var contact: XXClient.Contact
-  public var isChecking: Bool
-  public var result: Result?
-}
-
-public enum CheckContactAuthAction: Equatable {
-  case checkTapped
-  case didCheck(CheckContactAuthState.Result)
-}
-
-public struct CheckContactAuthEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension CheckContactAuthEnvironment {
-  public static let unimplemented = CheckContactAuthEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let checkContactAuthReducer = Reducer<CheckContactAuthState, CheckContactAuthAction, CheckContactAuthEnvironment>
-{ state, action, env in
-  switch action {
-  case .checkTapped:
-    state.isChecking = true
-    state.result = nil
-    return Effect.result { [state] in
-      do {
-        let e2e = try env.messenger.e2e.tryGet()
-        let contactId = try state.contact.getId()
-        let result = try e2e.hasAuthenticatedChannel(partnerId: contactId)
-        try env.db().bulkUpdateContacts.callAsFunction(
-          .init(id: [contactId]),
-          .init(authStatus: result ? .friend : .stranger)
-        )
-        return .success(.didCheck(.success(result)))
-      } catch {
-        return .success(.didCheck(.failure(error.localizedDescription)))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .didCheck(let result):
-    state.isChecking = false
-    state.result = result
-    return .none
-  }
-}
diff --git a/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthView.swift b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthView.swift
index dd2e7894e256c67f4a1ddcf421877dcbfbac63cc..b4b75bbc7dde04a75549d805009b302e4dd6c9b0 100644
--- a/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthView.swift
+++ b/Examples/xx-messenger/Sources/CheckContactAuthFeature/CheckContactAuthView.swift
@@ -3,20 +3,20 @@ import SwiftUI
 import XXClient
 
 public struct CheckContactAuthView: View {
-  public init(store: Store<CheckContactAuthState, CheckContactAuthAction>) {
+  public init(store: StoreOf<CheckContactAuthComponent>) {
     self.store = store
   }
 
-  let store: Store<CheckContactAuthState, CheckContactAuthAction>
+  let store: StoreOf<CheckContactAuthComponent>
 
   struct ViewState: Equatable {
     var username: String?
     var email: String?
     var phone: String?
     var isChecking: Bool
-    var result: CheckContactAuthState.Result?
+    var result: CheckContactAuthComponent.State.Result?
 
-    init(state: CheckContactAuthState) {
+    init(state: CheckContactAuthComponent.State) {
       username = try? state.contact.getFact(.username)?.value
       email = try? state.contact.getFact(.email)?.value
       phone = try? state.contact.getFact(.phone)?.value
@@ -90,11 +90,10 @@ public struct CheckContactAuthView: View {
 public struct CheckContactAuthView_Previews: PreviewProvider {
   public static var previews: some View {
     CheckContactAuthView(store: Store(
-      initialState: CheckContactAuthState(
+      initialState: CheckContactAuthComponent.State(
         contact: .unimplemented("contact-data".data(using: .utf8)!)
       ),
-      reducer: .empty,
-      environment: ()
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestComponent.swift b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..3a67c2e9d018cded645299e1853f78def0c8c806
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestComponent.swift
@@ -0,0 +1,76 @@
+import AppCore
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct ConfirmRequestComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Result: Equatable {
+      case success
+      case failure(String)
+    }
+
+    public init(
+      contact: XXClient.Contact,
+      isConfirming: Bool = false,
+      result: Result? = nil
+    ) {
+      self.contact = contact
+      self.isConfirming = isConfirming
+      self.result = result
+    }
+
+    public var contact: XXClient.Contact
+    public var isConfirming: Bool
+    public var result: Result?
+  }
+
+  public enum Action: Equatable {
+    case confirmTapped
+    case didConfirm(State.Result)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
+    switch action {
+    case .confirmTapped:
+      state.isConfirming = true
+      state.result = nil
+      return Effect.result { [state] in
+        func updateStatus(_ status: XXModels.Contact.AuthStatus) throws {
+          try db().bulkUpdateContacts.callAsFunction(
+            .init(id: [try state.contact.getId()]),
+            .init(authStatus: status)
+          )
+        }
+        do {
+          try updateStatus(.confirming)
+          let e2e = try messenger.e2e.tryGet()
+          _ = try e2e.confirmReceivedRequest(partner: state.contact)
+          try updateStatus(.friend)
+          return .success(.didConfirm(.success))
+        } catch {
+          try? updateStatus(.confirmationFailed)
+          return .success(.didConfirm(.failure(error.localizedDescription)))
+        }
+      }
+      .subscribe(on: bgQueue)
+      .receive(on: mainQueue)
+      .eraseToEffect()
+
+    case .didConfirm(let result):
+      state.isConfirming = false
+      state.result = result
+      return .none
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift
deleted file mode 100644
index 7cc40da0b7e268ce6ca78bb1b57b07e00784b148..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestFeature.swift
+++ /dev/null
@@ -1,98 +0,0 @@
-import AppCore
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct ConfirmRequestState: Equatable {
-  public enum Result: Equatable {
-    case success
-    case failure(String)
-  }
-
-  public init(
-    contact: XXClient.Contact,
-    isConfirming: Bool = false,
-    result: Result? = nil
-  ) {
-    self.contact = contact
-    self.isConfirming = isConfirming
-    self.result = result
-  }
-
-  public var contact: XXClient.Contact
-  public var isConfirming: Bool
-  public var result: Result?
-}
-
-public enum ConfirmRequestAction: Equatable {
-  case confirmTapped
-  case didConfirm(ConfirmRequestState.Result)
-}
-
-public struct ConfirmRequestEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension ConfirmRequestEnvironment {
-  public static let unimplemented = ConfirmRequestEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let confirmRequestReducer = Reducer<ConfirmRequestState, ConfirmRequestAction, ConfirmRequestEnvironment>
-{ state, action, env in
-  switch action {
-  case .confirmTapped:
-    state.isConfirming = true
-    state.result = nil
-    return Effect.result { [state] in
-      func updateStatus(_ status: XXModels.Contact.AuthStatus) throws {
-        try env.db().bulkUpdateContacts.callAsFunction(
-          .init(id: [try state.contact.getId()]),
-          .init(authStatus: status)
-        )
-      }
-      do {
-        try updateStatus(.confirming)
-        let e2e = try env.messenger.e2e.tryGet()
-        _ = try e2e.confirmReceivedRequest(partner: state.contact)
-        try updateStatus(.friend)
-        return .success(.didConfirm(.success))
-      } catch {
-        try? updateStatus(.confirmationFailed)
-        return .success(.didConfirm(.failure(error.localizedDescription)))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .didConfirm(let result):
-    state.isConfirming = false
-    state.result = result
-    return .none
-  }
-}
diff --git a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestView.swift b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestView.swift
index 90ebc70e4490d51792db636a47d2e02b318f5820..69f9d03ec076105c84956a9db41ed4dfe5153265 100644
--- a/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestView.swift
+++ b/Examples/xx-messenger/Sources/ConfirmRequestFeature/ConfirmRequestView.swift
@@ -2,20 +2,20 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct ConfirmRequestView: View {
-  public init(store: Store<ConfirmRequestState, ConfirmRequestAction>) {
+  public init(store: StoreOf<ConfirmRequestComponent>) {
     self.store = store
   }
 
-  let store: Store<ConfirmRequestState, ConfirmRequestAction>
+  let store: StoreOf<ConfirmRequestComponent>
 
   struct ViewState: Equatable {
     var username: String?
     var email: String?
     var phone: String?
     var isConfirming: Bool
-    var result: ConfirmRequestState.Result?
+    var result: ConfirmRequestComponent.State.Result?
 
-    init(state: ConfirmRequestState) {
+    init(state: ConfirmRequestComponent.State) {
       username = try? state.contact.getFact(.username)?.value
       email = try? state.contact.getFact(.email)?.value
       phone = try? state.contact.getFact(.phone)?.value
@@ -84,11 +84,10 @@ public struct ConfirmRequestView: View {
 public struct ConfirmRequestView_Previews: PreviewProvider {
   public static var previews: some View {
     ConfirmRequestView(store: Store(
-      initialState: ConfirmRequestState(
+      initialState: ConfirmRequestComponent.State(
         contact: .unimplemented("contact-data".data(using: .utf8)!)
       ),
-      reducer: .empty,
-      environment: ()
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactComponent.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..160c671feac5b1e7bf16a221259364e7549f50c1
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactComponent.swift
@@ -0,0 +1,273 @@
+import AppCore
+import ChatFeature
+import CheckContactAuthFeature
+import ComposableArchitecture
+import ComposablePresentation
+import ConfirmRequestFeature
+import ContactLookupFeature
+import Foundation
+import ResetAuthFeature
+import SendRequestFeature
+import VerifyContactFeature
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct ContactComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public init(
+      id: Data,
+      dbContact: XXModels.Contact? = nil,
+      xxContact: XXClient.Contact? = nil,
+      importUsername: Bool = true,
+      importEmail: Bool = true,
+      importPhone: Bool = true,
+      lookup: ContactLookupComponent.State? = nil,
+      sendRequest: SendRequestComponent.State? = nil,
+      verifyContact: VerifyContactComponent.State? = nil,
+      confirmRequest: ConfirmRequestComponent.State? = nil,
+      checkAuth: CheckContactAuthComponent.State? = nil,
+      resetAuth: ResetAuthComponent.State? = nil,
+      chat: ChatComponent.State? = nil
+    ) {
+      self.id = id
+      self.dbContact = dbContact
+      self.xxContact = xxContact
+      self.importUsername = importUsername
+      self.importEmail = importEmail
+      self.importPhone = importPhone
+      self.lookup = lookup
+      self.sendRequest = sendRequest
+      self.verifyContact = verifyContact
+      self.confirmRequest = confirmRequest
+      self.checkAuth = checkAuth
+      self.resetAuth = resetAuth
+      self.chat = chat
+    }
+
+    public var id: Data
+    public var dbContact: XXModels.Contact?
+    public var xxContact: XXClient.Contact?
+    @BindableState public var importUsername: Bool
+    @BindableState public var importEmail: Bool
+    @BindableState public var importPhone: Bool
+    public var lookup: ContactLookupComponent.State?
+    public var sendRequest: SendRequestComponent.State?
+    public var verifyContact: VerifyContactComponent.State?
+    public var confirmRequest: ConfirmRequestComponent.State?
+    public var checkAuth: CheckContactAuthComponent.State?
+    public var resetAuth: ResetAuthComponent.State?
+    public var chat: ChatComponent.State?
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case start
+    case dbContactFetched(XXModels.Contact?)
+    case importFactsTapped
+    case lookupTapped
+    case lookupDismissed
+    case lookup(ContactLookupComponent.Action)
+    case sendRequestTapped
+    case sendRequestDismissed
+    case sendRequest(SendRequestComponent.Action)
+    case verifyContactTapped
+    case verifyContactDismissed
+    case verifyContact(VerifyContactComponent.Action)
+    case checkAuthTapped
+    case checkAuthDismissed
+    case checkAuth(CheckContactAuthComponent.Action)
+    case confirmRequestTapped
+    case confirmRequestDismissed
+    case confirmRequest(ConfirmRequestComponent.Action)
+    case resetAuthTapped
+    case resetAuthDismissed
+    case resetAuth(ResetAuthComponent.Action)
+    case chatTapped
+    case chatDismissed
+    case chat(ChatComponent.Action)
+    case binding(BindingAction<State>)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      enum DBFetchEffectID {}
+
+      switch action {
+      case .start:
+        return try! db().fetchContactsPublisher(.init(id: [state.id]))
+          .assertNoFailure()
+          .map(\.first)
+          .map(Action.dbContactFetched)
+          .subscribe(on: bgQueue)
+          .receive(on: mainQueue)
+          .eraseToEffect()
+          .cancellable(id: DBFetchEffectID.self, cancelInFlight: true)
+
+      case .dbContactFetched(let contact):
+        state.dbContact = contact
+        return .none
+
+      case .importFactsTapped:
+        guard let xxContact = state.xxContact else { return .none }
+        return .fireAndForget { [state] in
+          var dbContact = state.dbContact ?? XXModels.Contact(id: state.id)
+          dbContact.marshaled = xxContact.data
+          if state.importUsername {
+            dbContact.username = try? xxContact.getFact(.username)?.value
+          }
+          if state.importEmail {
+            dbContact.email = try? xxContact.getFact(.email)?.value
+          }
+          if state.importPhone {
+            dbContact.phone = try? xxContact.getFact(.phone)?.value
+          }
+          _ = try! db().saveContact(dbContact)
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .lookupTapped:
+        state.lookup = ContactLookupComponent.State(id: state.id)
+        return .none
+
+      case .lookupDismissed:
+        state.lookup = nil
+        return .none
+
+      case .lookup(.didLookup(let xxContact)):
+        state.xxContact = xxContact
+        state.lookup = nil
+        return .none
+
+      case .sendRequestTapped:
+        if let xxContact = state.xxContact {
+          state.sendRequest = SendRequestComponent.State(contact: xxContact)
+        } else if let marshaled = state.dbContact?.marshaled {
+          state.sendRequest = SendRequestComponent.State(contact: .live(marshaled))
+        }
+        return .none
+
+      case .sendRequestDismissed:
+        state.sendRequest = nil
+        return .none
+
+      case .sendRequest(.sendSucceeded):
+        state.sendRequest = nil
+        return .none
+
+      case .verifyContactTapped:
+        if let marshaled = state.dbContact?.marshaled {
+          state.verifyContact = VerifyContactComponent.State(
+            contact: .live(marshaled)
+          )
+        }
+        return .none
+
+      case .verifyContactDismissed:
+        state.verifyContact = nil
+        return .none
+
+      case .checkAuthTapped:
+        if let marshaled = state.dbContact?.marshaled {
+          state.checkAuth = CheckContactAuthComponent.State(
+            contact: .live(marshaled)
+          )
+        }
+        return .none
+
+      case .checkAuthDismissed:
+        state.checkAuth = nil
+        return .none
+
+      case .confirmRequestTapped:
+        if let marshaled = state.dbContact?.marshaled {
+          state.confirmRequest = ConfirmRequestComponent.State(
+            contact: .live(marshaled)
+          )
+        }
+        return .none
+
+      case .confirmRequestDismissed:
+        state.confirmRequest = nil
+        return .none
+
+      case .chatTapped:
+        state.chat = ChatComponent.State(id: .contact(state.id))
+        return .none
+
+      case .chatDismissed:
+        state.chat = nil
+        return .none
+
+      case .resetAuthTapped:
+        if let marshaled = state.dbContact?.marshaled {
+          state.resetAuth = ResetAuthComponent.State(
+            partner: .live(marshaled)
+          )
+        }
+        return .none
+
+      case .resetAuthDismissed:
+        state.resetAuth = nil
+        return .none
+
+      case .binding(_), .lookup(_), .sendRequest(_),
+          .verifyContact(_), .confirmRequest(_),
+          .checkAuth(_), .resetAuth(_), .chat(_):
+        return .none
+      }
+    }
+    .presenting(
+      state: .keyPath(\.lookup),
+      id: .notNil(),
+      action: /ContactComponent.Action.lookup,
+      presented: { ContactLookupComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.sendRequest),
+      id: .notNil(),
+      action: /ContactComponent.Action.sendRequest,
+      presented: { SendRequestComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.verifyContact),
+      id: .notNil(),
+      action: /ContactComponent.Action.verifyContact,
+      presented: { VerifyContactComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.confirmRequest),
+      id: .notNil(),
+      action: /ContactComponent.Action.confirmRequest,
+      presented: { ConfirmRequestComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.checkAuth),
+      id: .notNil(),
+      action: /ContactComponent.Action.checkAuth,
+      presented: { CheckContactAuthComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.resetAuth),
+      id: .notNil(),
+      action: /ContactComponent.Action.resetAuth,
+      presented: { ResetAuthComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.chat),
+      id: .keyPath(\.?.id),
+      action: /ContactComponent.Action.chat,
+      presented: { ChatComponent() }
+    )
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
deleted file mode 100644
index be66fc8dfc8f59f7f2e20e1a1899a23da10106f6..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactFeature.swift
+++ /dev/null
@@ -1,328 +0,0 @@
-import AppCore
-import ChatFeature
-import CheckContactAuthFeature
-import ComposableArchitecture
-import ComposablePresentation
-import ConfirmRequestFeature
-import ContactLookupFeature
-import Foundation
-import ResetAuthFeature
-import SendRequestFeature
-import VerifyContactFeature
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct ContactState: Equatable {
-  public init(
-    id: Data,
-    dbContact: XXModels.Contact? = nil,
-    xxContact: XXClient.Contact? = nil,
-    importUsername: Bool = true,
-    importEmail: Bool = true,
-    importPhone: Bool = true,
-    lookup: ContactLookupState? = nil,
-    sendRequest: SendRequestState? = nil,
-    verifyContact: VerifyContactState? = nil,
-    confirmRequest: ConfirmRequestState? = nil,
-    checkAuth: CheckContactAuthState? = nil,
-    resetAuth: ResetAuthState? = nil,
-    chat: ChatState? = nil
-  ) {
-    self.id = id
-    self.dbContact = dbContact
-    self.xxContact = xxContact
-    self.importUsername = importUsername
-    self.importEmail = importEmail
-    self.importPhone = importPhone
-    self.lookup = lookup
-    self.sendRequest = sendRequest
-    self.verifyContact = verifyContact
-    self.confirmRequest = confirmRequest
-    self.checkAuth = checkAuth
-    self.resetAuth = resetAuth
-    self.chat = chat
-  }
-
-  public var id: Data
-  public var dbContact: XXModels.Contact?
-  public var xxContact: XXClient.Contact?
-  @BindableState public var importUsername: Bool
-  @BindableState public var importEmail: Bool
-  @BindableState public var importPhone: Bool
-  public var lookup: ContactLookupState?
-  public var sendRequest: SendRequestState?
-  public var verifyContact: VerifyContactState?
-  public var confirmRequest: ConfirmRequestState?
-  public var checkAuth: CheckContactAuthState?
-  public var resetAuth: ResetAuthState?
-  public var chat: ChatState?
-}
-
-public enum ContactAction: Equatable, BindableAction {
-  case start
-  case dbContactFetched(XXModels.Contact?)
-  case importFactsTapped
-  case lookupTapped
-  case lookupDismissed
-  case lookup(ContactLookupAction)
-  case sendRequestTapped
-  case sendRequestDismissed
-  case sendRequest(SendRequestAction)
-  case verifyContactTapped
-  case verifyContactDismissed
-  case verifyContact(VerifyContactAction)
-  case checkAuthTapped
-  case checkAuthDismissed
-  case checkAuth(CheckContactAuthAction)
-  case confirmRequestTapped
-  case confirmRequestDismissed
-  case confirmRequest(ConfirmRequestAction)
-  case resetAuthTapped
-  case resetAuthDismissed
-  case resetAuth(ResetAuthAction)
-  case chatTapped
-  case chatDismissed
-  case chat(ChatAction)
-  case binding(BindingAction<ContactState>)
-}
-
-public struct ContactEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>,
-    lookup: @escaping () -> ContactLookupEnvironment,
-    sendRequest: @escaping () -> SendRequestEnvironment,
-    verifyContact: @escaping () -> VerifyContactEnvironment,
-    confirmRequest: @escaping () -> ConfirmRequestEnvironment,
-    checkAuth: @escaping () -> CheckContactAuthEnvironment,
-    resetAuth: @escaping () -> ResetAuthEnvironment,
-    chat: @escaping () -> ChatEnvironment
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-    self.lookup = lookup
-    self.sendRequest = sendRequest
-    self.verifyContact = verifyContact
-    self.confirmRequest = confirmRequest
-    self.checkAuth = checkAuth
-    self.resetAuth = resetAuth
-    self.chat = chat
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-  public var lookup: () -> ContactLookupEnvironment
-  public var sendRequest: () -> SendRequestEnvironment
-  public var verifyContact: () -> VerifyContactEnvironment
-  public var confirmRequest: () -> ConfirmRequestEnvironment
-  public var checkAuth: () -> CheckContactAuthEnvironment
-  public var resetAuth: () -> ResetAuthEnvironment
-  public var chat: () -> ChatEnvironment
-}
-
-#if DEBUG
-extension ContactEnvironment {
-  public static let unimplemented = ContactEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented,
-    lookup: { .unimplemented },
-    sendRequest: { .unimplemented },
-    verifyContact: { .unimplemented },
-    confirmRequest: { .unimplemented },
-    checkAuth: { .unimplemented },
-    resetAuth: { .unimplemented },
-    chat: { .unimplemented }
-  )
-}
-#endif
-
-public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironment>
-{ state, action, env in
-  enum DBFetchEffectID {}
-
-  switch action {
-  case .start:
-    return try! env.db().fetchContactsPublisher(.init(id: [state.id]))
-      .assertNoFailure()
-      .map(\.first)
-      .map(ContactAction.dbContactFetched)
-      .subscribe(on: env.bgQueue)
-      .receive(on: env.mainQueue)
-      .eraseToEffect()
-      .cancellable(id: DBFetchEffectID.self, cancelInFlight: true)
-
-  case .dbContactFetched(let contact):
-    state.dbContact = contact
-    return .none
-
-  case .importFactsTapped:
-    guard let xxContact = state.xxContact else { return .none }
-    return .fireAndForget { [state] in
-      var dbContact = state.dbContact ?? XXModels.Contact(id: state.id)
-      dbContact.marshaled = xxContact.data
-      if state.importUsername {
-        dbContact.username = try? xxContact.getFact(.username)?.value
-      }
-      if state.importEmail {
-        dbContact.email = try? xxContact.getFact(.email)?.value
-      }
-      if state.importPhone {
-        dbContact.phone = try? xxContact.getFact(.phone)?.value
-      }
-      _ = try! env.db().saveContact(dbContact)
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .lookupTapped:
-    state.lookup = ContactLookupState(id: state.id)
-    return .none
-
-  case .lookupDismissed:
-    state.lookup = nil
-    return .none
-
-  case .lookup(.didLookup(let xxContact)):
-    state.xxContact = xxContact
-    state.lookup = nil
-    return .none
-
-  case .sendRequestTapped:
-    if let xxContact = state.xxContact {
-      state.sendRequest = SendRequestState(contact: xxContact)
-    } else if let marshaled = state.dbContact?.marshaled {
-      state.sendRequest = SendRequestState(contact: .live(marshaled))
-    }
-    return .none
-
-  case .sendRequestDismissed:
-    state.sendRequest = nil
-    return .none
-
-  case .sendRequest(.sendSucceeded):
-    state.sendRequest = nil
-    return .none
-
-  case .verifyContactTapped:
-    if let marshaled = state.dbContact?.marshaled {
-      state.verifyContact = VerifyContactState(
-        contact: .live(marshaled)
-      )
-    }
-    return .none
-
-  case .verifyContactDismissed:
-    state.verifyContact = nil
-    return .none
-
-  case .checkAuthTapped:
-    if let marshaled = state.dbContact?.marshaled {
-      state.checkAuth = CheckContactAuthState(
-        contact: .live(marshaled)
-      )
-    }
-    return .none
-
-  case .checkAuthDismissed:
-    state.checkAuth = nil
-    return .none
-
-  case .confirmRequestTapped:
-    if let marshaled = state.dbContact?.marshaled {
-      state.confirmRequest = ConfirmRequestState(
-        contact: .live(marshaled)
-      )
-    }
-    return .none
-
-  case .confirmRequestDismissed:
-    state.confirmRequest = nil
-    return .none
-
-  case .chatTapped:
-    state.chat = ChatState(id: .contact(state.id))
-    return .none
-
-  case .chatDismissed:
-    state.chat = nil
-    return .none
-
-  case .resetAuthTapped:
-    if let marshaled = state.dbContact?.marshaled {
-      state.resetAuth = ResetAuthState(
-        partner: .live(marshaled)
-      )
-    }
-    return .none
-
-  case .resetAuthDismissed:
-    state.resetAuth = nil
-    return .none
-
-  case .binding(_), .lookup(_), .sendRequest(_),
-      .verifyContact(_), .confirmRequest(_),
-      .checkAuth(_), .resetAuth(_), .chat(_):
-    return .none
-  }
-}
-.binding()
-.presenting(
-  contactLookupReducer,
-  state: .keyPath(\.lookup),
-  id: .notNil(),
-  action: /ContactAction.lookup,
-  environment: { $0.lookup() }
-)
-.presenting(
-  sendRequestReducer,
-  state: .keyPath(\.sendRequest),
-  id: .notNil(),
-  action: /ContactAction.sendRequest,
-  environment: { $0.sendRequest() }
-)
-.presenting(
-  verifyContactReducer,
-  state: .keyPath(\.verifyContact),
-  id: .notNil(),
-  action: /ContactAction.verifyContact,
-  environment: { $0.verifyContact() }
-)
-.presenting(
-  confirmRequestReducer,
-  state: .keyPath(\.confirmRequest),
-  id: .notNil(),
-  action: /ContactAction.confirmRequest,
-  environment: { $0.confirmRequest() }
-)
-.presenting(
-  checkContactAuthReducer,
-  state: .keyPath(\.checkAuth),
-  id: .notNil(),
-  action: /ContactAction.checkAuth,
-  environment: { $0.checkAuth() }
-)
-.presenting(
-  resetAuthReducer,
-  state: .keyPath(\.resetAuth),
-  id: .notNil(),
-  action: /ContactAction.resetAuth,
-  environment: { $0.resetAuth() }
-)
-.presenting(
-  chatReducer,
-  state: .keyPath(\.chat),
-  id: .keyPath(\.?.id),
-  action: /ContactAction.chat,
-  environment: { $0.chat() }
-)
diff --git a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
index 7da763e03748536aabb0cc99698c2edd0ee0fba4..1cbf0e91bdf20a090838fd770861a9120825afba 100644
--- a/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
+++ b/Examples/xx-messenger/Sources/ContactFeature/ContactView.swift
@@ -13,11 +13,11 @@ import XXClient
 import XXModels
 
 public struct ContactView: View {
-  public init(store: Store<ContactState, ContactAction>) {
+  public init(store: StoreOf<ContactComponent>) {
     self.store = store
   }
 
-  let store: Store<ContactState, ContactAction>
+  let store: StoreOf<ContactComponent>
 
   struct ViewState: Equatable {
     var dbContact: XXModels.Contact?
@@ -35,7 +35,7 @@ public struct ContactView: View {
     var canCheckAuthorization: Bool
     var canResetAuthorization: Bool
 
-    init(state: ContactState) {
+    init(state: ContactComponent.State) {
       dbContact = state.dbContact
       xxContactIsSet = state.xxContact != nil
       xxContactUsername = try? state.xxContact?.getFact(.username)?.value
@@ -217,7 +217,7 @@ public struct ContactView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.lookup,
-          action: ContactAction.lookup
+          action: ContactComponent.Action.lookup
         ),
         mapState: replayNonNil(),
         onDeactivate: { viewStore.send(.lookupDismissed) },
@@ -226,7 +226,7 @@ public struct ContactView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.sendRequest,
-          action: ContactAction.sendRequest
+          action: ContactComponent.Action.sendRequest
         ),
         mapState: replayNonNil(),
         onDeactivate: { viewStore.send(.sendRequestDismissed) },
@@ -235,7 +235,7 @@ public struct ContactView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.verifyContact,
-          action: ContactAction.verifyContact
+          action: ContactComponent.Action.verifyContact
         ),
         onDeactivate: { viewStore.send(.verifyContactDismissed) },
         destination: VerifyContactView.init(store:)
@@ -243,7 +243,7 @@ public struct ContactView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.confirmRequest,
-          action: ContactAction.confirmRequest
+          action: ContactComponent.Action.confirmRequest
         ),
         onDeactivate: { viewStore.send(.confirmRequestDismissed) },
         destination: ConfirmRequestView.init(store:)
@@ -251,7 +251,7 @@ public struct ContactView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.checkAuth,
-          action: ContactAction.checkAuth
+          action: ContactComponent.Action.checkAuth
         ),
         onDeactivate: { viewStore.send(.checkAuthDismissed) },
         destination: CheckContactAuthView.init(store:)
@@ -259,7 +259,7 @@ public struct ContactView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.resetAuth,
-          action: ContactAction.resetAuth
+          action: ContactComponent.Action.resetAuth
         ),
         onDeactivate: { viewStore.send(.resetAuthDismissed) },
         destination: ResetAuthView.init(store:)
@@ -267,7 +267,7 @@ public struct ContactView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.chat,
-          action: ContactAction.chat
+          action: ContactComponent.Action.chat
         ),
         onDeactivate: { viewStore.send(.chatDismissed) },
         destination: ChatView.init(store:)
@@ -280,11 +280,10 @@ public struct ContactView: View {
 public struct ContactView_Previews: PreviewProvider {
   public static var previews: some View {
     ContactView(store: Store(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!
       ),
-      reducer: .empty,
-      environment: ()
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupComponent.swift b/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..3369159037a2e12d8ad4846bad9effc8da3e0e11
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupComponent.swift
@@ -0,0 +1,64 @@
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+
+public struct ContactLookupComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public init(
+      id: Data,
+      isLookingUp: Bool = false,
+      failure: String? = nil
+    ) {
+      self.id = id
+      self.isLookingUp = isLookingUp
+      self.failure = failure
+    }
+
+    public var id: Data
+    public var isLookingUp: Bool
+    public var failure: String?
+  }
+
+  public enum Action: Equatable {
+    case lookupTapped
+    case didLookup(XXClient.Contact)
+    case didFail(NSError)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger
+  @Dependency(\.app.mainQueue) var mainQueue
+  @Dependency(\.app.bgQueue) var bgQueue
+
+  public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
+    switch action {
+    case .lookupTapped:
+      state.isLookingUp = true
+      state.failure = nil
+      return Effect.result { [state] in
+        do {
+          let contact = try messenger.lookupContact(id: state.id)
+          return .success(.didLookup(contact))
+        } catch {
+          return .success(.didFail(error as NSError))
+        }
+      }
+      .subscribe(on: bgQueue)
+      .receive(on: mainQueue)
+      .eraseToEffect()
+
+    case .didLookup(_):
+      state.isLookingUp = false
+      state.failure = nil
+      return .none
+
+    case .didFail(let error):
+      state.isLookingUp = false
+      state.failure = error.localizedDescription
+      return .none
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupFeature.swift b/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupFeature.swift
deleted file mode 100644
index 0b6f92dd46b351f6d10f78ef4b50961dad3e1fae..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupFeature.swift
+++ /dev/null
@@ -1,83 +0,0 @@
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-
-public struct ContactLookupState: Equatable {
-  public init(
-    id: Data,
-    isLookingUp: Bool = false,
-    failure: String? = nil
-  ) {
-    self.id = id
-    self.isLookingUp = isLookingUp
-    self.failure = failure
-  }
-
-  public var id: Data
-  public var isLookingUp: Bool
-  public var failure: String?
-}
-
-public enum ContactLookupAction: Equatable {
-  case lookupTapped
-  case didLookup(XXClient.Contact)
-  case didFail(NSError)
-}
-
-public struct ContactLookupEnvironment {
-  public init(
-    messenger: Messenger,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension ContactLookupEnvironment {
-  public static let unimplemented = ContactLookupEnvironment(
-    messenger: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let contactLookupReducer = Reducer<ContactLookupState, ContactLookupAction, ContactLookupEnvironment>
-{ state, action, env in
-  switch action {
-  case .lookupTapped:
-    state.isLookingUp = true
-    state.failure = nil
-    return Effect.result { [state] in
-      do {
-        let contact = try env.messenger.lookupContact(id: state.id)
-        return .success(.didLookup(contact))
-      } catch {
-        return .success(.didFail(error as NSError))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .didLookup(_):
-    state.isLookingUp = false
-    state.failure = nil
-    return .none
-
-  case .didFail(let error):
-    state.isLookingUp = false
-    state.failure = error.localizedDescription
-    return .none
-  }
-}
diff --git a/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupView.swift b/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupView.swift
index 6ce83eda6ba5dacfcf15c2fca4e8c202087ef42d..c4850fab42272c7ceba3cf626e625653ec04b4d3 100644
--- a/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupView.swift
+++ b/Examples/xx-messenger/Sources/ContactLookupFeature/ContactLookupView.swift
@@ -3,14 +3,14 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct ContactLookupView: View {
-  public init(store: Store<ContactLookupState, ContactLookupAction>) {
+  public init(store: StoreOf<ContactLookupComponent>) {
     self.store = store
   }
 
-  let store: Store<ContactLookupState, ContactLookupAction>
+  let store: StoreOf<ContactLookupComponent>
 
   struct ViewState: Equatable {
-    init(state: ContactLookupState) {
+    init(state: ContactLookupComponent.State) {
       id = state.id
       isLookingUp = state.isLookingUp
       failure = state.failure
@@ -64,11 +64,10 @@ public struct ContactLookupView_Previews: PreviewProvider {
   public static var previews: some View {
     NavigationView {
       ContactLookupView(store: Store(
-        initialState: ContactLookupState(
+        initialState: ContactLookupComponent.State(
           id: "1234".data(using: .utf8)!
         ),
-        reducer: .empty,
-        environment: ()
+        reducer: EmptyReducer()
       ))
     }
   }
diff --git a/Examples/xx-messenger/Sources/ContactsFeature/ContactsComponent.swift b/Examples/xx-messenger/Sources/ContactsFeature/ContactsComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a0ce4ed860b1dd45596fd70039aa1344b8deed76
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ContactsFeature/ContactsComponent.swift
@@ -0,0 +1,105 @@
+import AppCore
+import ComposableArchitecture
+import ComposablePresentation
+import ContactFeature
+import Foundation
+import MyContactFeature
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct ContactsComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public init(
+      myId: Data? = nil,
+      contacts: IdentifiedArrayOf<XXModels.Contact> = [],
+      contact: ContactComponent.State? = nil,
+      myContact: MyContactComponent.State? = nil
+    ) {
+      self.myId = myId
+      self.contacts = contacts
+      self.contact = contact
+      self.myContact = myContact
+    }
+
+    public var myId: Data?
+    public var contacts: IdentifiedArrayOf<XXModels.Contact>
+    public var contact: ContactComponent.State?
+    public var myContact: MyContactComponent.State?
+  }
+
+  public enum Action: Equatable {
+    case start
+    case didFetchContacts([XXModels.Contact])
+    case contactSelected(XXModels.Contact)
+    case contactDismissed
+    case contact(ContactComponent.Action)
+    case myContactSelected
+    case myContactDismissed
+    case myContact(MyContactComponent.Action)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    Reduce { state, action in
+      switch action {
+      case .start:
+        state.myId = try? messenger.e2e.tryGet().getContact().getId()
+        return Effect
+          .catching { try db() }
+          .flatMap { $0.fetchContactsPublisher(.init()) }
+          .assertNoFailure()
+          .map(Action.didFetchContacts)
+          .subscribe(on: bgQueue)
+          .receive(on: mainQueue)
+          .eraseToEffect()
+
+      case .didFetchContacts(var contacts):
+        if let myId = state.myId,
+           let myIndex = contacts.firstIndex(where: { $0.id == myId }) {
+          contacts.move(fromOffsets: [myIndex], toOffset: contacts.startIndex)
+        }
+        state.contacts = IdentifiedArray(uniqueElements: contacts)
+        return .none
+
+      case .contactSelected(let contact):
+        state.contact = ContactComponent.State(id: contact.id, dbContact: contact)
+        return .none
+
+      case .contactDismissed:
+        state.contact = nil
+        return .none
+
+      case .myContactSelected:
+        state.myContact = MyContactComponent.State()
+        return .none
+
+      case .myContactDismissed:
+        state.myContact = nil
+        return .none
+
+      case .contact(_), .myContact(_):
+        return .none
+      }
+    }
+    .presenting(
+      state: .keyPath(\.contact),
+      id: .keyPath(\.?.id),
+      action: /Action.contact,
+      presented: { ContactComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.myContact),
+      id: .notNil(),
+      action: /Action.myContact,
+      presented: { MyContactComponent() }
+    )
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ContactsFeature/ContactsFeature.swift b/Examples/xx-messenger/Sources/ContactsFeature/ContactsFeature.swift
deleted file mode 100644
index 680a231ec8cc9e6e13487a849a65941bda7a7428..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/ContactsFeature/ContactsFeature.swift
+++ /dev/null
@@ -1,135 +0,0 @@
-import AppCore
-import ComposableArchitecture
-import ComposablePresentation
-import ContactFeature
-import Foundation
-import MyContactFeature
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct ContactsState: Equatable {
-  public init(
-    myId: Data? = nil,
-    contacts: IdentifiedArrayOf<XXModels.Contact> = [],
-    contact: ContactState? = nil,
-    myContact: MyContactState? = nil
-  ) {
-    self.myId = myId
-    self.contacts = contacts
-    self.contact = contact
-    self.myContact = myContact
-  }
-
-  public var myId: Data?
-  public var contacts: IdentifiedArrayOf<XXModels.Contact>
-  public var contact: ContactState?
-  public var myContact: MyContactState?
-}
-
-public enum ContactsAction: Equatable {
-  case start
-  case didFetchContacts([XXModels.Contact])
-  case contactSelected(XXModels.Contact)
-  case contactDismissed
-  case contact(ContactAction)
-  case myContactSelected
-  case myContactDismissed
-  case myContact(MyContactAction)
-}
-
-public struct ContactsEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>,
-    contact: @escaping () -> ContactEnvironment,
-    myContact: @escaping () -> MyContactEnvironment
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-    self.contact = contact
-    self.myContact = myContact
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-  public var contact: () -> ContactEnvironment
-  public var myContact: () -> MyContactEnvironment
-}
-
-#if DEBUG
-extension ContactsEnvironment {
-  public static let unimplemented = ContactsEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented,
-    contact: { .unimplemented },
-    myContact: { .unimplemented }
-  )
-}
-#endif
-
-public let contactsReducer = Reducer<ContactsState, ContactsAction, ContactsEnvironment>
-{ state, action, env in
-  switch action {
-  case .start:
-    state.myId = try? env.messenger.e2e.tryGet().getContact().getId()
-    return Effect
-      .catching { try env.db() }
-      .flatMap { $0.fetchContactsPublisher(.init()) }
-      .assertNoFailure()
-      .map(ContactsAction.didFetchContacts)
-      .subscribe(on: env.bgQueue)
-      .receive(on: env.mainQueue)
-      .eraseToEffect()
-
-  case .didFetchContacts(var contacts):
-    if let myId = state.myId,
-       let myIndex = contacts.firstIndex(where: { $0.id == myId }) {
-      contacts.move(fromOffsets: [myIndex], toOffset: contacts.startIndex)
-    }
-    state.contacts = IdentifiedArray(uniqueElements: contacts)
-    return .none
-
-  case .contactSelected(let contact):
-    state.contact = ContactState(id: contact.id, dbContact: contact)
-    return .none
-
-  case .contactDismissed:
-    state.contact = nil
-    return .none
-
-  case .myContactSelected:
-    state.myContact = MyContactState()
-    return .none
-
-  case .myContactDismissed:
-    state.myContact = nil
-    return .none
-
-  case .contact(_), .myContact(_):
-    return .none
-  }
-}
-.presenting(
-  contactReducer,
-  state: .keyPath(\.contact),
-  id: .keyPath(\.?.id),
-  action: /ContactsAction.contact,
-  environment: { $0.contact() }
-)
-.presenting(
-  myContactReducer,
-  state: .keyPath(\.myContact),
-  id: .notNil(),
-  action: /ContactsAction.myContact,
-  environment: { $0.myContact() }
-)
diff --git a/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift b/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift
index e09725d92c84feeb7f8cb7da5dbc38cf9d998087..9813bc54bc84cf3191925e3b6db60e7ebefd9629 100644
--- a/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift
+++ b/Examples/xx-messenger/Sources/ContactsFeature/ContactsView.swift
@@ -7,17 +7,17 @@ import SwiftUI
 import XXModels
 
 public struct ContactsView: View {
-  public init(store: Store<ContactsState, ContactsAction>) {
+  public init(store: StoreOf<ContactsComponent>) {
     self.store = store
   }
 
-  let store: Store<ContactsState, ContactsAction>
+  let store: StoreOf<ContactsComponent>
 
   struct ViewState: Equatable {
     var myId: Data?
     var contacts: IdentifiedArrayOf<XXModels.Contact>
 
-    init(state: ContactsState) {
+    init(state: ContactsComponent.State) {
       myId = state.myId
       contacts = state.contacts
     }
@@ -74,7 +74,7 @@ public struct ContactsView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.contact,
-          action: ContactsAction.contact
+          action: ContactsComponent.Action.contact
         ),
         onDeactivate: { viewStore.send(.contactDismissed) },
         destination: ContactView.init(store:)
@@ -82,7 +82,7 @@ public struct ContactsView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.myContact,
-          action: ContactsAction.myContact
+          action: ContactsComponent.Action.myContact
         ),
         onDeactivate: { viewStore.send(.myContactDismissed) },
         destination: MyContactView.init(store:)
@@ -96,7 +96,7 @@ public struct ContactsView_Previews: PreviewProvider {
   public static var previews: some View {
     NavigationView {
       ContactsView(store: Store(
-        initialState: ContactsState(
+        initialState: ContactsComponent.State(
           contacts: [
             .init(
               id: "1".data(using: .utf8)!,
@@ -115,8 +115,7 @@ public struct ContactsView_Previews: PreviewProvider {
             ),
           ]
         ),
-        reducer: .empty,
-        environment: ()
+        reducer: EmptyReducer()
       ))
     }
   }
diff --git a/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift
index 3b9b9a3d00ee09ec4dd23ddc24fec105f94946fe..36b604362e0f07c67b0a1e626a519241f692271c 100644
--- a/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift
+++ b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift
@@ -1,8 +1,8 @@
 import ComposableArchitecture
 
 extension AlertState {
-  public static func confirmAccountDeletion() -> AlertState<HomeAction> {
-    AlertState<HomeAction>(
+  public static func confirmAccountDeletion() -> AlertState<HomeComponent.Action> {
+    AlertState<HomeComponent.Action>(
       title: TextState("Delete Account"),
       message: TextState("This will permanently delete your account and can't be undone."),
       buttons: [
@@ -12,8 +12,8 @@ extension AlertState {
     )
   }
 
-  public static func accountDeletionFailed(_ error: Error) -> AlertState<HomeAction> {
-    AlertState<HomeAction>(
+  public static func accountDeletionFailed(_ error: Error) -> AlertState<HomeComponent.Action> {
+    AlertState<HomeComponent.Action>(
       title: TextState("Error"),
       message: TextState(error.localizedDescription),
       buttons: []
diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeComponent.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..18212bf0ca916cffff86f5a4ee6cb09f4d220359
--- /dev/null
+++ b/Examples/xx-messenger/Sources/HomeFeature/HomeComponent.swift
@@ -0,0 +1,292 @@
+import AppCore
+import BackupFeature
+import Combine
+import ComposableArchitecture
+import ComposablePresentation
+import ContactsFeature
+import Foundation
+import RegisterFeature
+import UserSearchFeature
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct HomeComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public init(
+      failure: String? = nil,
+      isNetworkHealthy: Bool? = nil,
+      networkNodesReport: NodeRegistrationReport? = nil,
+      isDeletingAccount: Bool = false,
+      alert: AlertState<Action>? = nil,
+      register: RegisterComponent.State? = nil,
+      contacts: ContactsComponent.State? = nil,
+      userSearch: UserSearchComponent.State? = nil,
+      backup: BackupComponent.State? = nil
+    ) {
+      self.failure = failure
+      self.isNetworkHealthy = isNetworkHealthy
+      self.isDeletingAccount = isDeletingAccount
+      self.alert = alert
+      self.register = register
+      self.contacts = contacts
+      self.userSearch = userSearch
+      self.backup = backup
+    }
+
+    public var failure: String?
+    public var isNetworkHealthy: Bool?
+    public var networkNodesReport: NodeRegistrationReport?
+    public var isDeletingAccount: Bool
+    public var alert: AlertState<Action>?
+    public var register: RegisterComponent.State?
+    public var contacts: ContactsComponent.State?
+    public var userSearch: UserSearchComponent.State?
+    public var backup: BackupComponent.State?
+  }
+
+  public enum Action: Equatable {
+    public enum Messenger: Equatable {
+      case start
+      case didStartRegistered
+      case didStartUnregistered
+      case failure(NSError)
+    }
+
+    public enum NetworkMonitor: Equatable {
+      case start
+      case stop
+      case health(Bool)
+      case nodes(NodeRegistrationReport)
+    }
+
+    public enum DeleteAccount: Equatable {
+      case buttonTapped
+      case confirmed
+      case success
+      case failure(NSError)
+    }
+
+    case messenger(Messenger)
+    case networkMonitor(NetworkMonitor)
+    case deleteAccount(DeleteAccount)
+    case didDismissAlert
+    case didDismissRegister
+    case userSearchButtonTapped
+    case didDismissUserSearch
+    case contactsButtonTapped
+    case didDismissContacts
+    case backupButtonTapped
+    case didDismissBackup
+    case register(RegisterComponent.Action)
+    case contacts(ContactsComponent.Action)
+    case userSearch(UserSearchComponent.Action)
+    case backup(BackupComponent.Action)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager) var dbManager: DBManager
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    Reduce { state, action in
+      enum NetworkHealthEffectId {}
+      enum NetworkNodesEffectId {}
+
+      let messenger = self.messenger
+
+      switch action {
+      case .messenger(.start):
+        return .merge(
+          Effect(value: .networkMonitor(.stop)),
+          Effect.result {
+            do {
+              try messenger.start()
+
+              if messenger.isConnected() == false {
+                try messenger.connect()
+              }
+
+              if messenger.isListeningForMessages() == false {
+                try messenger.listenForMessages()
+              }
+
+              if messenger.isFileTransferRunning() == false {
+                try messenger.startFileTransfer()
+              }
+
+              if messenger.isLoggedIn() == false {
+                if try messenger.isRegistered() == false {
+                  return .success(.messenger(.didStartUnregistered))
+                }
+                try messenger.logIn()
+              }
+
+              if !messenger.isBackupRunning() {
+                try? messenger.resumeBackup()
+              }
+
+              return .success(.messenger(.didStartRegistered))
+            } catch {
+              return .success(.messenger(.failure(error as NSError)))
+            }
+          }
+        )
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .messenger(.didStartUnregistered):
+        state.register = RegisterComponent.State()
+        return .none
+
+      case .messenger(.didStartRegistered):
+        return Effect(value: .networkMonitor(.start))
+
+      case .messenger(.failure(let error)):
+        state.failure = error.localizedDescription
+        return .none
+
+      case .networkMonitor(.start):
+        return .merge(
+          Effect.run { subscriber in
+            let callback = HealthCallback { isHealthy in
+              subscriber.send(.networkMonitor(.health(isHealthy)))
+            }
+            let cancellable = messenger.cMix()?.addHealthCallback(callback)
+            return AnyCancellable { cancellable?.cancel() }
+          }
+            .cancellable(id: NetworkHealthEffectId.self, cancelInFlight: true),
+          Effect.timer(
+            id: NetworkNodesEffectId.self,
+            every: .seconds(2),
+            on: bgQueue
+          )
+          .compactMap { _ in try? messenger.cMix()?.getNodeRegistrationStatus() }
+            .map { Action.networkMonitor(.nodes($0)) }
+            .eraseToEffect()
+        )
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .networkMonitor(.stop):
+        state.isNetworkHealthy = nil
+        state.networkNodesReport = nil
+        return .merge(
+          .cancel(id: NetworkHealthEffectId.self),
+          .cancel(id: NetworkNodesEffectId.self)
+        )
+
+      case .networkMonitor(.health(let isHealthy)):
+        state.isNetworkHealthy = isHealthy
+        return .none
+
+      case .networkMonitor(.nodes(let report)):
+        state.networkNodesReport = report
+        return .none
+
+      case .deleteAccount(.buttonTapped):
+        state.alert = .confirmAccountDeletion()
+        return .none
+
+      case .deleteAccount(.confirmed):
+        state.isDeletingAccount = true
+        return .result {
+          do {
+            let contactId = try messenger.e2e.tryGet().getContact().getId()
+            let contact = try dbManager.getDB().fetchContacts(.init(id: [contactId])).first
+            if let username = contact?.username {
+              let ud = try messenger.ud.tryGet()
+              try ud.permanentDeleteAccount(username: Fact(type: .username, value: username))
+            }
+            try messenger.destroy()
+            try dbManager.removeDB()
+            return .success(.deleteAccount(.success))
+          } catch {
+            return .success(.deleteAccount(.failure(error as NSError)))
+          }
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .deleteAccount(.success):
+        state.isDeletingAccount = false
+        return .none
+
+      case .deleteAccount(.failure(let error)):
+        state.isDeletingAccount = false
+        state.alert = .accountDeletionFailed(error)
+        return .none
+
+      case .didDismissAlert:
+        state.alert = nil
+        return .none
+
+      case .didDismissRegister:
+        state.register = nil
+        return .none
+
+      case .userSearchButtonTapped:
+        state.userSearch = UserSearchComponent.State()
+        return .none
+
+      case .didDismissUserSearch:
+        state.userSearch = nil
+        return .none
+
+      case .contactsButtonTapped:
+        state.contacts = ContactsComponent.State()
+        return .none
+
+      case .didDismissContacts:
+        state.contacts = nil
+        return .none
+
+      case .register(.finished):
+        state.register = nil
+        return Effect(value: .messenger(.start))
+
+      case .backupButtonTapped:
+        state.backup = BackupComponent.State()
+        return .none
+
+      case .didDismissBackup:
+        state.backup = nil
+        return .none
+
+      case .register(_), .contacts(_), .userSearch(_), .backup(_):
+        return .none
+      }
+    }
+    .presenting(
+      state: .keyPath(\.register),
+      id: .notNil(),
+      action: /Action.register,
+      presented: { RegisterComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.contacts),
+      id: .notNil(),
+      action: /Action.contacts,
+      presented: { ContactsComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.userSearch),
+      id: .notNil(),
+      action: /Action.userSearch,
+      presented: { UserSearchComponent() }
+    )
+    .presenting(
+      state: .keyPath(\.backup),
+      id: .notNil(),
+      action: /Action.backup,
+      presented: { BackupComponent() }
+    )
+  }
+}
diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift
deleted file mode 100644
index 6433dd330faac74045cad472798cd8c06a79cb40..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift
+++ /dev/null
@@ -1,330 +0,0 @@
-import AppCore
-import BackupFeature
-import Combine
-import ComposableArchitecture
-import ComposablePresentation
-import ContactsFeature
-import Foundation
-import RegisterFeature
-import UserSearchFeature
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct HomeState: Equatable {
-  public init(
-    failure: String? = nil,
-    isNetworkHealthy: Bool? = nil,
-    networkNodesReport: NodeRegistrationReport? = nil,
-    isDeletingAccount: Bool = false,
-    alert: AlertState<HomeAction>? = nil,
-    register: RegisterState? = nil,
-    contacts: ContactsState? = nil,
-    userSearch: UserSearchState? = nil,
-    backup: BackupState? = nil
-  ) {
-    self.failure = failure
-    self.isNetworkHealthy = isNetworkHealthy
-    self.isDeletingAccount = isDeletingAccount
-    self.alert = alert
-    self.register = register
-    self.contacts = contacts
-    self.userSearch = userSearch
-    self.backup = backup
-  }
-
-  public var failure: String?
-  public var isNetworkHealthy: Bool?
-  public var networkNodesReport: NodeRegistrationReport?
-  public var isDeletingAccount: Bool
-  public var alert: AlertState<HomeAction>?
-  public var register: RegisterState?
-  public var contacts: ContactsState?
-  public var userSearch: UserSearchState?
-  public var backup: BackupState?
-}
-
-public enum HomeAction: Equatable {
-  public enum Messenger: Equatable {
-    case start
-    case didStartRegistered
-    case didStartUnregistered
-    case failure(NSError)
-  }
-
-  public enum NetworkMonitor: Equatable {
-    case start
-    case stop
-    case health(Bool)
-    case nodes(NodeRegistrationReport)
-  }
-
-  public enum DeleteAccount: Equatable {
-    case buttonTapped
-    case confirmed
-    case success
-    case failure(NSError)
-  }
-
-  case messenger(Messenger)
-  case networkMonitor(NetworkMonitor)
-  case deleteAccount(DeleteAccount)
-  case didDismissAlert
-  case didDismissRegister
-  case userSearchButtonTapped
-  case didDismissUserSearch
-  case contactsButtonTapped
-  case didDismissContacts
-  case backupButtonTapped
-  case didDismissBackup
-  case register(RegisterAction)
-  case contacts(ContactsAction)
-  case userSearch(UserSearchAction)
-  case backup(BackupAction)
-}
-
-public struct HomeEnvironment {
-  public init(
-    messenger: Messenger,
-    dbManager: DBManager,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>,
-    register: @escaping () -> RegisterEnvironment,
-    contacts: @escaping () -> ContactsEnvironment,
-    userSearch: @escaping () -> UserSearchEnvironment,
-    backup: @escaping () -> BackupEnvironment
-  ) {
-    self.messenger = messenger
-    self.dbManager = dbManager
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-    self.register = register
-    self.contacts = contacts
-    self.userSearch = userSearch
-    self.backup = backup
-  }
-
-  public var messenger: Messenger
-  public var dbManager: DBManager
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-  public var register: () -> RegisterEnvironment
-  public var contacts: () -> ContactsEnvironment
-  public var userSearch: () -> UserSearchEnvironment
-  public var backup: () -> BackupEnvironment
-}
-
-#if DEBUG
-extension HomeEnvironment {
-  public static let unimplemented = HomeEnvironment(
-    messenger: .unimplemented,
-    dbManager: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented,
-    register: { .unimplemented },
-    contacts: { .unimplemented },
-    userSearch: { .unimplemented },
-    backup: { .unimplemented }
-  )
-}
-#endif
-
-public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
-{ state, action, env in
-  enum NetworkHealthEffectId {}
-  enum NetworkNodesEffectId {}
-
-  switch action {
-  case .messenger(.start):
-    return .merge(
-      Effect(value: .networkMonitor(.stop)),
-      Effect.result {
-        do {
-          try env.messenger.start()
-
-          if env.messenger.isConnected() == false {
-            try env.messenger.connect()
-          }
-
-          if env.messenger.isListeningForMessages() == false {
-            try env.messenger.listenForMessages()
-          }
-
-          if env.messenger.isFileTransferRunning() == false {
-            try env.messenger.startFileTransfer()
-          }
-
-          if env.messenger.isLoggedIn() == false {
-            if try env.messenger.isRegistered() == false {
-              return .success(.messenger(.didStartUnregistered))
-            }
-            try env.messenger.logIn()
-          }
-
-          if !env.messenger.isBackupRunning() {
-            try? env.messenger.resumeBackup()
-          }
-
-          return .success(.messenger(.didStartRegistered))
-        } catch {
-          return .success(.messenger(.failure(error as NSError)))
-        }
-      }
-    )
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .messenger(.didStartUnregistered):
-    state.register = RegisterState()
-    return .none
-
-  case .messenger(.didStartRegistered):
-    return Effect(value: .networkMonitor(.start))
-
-  case .messenger(.failure(let error)):
-    state.failure = error.localizedDescription
-    return .none
-
-  case .networkMonitor(.start):
-    return .merge(
-      Effect.run { subscriber in
-        let callback = HealthCallback { isHealthy in
-          subscriber.send(.networkMonitor(.health(isHealthy)))
-        }
-        let cancellable = env.messenger.cMix()?.addHealthCallback(callback)
-        return AnyCancellable { cancellable?.cancel() }
-      }
-        .cancellable(id: NetworkHealthEffectId.self, cancelInFlight: true),
-      Effect.timer(
-        id: NetworkNodesEffectId.self,
-        every: .seconds(2),
-        on: env.bgQueue
-      )
-      .compactMap { _ in try? env.messenger.cMix()?.getNodeRegistrationStatus() }
-        .map { HomeAction.networkMonitor(.nodes($0)) }
-        .eraseToEffect()
-    )
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .networkMonitor(.stop):
-    state.isNetworkHealthy = nil
-    state.networkNodesReport = nil
-    return .merge(
-      .cancel(id: NetworkHealthEffectId.self),
-      .cancel(id: NetworkNodesEffectId.self)
-    )
-
-  case .networkMonitor(.health(let isHealthy)):
-    state.isNetworkHealthy = isHealthy
-    return .none
-
-  case .networkMonitor(.nodes(let report)):
-    state.networkNodesReport = report
-    return .none
-
-  case .deleteAccount(.buttonTapped):
-    state.alert = .confirmAccountDeletion()
-    return .none
-
-  case .deleteAccount(.confirmed):
-    state.isDeletingAccount = true
-    return .result {
-      do {
-        let contactId = try env.messenger.e2e.tryGet().getContact().getId()
-        let contact = try env.dbManager.getDB().fetchContacts(.init(id: [contactId])).first
-        if let username = contact?.username {
-          let ud = try env.messenger.ud.tryGet()
-          try ud.permanentDeleteAccount(username: Fact(type: .username, value: username))
-        }
-        try env.messenger.destroy()
-        try env.dbManager.removeDB()
-        return .success(.deleteAccount(.success))
-      } catch {
-        return .success(.deleteAccount(.failure(error as NSError)))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .deleteAccount(.success):
-    state.isDeletingAccount = false
-    return .none
-
-  case .deleteAccount(.failure(let error)):
-    state.isDeletingAccount = false
-    state.alert = .accountDeletionFailed(error)
-    return .none
-
-  case .didDismissAlert:
-    state.alert = nil
-    return .none
-
-  case .didDismissRegister:
-    state.register = nil
-    return .none
-
-  case .userSearchButtonTapped:
-    state.userSearch = UserSearchState()
-    return .none
-
-  case .didDismissUserSearch:
-    state.userSearch = nil
-    return .none
-
-  case .contactsButtonTapped:
-    state.contacts = ContactsState()
-    return .none
-
-  case .didDismissContacts:
-    state.contacts = nil
-    return .none
-
-  case .register(.finished):
-    state.register = nil
-    return Effect(value: .messenger(.start))
-
-  case .backupButtonTapped:
-    state.backup = BackupState()
-    return .none
-
-  case .didDismissBackup:
-    state.backup = nil
-    return .none
-
-  case .register(_), .contacts(_), .userSearch(_), .backup(_):
-    return .none
-  }
-}
-.presenting(
-  registerReducer,
-  state: .keyPath(\.register),
-  id: .notNil(),
-  action: /HomeAction.register,
-  environment: { $0.register() }
-)
-.presenting(
-  contactsReducer,
-  state: .keyPath(\.contacts),
-  id: .notNil(),
-  action: /HomeAction.contacts,
-  environment: { $0.contacts() }
-)
-.presenting(
-  userSearchReducer,
-  state: .keyPath(\.userSearch),
-  id: .notNil(),
-  action: /HomeAction.userSearch,
-  environment: { $0.userSearch() }
-)
-.presenting(
-  backupReducer,
-  state: .keyPath(\.backup),
-  id: .notNil(),
-  action: /HomeAction.backup,
-  environment: { $0.backup() }
-)
diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift
index 8a1775d6a84ffdfc6b5ac1b2b816a8ee686b039c..d505100474cdd330d28afc81e126bbb0daf853b0 100644
--- a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift
+++ b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift
@@ -9,11 +9,11 @@ import UserSearchFeature
 import XXClient
 
 public struct HomeView: View {
-  public init(store: Store<HomeState, HomeAction>) {
+  public init(store: StoreOf<HomeComponent>) {
     self.store = store
   }
 
-  let store: Store<HomeState, HomeAction>
+  let store: StoreOf<HomeComponent>
 
   struct ViewState: Equatable {
     var failure: String?
@@ -21,7 +21,7 @@ public struct HomeView: View {
     var networkNodesReport: NodeRegistrationReport?
     var isDeletingAccount: Bool
 
-    init(state: HomeState) {
+    init(state: HomeComponent.State) {
       failure = state.failure
       isNetworkHealthy = state.isNetworkHealthy
       isDeletingAccount = state.isDeletingAccount
@@ -148,12 +148,12 @@ public struct HomeView: View {
         .navigationTitle("Home")
         .alert(
           store.scope(state: \.alert),
-          dismiss: HomeAction.didDismissAlert
+          dismiss: HomeComponent.Action.didDismissAlert
         )
         .background(NavigationLinkWithStore(
           store.scope(
             state: \.contacts,
-            action: HomeAction.contacts
+            action: HomeComponent.Action.contacts
           ),
           onDeactivate: {
             viewStore.send(.didDismissContacts)
@@ -163,7 +163,7 @@ public struct HomeView: View {
         .background(NavigationLinkWithStore(
           store.scope(
             state: \.userSearch,
-            action: HomeAction.userSearch
+            action: HomeComponent.Action.userSearch
           ),
           onDeactivate: {
             viewStore.send(.didDismissUserSearch)
@@ -173,7 +173,7 @@ public struct HomeView: View {
         .background(NavigationLinkWithStore(
           store.scope(
             state: \.backup,
-            action: HomeAction.backup
+            action: HomeComponent.Action.backup
           ),
           onDeactivate: {
             viewStore.send(.didDismissBackup)
@@ -186,7 +186,7 @@ public struct HomeView: View {
       .fullScreenCover(
         store.scope(
           state: \.register,
-          action: HomeAction.register
+          action: HomeComponent.Action.register
         ),
         onDismiss: {
           viewStore.send(.didDismissRegister)
@@ -201,9 +201,8 @@ public struct HomeView: View {
 public struct HomeView_Previews: PreviewProvider {
   public static var previews: some View {
     HomeView(store: Store(
-      initialState: HomeState(),
-      reducer: .empty,
-      environment: ()
+      initialState: HomeComponent.State(),
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift b/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift
index 321139aec18ce7ae51b9be8097878a9133798236..f8693f17cc18c8f13c4412b1f30c8aac1a3c3d62 100644
--- a/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift
+++ b/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift
@@ -1,8 +1,8 @@
 import ComposableArchitecture
 
 extension AlertState {
-  public static func error(_ message: String) -> AlertState<MyContactAction> {
-    AlertState<MyContactAction>(
+  public static func error(_ message: String) -> AlertState<MyContactComponent.Action> {
+    AlertState<MyContactComponent.Action>(
       title: TextState("Error"),
       message: TextState(message),
       buttons: []
diff --git a/Examples/xx-messenger/Sources/MyContactFeature/MyContactComponent.swift b/Examples/xx-messenger/Sources/MyContactFeature/MyContactComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..377e824b807d50e056bfd5b4826585e8fde6f6b9
--- /dev/null
+++ b/Examples/xx-messenger/Sources/MyContactFeature/MyContactComponent.swift
@@ -0,0 +1,297 @@
+import AppCore
+import Combine
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct MyContactComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Field: String, Hashable {
+      case email
+      case emailCode
+      case phone
+      case phoneCode
+    }
+
+    public init(
+      contact: XXModels.Contact? = nil,
+      focusedField: Field? = nil,
+      email: String = "",
+      emailConfirmationID: String? = nil,
+      emailConfirmationCode: String = "",
+      isRegisteringEmail: Bool = false,
+      isConfirmingEmail: Bool = false,
+      isUnregisteringEmail: Bool = false,
+      phone: String = "",
+      phoneConfirmationID: String? = nil,
+      phoneConfirmationCode: String = "",
+      isRegisteringPhone: Bool = false,
+      isConfirmingPhone: Bool = false,
+      isUnregisteringPhone: Bool = false,
+      isLoadingFacts: Bool = false,
+      alert: AlertState<Action>? = nil
+    ) {
+      self.contact = contact
+      self.focusedField = focusedField
+      self.email = email
+      self.emailConfirmationID = emailConfirmationID
+      self.emailConfirmationCode = emailConfirmationCode
+      self.isRegisteringEmail = isRegisteringEmail
+      self.isConfirmingEmail = isConfirmingEmail
+      self.isUnregisteringEmail = isUnregisteringEmail
+      self.phone = phone
+      self.phoneConfirmationID = phoneConfirmationID
+      self.phoneConfirmationCode = phoneConfirmationCode
+      self.isRegisteringPhone = isRegisteringPhone
+      self.isConfirmingPhone = isConfirmingPhone
+      self.isUnregisteringPhone = isUnregisteringPhone
+      self.isLoadingFacts = isLoadingFacts
+      self.alert = alert
+    }
+
+    public var contact: XXModels.Contact?
+    @BindableState public var focusedField: Field?
+    @BindableState public var email: String
+    @BindableState public var emailConfirmationID: String?
+    @BindableState public var emailConfirmationCode: String
+    @BindableState public var isRegisteringEmail: Bool
+    @BindableState public var isConfirmingEmail: Bool
+    @BindableState public var isUnregisteringEmail: Bool
+    @BindableState public var phone: String
+    @BindableState public var phoneConfirmationID: String?
+    @BindableState public var phoneConfirmationCode: String
+    @BindableState public var isRegisteringPhone: Bool
+    @BindableState public var isConfirmingPhone: Bool
+    @BindableState public var isUnregisteringPhone: Bool
+    @BindableState public var isLoadingFacts: Bool
+    public var alert: AlertState<Action>?
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case start
+    case contactFetched(XXModels.Contact?)
+    case registerEmailTapped
+    case confirmEmailTapped
+    case unregisterEmailTapped
+    case registerPhoneTapped
+    case confirmPhoneTapped
+    case unregisterPhoneTapped
+    case loadFactsTapped
+    case didFail(String)
+    case alertDismissed
+    case binding(BindingAction<State>)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      enum DBFetchEffectID {}
+      let db = self.db
+
+      switch action {
+      case .start:
+        return Effect
+          .catching { try messenger.e2e.tryGet().getContact().getId() }
+          .tryMap { try db().fetchContactsPublisher(.init(id: [$0])) }
+          .flatMap { $0 }
+          .assertNoFailure()
+          .map(\.first)
+          .map(Action.contactFetched)
+          .subscribe(on: bgQueue)
+          .receive(on: mainQueue)
+          .eraseToEffect()
+          .cancellable(id: DBFetchEffectID.self, cancelInFlight: true)
+
+      case .contactFetched(let contact):
+        state.contact = contact
+        return .none
+
+      case .registerEmailTapped:
+        state.focusedField = nil
+        state.isRegisteringEmail = true
+        return Effect.run { [state] subscriber in
+          do {
+            let ud = try messenger.ud.tryGet()
+            let fact = Fact(type: .email, value: state.email)
+            let confirmationID = try ud.sendRegisterFact(fact)
+            subscriber.send(.set(\.$emailConfirmationID, confirmationID))
+          } catch {
+            subscriber.send(.didFail(error.localizedDescription))
+          }
+          subscriber.send(.set(\.$isRegisteringEmail, false))
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .confirmEmailTapped:
+        guard let confirmationID = state.emailConfirmationID else { return .none }
+        state.focusedField = nil
+        state.isConfirmingEmail = true
+        return Effect.run { [state] subscriber in
+          do {
+            let ud = try messenger.ud.tryGet()
+            try ud.confirmFact(confirmationId: confirmationID, code: state.emailConfirmationCode)
+            let contactId = try messenger.e2e.tryGet().getContact().getId()
+            if var dbContact = try db().fetchContacts(.init(id: [contactId])).first {
+              dbContact.email = state.email
+              try db().saveContact(dbContact)
+            }
+            subscriber.send(.set(\.$email, ""))
+            subscriber.send(.set(\.$emailConfirmationID, nil))
+            subscriber.send(.set(\.$emailConfirmationCode, ""))
+          } catch {
+            subscriber.send(.didFail(error.localizedDescription))
+          }
+          subscriber.send(.set(\.$isConfirmingEmail, false))
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .unregisterEmailTapped:
+        guard let email = state.contact?.email else { return .none }
+        state.isUnregisteringEmail = true
+        return Effect.run { subscriber in
+          do {
+            let ud: UserDiscovery = try messenger.ud.tryGet()
+            let fact = Fact(type: .email, value: email)
+            try ud.removeFact(fact)
+            let contactId = try messenger.e2e.tryGet().getContact().getId()
+            if var dbContact = try db().fetchContacts(.init(id: [contactId])).first {
+              dbContact.email = nil
+              try db().saveContact(dbContact)
+            }
+          } catch {
+            subscriber.send(.didFail(error.localizedDescription))
+          }
+          subscriber.send(.set(\.$isUnregisteringEmail, false))
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .registerPhoneTapped:
+        state.focusedField = nil
+        state.isRegisteringPhone = true
+        return Effect.run { [state] subscriber in
+          do {
+            let ud = try messenger.ud.tryGet()
+            let fact = Fact(type: .phone, value: state.phone)
+            let confirmationID = try ud.sendRegisterFact(fact)
+            subscriber.send(.set(\.$phoneConfirmationID, confirmationID))
+          } catch {
+            subscriber.send(.didFail(error.localizedDescription))
+          }
+          subscriber.send(.set(\.$isRegisteringPhone, false))
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .confirmPhoneTapped:
+        guard let confirmationID = state.phoneConfirmationID else { return .none }
+        state.focusedField = nil
+        state.isConfirmingPhone = true
+        return Effect.run { [state] subscriber in
+          do {
+            let ud = try messenger.ud.tryGet()
+            try ud.confirmFact(confirmationId: confirmationID, code: state.phoneConfirmationCode)
+            let contactId = try messenger.e2e.tryGet().getContact().getId()
+            if var dbContact = try db().fetchContacts(.init(id: [contactId])).first {
+              dbContact.phone = state.phone
+              try db().saveContact(dbContact)
+            }
+            subscriber.send(.set(\.$phone, ""))
+            subscriber.send(.set(\.$phoneConfirmationID, nil))
+            subscriber.send(.set(\.$phoneConfirmationCode, ""))
+          } catch {
+            subscriber.send(.didFail(error.localizedDescription))
+          }
+          subscriber.send(.set(\.$isConfirmingPhone, false))
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .unregisterPhoneTapped:
+        guard let phone = state.contact?.phone else { return .none }
+        state.isUnregisteringPhone = true
+        return Effect.run { subscriber in
+          do {
+            let ud: UserDiscovery = try messenger.ud.tryGet()
+            let fact = Fact(type: .phone, value: phone)
+            try ud.removeFact(fact)
+            let contactId = try messenger.e2e.tryGet().getContact().getId()
+            if var dbContact = try db().fetchContacts(.init(id: [contactId])).first {
+              dbContact.phone = nil
+              try db().saveContact(dbContact)
+            }
+          } catch {
+            subscriber.send(.didFail(error.localizedDescription))
+          }
+          subscriber.send(.set(\.$isUnregisteringPhone, false))
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .loadFactsTapped:
+        state.isLoadingFacts = true
+        return Effect.run { subscriber in
+          do {
+            let contactId = try messenger.e2e.tryGet().getContact().getId()
+            if var dbContact = try db().fetchContacts(.init(id: [contactId])).first {
+              let facts = try messenger.ud.tryGet().getFacts()
+              dbContact.username = facts.get(.username)?.value
+              dbContact.email = facts.get(.email)?.value
+              dbContact.phone = facts.get(.phone)?.value
+              try db().saveContact(dbContact)
+            }
+          } catch {
+            subscriber.send(.didFail(error.localizedDescription))
+          }
+          subscriber.send(.set(\.$isLoadingFacts, false))
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .didFail(let failure):
+        state.alert = .error(failure)
+        return .none
+
+      case .alertDismissed:
+        state.alert = nil
+        return .none
+
+      case .binding(_):
+        return .none
+      }
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/MyContactFeature/MyContactFeature.swift b/Examples/xx-messenger/Sources/MyContactFeature/MyContactFeature.swift
deleted file mode 100644
index 434a1aca3c7cdc74b340c54a22558fa67a90a0eb..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/MyContactFeature/MyContactFeature.swift
+++ /dev/null
@@ -1,316 +0,0 @@
-import AppCore
-import Combine
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct MyContactState: Equatable {
-  public enum Field: String, Hashable {
-    case email
-    case emailCode
-    case phone
-    case phoneCode
-  }
-
-  public init(
-    contact: XXModels.Contact? = nil,
-    focusedField: Field? = nil,
-    email: String = "",
-    emailConfirmationID: String? = nil,
-    emailConfirmationCode: String = "",
-    isRegisteringEmail: Bool = false,
-    isConfirmingEmail: Bool = false,
-    isUnregisteringEmail: Bool = false,
-    phone: String = "",
-    phoneConfirmationID: String? = nil,
-    phoneConfirmationCode: String = "",
-    isRegisteringPhone: Bool = false,
-    isConfirmingPhone: Bool = false,
-    isUnregisteringPhone: Bool = false,
-    isLoadingFacts: Bool = false,
-    alert: AlertState<MyContactAction>? = nil
-  ) {
-    self.contact = contact
-    self.focusedField = focusedField
-    self.email = email
-    self.emailConfirmationID = emailConfirmationID
-    self.emailConfirmationCode = emailConfirmationCode
-    self.isRegisteringEmail = isRegisteringEmail
-    self.isConfirmingEmail = isConfirmingEmail
-    self.isUnregisteringEmail = isUnregisteringEmail
-    self.phone = phone
-    self.phoneConfirmationID = phoneConfirmationID
-    self.phoneConfirmationCode = phoneConfirmationCode
-    self.isRegisteringPhone = isRegisteringPhone
-    self.isConfirmingPhone = isConfirmingPhone
-    self.isUnregisteringPhone = isUnregisteringPhone
-    self.isLoadingFacts = isLoadingFacts
-    self.alert = alert
-  }
-
-  public var contact: XXModels.Contact?
-  @BindableState public var focusedField: Field?
-  @BindableState public var email: String
-  @BindableState public var emailConfirmationID: String?
-  @BindableState public var emailConfirmationCode: String
-  @BindableState public var isRegisteringEmail: Bool
-  @BindableState public var isConfirmingEmail: Bool
-  @BindableState public var isUnregisteringEmail: Bool
-  @BindableState public var phone: String
-  @BindableState public var phoneConfirmationID: String?
-  @BindableState public var phoneConfirmationCode: String
-  @BindableState public var isRegisteringPhone: Bool
-  @BindableState public var isConfirmingPhone: Bool
-  @BindableState public var isUnregisteringPhone: Bool
-  @BindableState public var isLoadingFacts: Bool
-  public var alert: AlertState<MyContactAction>?
-}
-
-public enum MyContactAction: Equatable, BindableAction {
-  case start
-  case contactFetched(XXModels.Contact?)
-  case registerEmailTapped
-  case confirmEmailTapped
-  case unregisterEmailTapped
-  case registerPhoneTapped
-  case confirmPhoneTapped
-  case unregisterPhoneTapped
-  case loadFactsTapped
-  case didFail(String)
-  case alertDismissed
-  case binding(BindingAction<MyContactState>)
-}
-
-public struct MyContactEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension MyContactEnvironment {
-  public static let unimplemented = MyContactEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let myContactReducer = Reducer<MyContactState, MyContactAction, MyContactEnvironment>
-{ state, action, env in
-  enum DBFetchEffectID {}
-
-  switch action {
-  case .start:
-    return Effect
-      .catching { try env.messenger.e2e.tryGet().getContact().getId() }
-      .tryMap { try env.db().fetchContactsPublisher(.init(id: [$0])) }
-      .flatMap { $0 }
-      .assertNoFailure()
-      .map(\.first)
-      .map(MyContactAction.contactFetched)
-      .subscribe(on: env.bgQueue)
-      .receive(on: env.mainQueue)
-      .eraseToEffect()
-      .cancellable(id: DBFetchEffectID.self, cancelInFlight: true)
-
-  case .contactFetched(let contact):
-    state.contact = contact
-    return .none
-
-  case .registerEmailTapped:
-    state.focusedField = nil
-    state.isRegisteringEmail = true
-    return Effect.run { [state] subscriber in
-      do {
-        let ud = try env.messenger.ud.tryGet()
-        let fact = Fact(type: .email, value: state.email)
-        let confirmationID = try ud.sendRegisterFact(fact)
-        subscriber.send(.set(\.$emailConfirmationID, confirmationID))
-      } catch {
-        subscriber.send(.didFail(error.localizedDescription))
-      }
-      subscriber.send(.set(\.$isRegisteringEmail, false))
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .confirmEmailTapped:
-    guard let confirmationID = state.emailConfirmationID else { return .none }
-    state.focusedField = nil
-    state.isConfirmingEmail = true
-    return Effect.run { [state] subscriber in
-      do {
-        let ud = try env.messenger.ud.tryGet()
-        try ud.confirmFact(confirmationId: confirmationID, code: state.emailConfirmationCode)
-        let contactId = try env.messenger.e2e.tryGet().getContact().getId()
-        if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first {
-          dbContact.email = state.email
-          try env.db().saveContact(dbContact)
-        }
-        subscriber.send(.set(\.$email, ""))
-        subscriber.send(.set(\.$emailConfirmationID, nil))
-        subscriber.send(.set(\.$emailConfirmationCode, ""))
-      } catch {
-        subscriber.send(.didFail(error.localizedDescription))
-      }
-      subscriber.send(.set(\.$isConfirmingEmail, false))
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .unregisterEmailTapped:
-    guard let email = state.contact?.email else { return .none }
-    state.isUnregisteringEmail = true
-    return Effect.run { [state] subscriber in
-      do {
-        let ud: UserDiscovery = try env.messenger.ud.tryGet()
-        let fact = Fact(type: .email, value: email)
-        try ud.removeFact(fact)
-        let contactId = try env.messenger.e2e.tryGet().getContact().getId()
-        if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first {
-          dbContact.email = nil
-          try env.db().saveContact(dbContact)
-        }
-      } catch {
-        subscriber.send(.didFail(error.localizedDescription))
-      }
-      subscriber.send(.set(\.$isUnregisteringEmail, false))
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .registerPhoneTapped:
-    state.focusedField = nil
-    state.isRegisteringPhone = true
-    return Effect.run { [state] subscriber in
-      do {
-        let ud = try env.messenger.ud.tryGet()
-        let fact = Fact(type: .phone, value: state.phone)
-        let confirmationID = try ud.sendRegisterFact(fact)
-        subscriber.send(.set(\.$phoneConfirmationID, confirmationID))
-      } catch {
-        subscriber.send(.didFail(error.localizedDescription))
-      }
-      subscriber.send(.set(\.$isRegisteringPhone, false))
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .confirmPhoneTapped:
-    guard let confirmationID = state.phoneConfirmationID else { return .none }
-    state.focusedField = nil
-    state.isConfirmingPhone = true
-    return Effect.run { [state] subscriber in
-      do {
-        let ud = try env.messenger.ud.tryGet()
-        try ud.confirmFact(confirmationId: confirmationID, code: state.phoneConfirmationCode)
-        let contactId = try env.messenger.e2e.tryGet().getContact().getId()
-        if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first {
-          dbContact.phone = state.phone
-          try env.db().saveContact(dbContact)
-        }
-        subscriber.send(.set(\.$phone, ""))
-        subscriber.send(.set(\.$phoneConfirmationID, nil))
-        subscriber.send(.set(\.$phoneConfirmationCode, ""))
-      } catch {
-        subscriber.send(.didFail(error.localizedDescription))
-      }
-      subscriber.send(.set(\.$isConfirmingPhone, false))
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .unregisterPhoneTapped:
-    guard let phone = state.contact?.phone else { return .none }
-    state.isUnregisteringPhone = true
-    return Effect.run { [state] subscriber in
-      do {
-        let ud: UserDiscovery = try env.messenger.ud.tryGet()
-        let fact = Fact(type: .phone, value: phone)
-        try ud.removeFact(fact)
-        let contactId = try env.messenger.e2e.tryGet().getContact().getId()
-        if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first {
-          dbContact.phone = nil
-          try env.db().saveContact(dbContact)
-        }
-      } catch {
-        subscriber.send(.didFail(error.localizedDescription))
-      }
-      subscriber.send(.set(\.$isUnregisteringPhone, false))
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .loadFactsTapped:
-    state.isLoadingFacts = true
-    return Effect.run { subscriber in
-      do {
-        let contactId = try env.messenger.e2e.tryGet().getContact().getId()
-        if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first {
-          let facts = try env.messenger.ud.tryGet().getFacts()
-          dbContact.username = facts.get(.username)?.value
-          dbContact.email = facts.get(.email)?.value
-          dbContact.phone = facts.get(.phone)?.value
-          try env.db().saveContact(dbContact)
-        }
-      } catch {
-        subscriber.send(.didFail(error.localizedDescription))
-      }
-      subscriber.send(.set(\.$isLoadingFacts, false))
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .didFail(let failure):
-    state.alert = .error(failure)
-    return .none
-
-  case .alertDismissed:
-    state.alert = nil
-    return .none
-
-  case .binding(_):
-    return .none
-  }
-}
-.binding()
diff --git a/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift b/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift
index d32a6f68848e89ad7eeb4a9bf5f25b543d379f40..b5cd127ed53e7037a278e476147a53ae0b42ebc7 100644
--- a/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift
+++ b/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift
@@ -4,15 +4,15 @@ import SwiftUI
 import XXModels
 
 public struct MyContactView: View {
-  public init(store: Store<MyContactState, MyContactAction>) {
+  public init(store: StoreOf<MyContactComponent>) {
     self.store = store
   }
 
-  let store: Store<MyContactState, MyContactAction>
-  @FocusState var focusedField: MyContactState.Field?
+  let store: StoreOf<MyContactComponent>
+  @FocusState var focusedField: MyContactComponent.State.Field?
 
   struct ViewState: Equatable {
-    init(state: MyContactState) {
+    init(state: MyContactComponent.State) {
       contact = state.contact
       focusedField = state.focusedField
       email = state.email
@@ -31,7 +31,7 @@ public struct MyContactView: View {
     }
 
     var contact: XXModels.Contact?
-    var focusedField: MyContactState.Field?
+    var focusedField: MyContactComponent.State.Field?
     var email: String
     var emailConfirmation: Bool
     var emailCode: String
@@ -86,7 +86,7 @@ public struct MyContactView: View {
               TextField(
                 text: viewStore.binding(
                   get: \.email,
-                  send: { MyContactAction.set(\.$email, $0) }
+                  send: { MyContactComponent.Action.set(\.$email, $0) }
                 ),
                 prompt: Text("Enter email"),
                 label: { Text("Email") }
@@ -99,7 +99,7 @@ public struct MyContactView: View {
                 TextField(
                   text: viewStore.binding(
                     get: \.emailCode,
-                    send: { MyContactAction.set(\.$emailConfirmationCode, $0) }
+                    send: { MyContactComponent.Action.set(\.$emailConfirmationCode, $0) }
                   ),
                   prompt: Text("Enter confirmation code"),
                   label: { Text("Confirmation code") }
@@ -163,7 +163,7 @@ public struct MyContactView: View {
               TextField(
                 text: viewStore.binding(
                   get: \.phone,
-                  send: { MyContactAction.set(\.$phone, $0) }
+                  send: { MyContactComponent.Action.set(\.$phone, $0) }
                 ),
                 prompt: Text("Enter phone"),
                 label: { Text("Phone") }
@@ -176,7 +176,7 @@ public struct MyContactView: View {
                 TextField(
                   text: viewStore.binding(
                     get: \.phoneCode,
-                    send: { MyContactAction.set(\.$phoneConfirmationCode, $0) }
+                    send: { MyContactComponent.Action.set(\.$phoneConfirmationCode, $0) }
                   ),
                   prompt: Text("Enter confirmation code"),
                   label: { Text("Confirmation code") }
@@ -250,9 +250,8 @@ public struct MyContactView_Previews: PreviewProvider {
   public static var previews: some View {
     NavigationView {
       MyContactView(store: Store(
-        initialState: MyContactState(),
-        reducer: .empty,
-        environment: ()
+        initialState: MyContactComponent.State(),
+        reducer: EmptyReducer()
       ))
     }
   }
diff --git a/Examples/xx-messenger/Sources/RegisterFeature/RegisterComponent.swift b/Examples/xx-messenger/Sources/RegisterFeature/RegisterComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..062080af6b86f9243ecbf2644bc7df63ecf60a17
--- /dev/null
+++ b/Examples/xx-messenger/Sources/RegisterFeature/RegisterComponent.swift
@@ -0,0 +1,104 @@
+import AppCore
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct RegisterComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Error: Swift.Error, Equatable {
+      case usernameMismatch(registering: String, registered: String?)
+    }
+
+    public enum Field: String, Hashable {
+      case username
+    }
+
+    public init(
+      focusedField: Field? = nil,
+      username: String = "",
+      isRegistering: Bool = false,
+      failure: String? = nil
+    ) {
+      self.focusedField = focusedField
+      self.username = username
+      self.isRegistering = isRegistering
+      self.failure = failure
+    }
+
+    @BindableState public var focusedField: Field?
+    @BindableState public var username: String
+    public var isRegistering: Bool
+    public var failure: String?
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case registerTapped
+    case failed(String)
+    case finished
+    case binding(BindingAction<State>)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.now) var now: () -> Date
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      switch action {
+      case .binding(_):
+        return .none
+
+      case .registerTapped:
+        state.focusedField = nil
+        state.isRegistering = true
+        state.failure = nil
+        return .future { [username = state.username] fulfill in
+          do {
+            let db = try db()
+            try messenger.register(username: username)
+            let contact = try messenger.myContact()
+            let facts = try contact.getFacts()
+            try db.saveContact(Contact(
+              id: try contact.getId(),
+              marshaled: contact.data,
+              username: facts.get(.username)?.value,
+              email: facts.get(.email)?.value,
+              phone: facts.get(.phone)?.value,
+              createdAt: now()
+            ))
+            guard facts.get(.username)?.value == username else {
+              throw State.Error.usernameMismatch(
+                registering: username,
+                registered: facts.get(.username)?.value
+              )
+            }
+            fulfill(.success(.finished))
+          }
+          catch {
+            fulfill(.success(.failed(error.localizedDescription)))
+          }
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .failed(let failure):
+        state.isRegistering = false
+        state.failure = failure
+        return .none
+
+      case .finished:
+        state.isRegistering = false
+        return .none
+      }
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift b/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift
deleted file mode 100644
index f8fdabefea7d859b3153c6716133d3351bff2fdd..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/RegisterFeature/RegisterFeature.swift
+++ /dev/null
@@ -1,127 +0,0 @@
-import AppCore
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct RegisterState: Equatable {
-  public enum Error: Swift.Error, Equatable {
-    case usernameMismatch(registering: String, registered: String?)
-  }
-
-  public enum Field: String, Hashable {
-    case username
-  }
-
-  public init(
-    focusedField: Field? = nil,
-    username: String = "",
-    isRegistering: Bool = false,
-    failure: String? = nil
-  ) {
-    self.focusedField = focusedField
-    self.username = username
-    self.isRegistering = isRegistering
-    self.failure = failure
-  }
-
-  @BindableState public var focusedField: Field?
-  @BindableState public var username: String
-  public var isRegistering: Bool
-  public var failure: String?
-}
-
-public enum RegisterAction: Equatable, BindableAction {
-  case registerTapped
-  case failed(String)
-  case finished
-  case binding(BindingAction<RegisterState>)
-}
-
-public struct RegisterEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    now: @escaping () -> Date,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.now = now
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var now: () -> Date
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension RegisterEnvironment {
-  public static let unimplemented = RegisterEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    now: XCTUnimplemented("\(Self.self).now"),
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let registerReducer = Reducer<RegisterState, RegisterAction, RegisterEnvironment>
-{ state, action, env in
-  switch action {
-  case .binding(_):
-    return .none
-
-  case .registerTapped:
-    state.focusedField = nil
-    state.isRegistering = true
-    state.failure = nil
-    return .future { [username = state.username] fulfill in
-      do {
-        let db = try env.db()
-        try env.messenger.register(username: username)
-        let contact = try env.messenger.myContact()
-        let facts = try contact.getFacts()
-        try db.saveContact(Contact(
-          id: try contact.getId(),
-          marshaled: contact.data,
-          username: facts.get(.username)?.value,
-          email: facts.get(.email)?.value,
-          phone: facts.get(.phone)?.value,
-          createdAt: env.now()
-        ))
-        guard facts.get(.username)?.value == username else {
-          throw RegisterState.Error.usernameMismatch(
-            registering: username,
-            registered: facts.get(.username)?.value
-          )
-        }
-        fulfill(.success(.finished))
-      }
-      catch {
-        fulfill(.success(.failed(error.localizedDescription)))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .failed(let failure):
-    state.isRegistering = false
-    state.failure = failure
-    return .none
-
-  case .finished:
-    state.isRegistering = false
-    return .none
-  }
-}
-.binding()
diff --git a/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift b/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift
index 4b168c2765f7a2f67afad164362807e0daa5a819..a41e16ab9648176d3efb5eda5f616e66f9ae05cf 100644
--- a/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift
+++ b/Examples/xx-messenger/Sources/RegisterFeature/RegisterView.swift
@@ -2,22 +2,22 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct RegisterView: View {
-  public init(store: Store<RegisterState, RegisterAction>) {
+  public init(store: StoreOf<RegisterComponent>) {
     self.store = store
   }
 
-  let store: Store<RegisterState, RegisterAction>
-  @FocusState var focusedField: RegisterState.Field?
+  let store: StoreOf<RegisterComponent>
+  @FocusState var focusedField: RegisterComponent.State.Field?
 
   struct ViewState: Equatable {
-    init(_ state: RegisterState) {
+    init(_ state: RegisterComponent.State) {
       focusedField = state.focusedField
       username = state.username
       isRegistering = state.isRegistering
       failure = state.failure
     }
 
-    var focusedField: RegisterState.Field?
+    var focusedField: RegisterComponent.State.Field?
     var username: String
     var isRegistering: Bool
     var failure: String?
@@ -31,7 +31,7 @@ public struct RegisterView: View {
             TextField(
               text: viewStore.binding(
                 get: \.username,
-                send: { RegisterAction.set(\.$username, $0) }
+                send: { RegisterComponent.Action.set(\.$username, $0) }
               ),
               prompt: Text("Enter username"),
               label: { Text("Username") }
@@ -78,9 +78,8 @@ public struct RegisterView: View {
 public struct RegisterView_Previews: PreviewProvider {
   public static var previews: some View {
     RegisterView(store: Store(
-      initialState: RegisterState(),
-      reducer: .empty,
-      environment: ()
+      initialState: RegisterComponent.State(),
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthComponent.swift b/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..629c150dc271792e1b1b88bc9733a364955a8985
--- /dev/null
+++ b/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthComponent.swift
@@ -0,0 +1,71 @@
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+
+public struct ResetAuthComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public init(
+      partner: Contact,
+      isResetting: Bool = false,
+      failure: String? = nil,
+      didReset: Bool = false
+    ) {
+      self.partner = partner
+      self.isResetting = isResetting
+      self.failure = failure
+      self.didReset = didReset
+    }
+
+    public var partner: Contact
+    public var isResetting: Bool
+    public var failure: String?
+    public var didReset: Bool
+  }
+
+  public enum Action: Equatable {
+    case resetTapped
+    case didReset
+    case didFail(String)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
+    switch action {
+    case .resetTapped:
+      state.isResetting = true
+      state.didReset = false
+      state.failure = nil
+      return Effect.result { [state] in
+        do {
+          let e2e = try messenger.e2e.tryGet()
+          _ = try e2e.resetAuthenticatedChannel(partner: state.partner)
+          return .success(.didReset)
+        } catch {
+          return .success(.didFail(error.localizedDescription))
+        }
+      }
+      .subscribe(on: bgQueue)
+      .receive(on: mainQueue)
+      .eraseToEffect()
+
+    case .didReset:
+      state.isResetting = false
+      state.didReset = true
+      state.failure = nil
+      return .none
+
+    case .didFail(let failure):
+      state.isResetting = false
+      state.didReset = false
+      state.failure = failure
+      return .none
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthFeature.swift b/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthFeature.swift
deleted file mode 100644
index d4acb74002902f9b9a82da2981f78cd3312deb9e..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthFeature.swift
+++ /dev/null
@@ -1,90 +0,0 @@
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-
-public struct ResetAuthState: Equatable {
-  public init(
-    partner: Contact,
-    isResetting: Bool = false,
-    failure: String? = nil,
-    didReset: Bool = false
-  ) {
-    self.partner = partner
-    self.isResetting = isResetting
-    self.failure = failure
-    self.didReset = didReset
-  }
-
-  public var partner: Contact
-  public var isResetting: Bool
-  public var failure: String?
-  public var didReset: Bool
-}
-
-public enum ResetAuthAction: Equatable {
-  case resetTapped
-  case didReset
-  case didFail(String)
-}
-
-public struct ResetAuthEnvironment {
-  public init(
-    messenger: Messenger,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension ResetAuthEnvironment {
-  public static let unimplemented = ResetAuthEnvironment(
-    messenger: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let resetAuthReducer = Reducer<ResetAuthState, ResetAuthAction, ResetAuthEnvironment>
-{ state, action, env in
-  switch action {
-  case .resetTapped:
-    state.isResetting = true
-    state.didReset = false
-    state.failure = nil
-    return Effect.result { [state] in
-      do {
-        let e2e = try env.messenger.e2e.tryGet()
-        _ = try e2e.resetAuthenticatedChannel(partner: state.partner)
-        return .success(.didReset)
-      } catch {
-        return .success(.didFail(error.localizedDescription))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .didReset:
-    state.isResetting = false
-    state.didReset = true
-    state.failure = nil
-    return .none
-
-  case .didFail(let failure):
-    state.isResetting = false
-    state.didReset = false
-    state.failure = failure
-    return .none
-  }
-}
diff --git a/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthView.swift b/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthView.swift
index 7b384efb74d841b4edf04a61cf55aa02a7c0d725..cc15b34a75c475c41c5cedacf0bd11181877791d 100644
--- a/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthView.swift
+++ b/Examples/xx-messenger/Sources/ResetAuthFeature/ResetAuthView.swift
@@ -3,14 +3,14 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct ResetAuthView: View {
-  public init(store: Store<ResetAuthState, ResetAuthAction>) {
+  public init(store: StoreOf<ResetAuthComponent>) {
     self.store = store
   }
 
-  let store: Store<ResetAuthState, ResetAuthAction>
+  let store: StoreOf<ResetAuthComponent>
 
   struct ViewState: Equatable {
-    init(state: ResetAuthState) {
+    init(state: ResetAuthComponent.State) {
       contactID = try? state.partner.getId()
       isResetting = state.isResetting
       failure = state.failure
@@ -68,11 +68,10 @@ public struct ResetAuthView_Previews: PreviewProvider {
   public static var previews: some View {
     NavigationView {
       ResetAuthView(store: Store(
-        initialState: ResetAuthState(
+        initialState: ResetAuthComponent.State(
           partner: .unimplemented(Data())
         ),
-        reducer: .empty,
-        environment: ()
+        reducer: EmptyReducer()
       ))
     }
   }
diff --git a/Examples/xx-messenger/Sources/RestoreFeature/RestoreComponent.swift b/Examples/xx-messenger/Sources/RestoreFeature/RestoreComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5a7ef06de236b757cfc82ebbab07b1829ba45822
--- /dev/null
+++ b/Examples/xx-messenger/Sources/RestoreFeature/RestoreComponent.swift
@@ -0,0 +1,155 @@
+import AppCore
+import Combine
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXMessengerClient
+import XXModels
+
+public struct RestoreComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Field: String, Hashable {
+      case passphrase
+    }
+
+    public struct File: Equatable {
+      public init(name: String, data: Data) {
+        self.name = name
+        self.data = data
+      }
+
+      public var name: String
+      public var data: Data
+    }
+
+    public init(
+      file: File? = nil,
+      fileImportFailure: String? = nil,
+      restoreFailures: [String] = [],
+      focusedField: Field? = nil,
+      isImportingFile: Bool = false,
+      passphrase: String = "",
+      isRestoring: Bool = false
+    ) {
+      self.file = file
+      self.fileImportFailure = fileImportFailure
+      self.restoreFailures = restoreFailures
+      self.focusedField = focusedField
+      self.isImportingFile = isImportingFile
+      self.passphrase = passphrase
+      self.isRestoring = isRestoring
+    }
+
+    public var file: File?
+    public var fileImportFailure: String?
+    public var restoreFailures: [String]
+    @BindableState public var focusedField: Field?
+    @BindableState public var isImportingFile: Bool
+    @BindableState public var passphrase: String
+    @BindableState public var isRestoring: Bool
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case importFileTapped
+    case fileImport(Result<URL, NSError>)
+    case restoreTapped
+    case finished
+    case failed([NSError])
+    case binding(BindingAction<State>)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.loadData) var loadData: URLDataLoader
+  @Dependency(\.app.now) var now: () -> Date
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      switch action {
+      case .importFileTapped:
+        state.isImportingFile = true
+        state.fileImportFailure = nil
+        return .none
+
+      case .fileImport(.success(let url)):
+        state.isImportingFile = false
+        do {
+          state.file = .init(
+            name: url.lastPathComponent,
+            data: try loadData(url)
+          )
+          state.fileImportFailure = nil
+        } catch {
+          state.file = nil
+          state.fileImportFailure = error.localizedDescription
+        }
+        return .none
+
+      case .fileImport(.failure(let error)):
+        state.isImportingFile = false
+        state.file = nil
+        state.fileImportFailure = error.localizedDescription
+        return .none
+
+      case .restoreTapped:
+        guard let backupData = state.file?.data, backupData.count > 0 else { return .none }
+        let backupPassphrase = state.passphrase
+        state.isRestoring = true
+        state.restoreFailures = []
+        return Effect.result {
+          do {
+            let result = try messenger.restoreBackup(
+              backupData: backupData,
+              backupPassphrase: backupPassphrase
+            )
+            let facts = try messenger.ud.tryGet().getFacts()
+            try db().saveContact(Contact(
+              id: try messenger.e2e.tryGet().getContact().getId(),
+              username: facts.get(.username)?.value,
+              email: facts.get(.email)?.value,
+              phone: facts.get(.phone)?.value,
+              createdAt: now()
+            ))
+            try result.restoredContacts.forEach { contactId in
+              if try db().fetchContacts(.init(id: [contactId])).isEmpty {
+                try db().saveContact(Contact(
+                  id: contactId,
+                  createdAt: now()
+                ))
+              }
+            }
+            return .success(.finished)
+          } catch {
+            var errors = [error as NSError]
+            do {
+              try messenger.destroy()
+            } catch {
+              errors.append(error as NSError)
+            }
+            return .success(.failed(errors))
+          }
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .finished:
+        state.isRestoring = false
+        return .none
+
+      case .failed(let errors):
+        state.isRestoring = false
+        state.restoreFailures = errors.map(\.localizedDescription)
+        return .none
+
+      case .binding(_):
+        return .none
+      }
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/RestoreFeature/RestoreFeature.swift b/Examples/xx-messenger/Sources/RestoreFeature/RestoreFeature.swift
deleted file mode 100644
index 6b3d61d340a7932b6367f71894a0efd707ae81d2..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/RestoreFeature/RestoreFeature.swift
+++ /dev/null
@@ -1,181 +0,0 @@
-import AppCore
-import Combine
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXMessengerClient
-import XXModels
-
-public struct RestoreState: Equatable {
-  public enum Field: String, Hashable {
-    case passphrase
-  }
-
-  public struct File: Equatable {
-    public init(name: String, data: Data) {
-      self.name = name
-      self.data = data
-    }
-
-    public var name: String
-    public var data: Data
-  }
-
-  public init(
-    file: File? = nil,
-    fileImportFailure: String? = nil,
-    restoreFailures: [String] = [],
-    focusedField: Field? = nil,
-    isImportingFile: Bool = false,
-    passphrase: String = "",
-    isRestoring: Bool = false
-  ) {
-    self.file = file
-    self.fileImportFailure = fileImportFailure
-    self.restoreFailures = restoreFailures
-    self.focusedField = focusedField
-    self.isImportingFile = isImportingFile
-    self.passphrase = passphrase
-    self.isRestoring = isRestoring
-  }
-
-  public var file: File?
-  public var fileImportFailure: String?
-  public var restoreFailures: [String]
-  @BindableState public var focusedField: Field?
-  @BindableState public var isImportingFile: Bool
-  @BindableState public var passphrase: String
-  @BindableState public var isRestoring: Bool
-}
-
-public enum RestoreAction: Equatable, BindableAction {
-  case importFileTapped
-  case fileImport(Result<URL, NSError>)
-  case restoreTapped
-  case finished
-  case failed([NSError])
-  case binding(BindingAction<RestoreState>)
-}
-
-public struct RestoreEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    loadData: URLDataLoader,
-    now: @escaping () -> Date,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.loadData = loadData
-    self.now = now
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var loadData: URLDataLoader
-  public var now: () -> Date
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension RestoreEnvironment {
-  public static let unimplemented = RestoreEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    loadData: .unimplemented,
-    now: XCTUnimplemented("\(Self.self).now"),
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let restoreReducer = Reducer<RestoreState, RestoreAction, RestoreEnvironment>
-{ state, action, env in
-  switch action {
-  case .importFileTapped:
-    state.isImportingFile = true
-    state.fileImportFailure = nil
-    return .none
-
-  case .fileImport(.success(let url)):
-    state.isImportingFile = false
-    do {
-      state.file = .init(
-        name: url.lastPathComponent,
-        data: try env.loadData(url)
-      )
-      state.fileImportFailure = nil
-    } catch {
-      state.file = nil
-      state.fileImportFailure = error.localizedDescription
-    }
-    return .none
-
-  case .fileImport(.failure(let error)):
-    state.isImportingFile = false
-    state.file = nil
-    state.fileImportFailure = error.localizedDescription
-    return .none
-
-  case .restoreTapped:
-    guard let backupData = state.file?.data, backupData.count > 0 else { return .none }
-    let backupPassphrase = state.passphrase
-    state.isRestoring = true
-    state.restoreFailures = []
-    return Effect.result {
-      do {
-        let result = try env.messenger.restoreBackup(
-          backupData: backupData,
-          backupPassphrase: backupPassphrase
-        )
-        let facts = try env.messenger.ud.tryGet().getFacts()
-        try env.db().saveContact(Contact(
-          id: try env.messenger.e2e.tryGet().getContact().getId(),
-          username: facts.get(.username)?.value,
-          email: facts.get(.email)?.value,
-          phone: facts.get(.phone)?.value,
-          createdAt: env.now()
-        ))
-        try result.restoredContacts.forEach { contactId in
-          if try env.db().fetchContacts(.init(id: [contactId])).isEmpty {
-            try env.db().saveContact(Contact(
-              id: contactId,
-              createdAt: env.now()
-            ))
-          }
-        }
-        return .success(.finished)
-      } catch {
-        var errors = [error as NSError]
-        do {
-          try env.messenger.destroy()
-        } catch {
-          errors.append(error as NSError)
-        }
-        return .success(.failed(errors))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .finished:
-    state.isRestoring = false
-    return .none
-
-  case .failed(let errors):
-    state.isRestoring = false
-    state.restoreFailures = errors.map(\.localizedDescription)
-    return .none
-
-  case .binding(_):
-    return .none
-  }
-}
-.binding()
diff --git a/Examples/xx-messenger/Sources/RestoreFeature/RestoreView.swift b/Examples/xx-messenger/Sources/RestoreFeature/RestoreView.swift
index 281f3f061e4a3ff5ccc1913bb45e45a03cff6576..503b868d44a587b19fb89728b2e9c7e713606301 100644
--- a/Examples/xx-messenger/Sources/RestoreFeature/RestoreView.swift
+++ b/Examples/xx-messenger/Sources/RestoreFeature/RestoreView.swift
@@ -2,12 +2,12 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct RestoreView: View {
-  public init(store: Store<RestoreState, RestoreAction>) {
+  public init(store: StoreOf<RestoreComponent>) {
     self.store = store
   }
 
-  let store: Store<RestoreState, RestoreAction>
-  @FocusState var focusedField: RestoreState.Field?
+  let store: StoreOf<RestoreComponent>
+  @FocusState var focusedField: RestoreComponent.State.Field?
 
   struct ViewState: Equatable {
     struct File: Equatable {
@@ -19,11 +19,11 @@ public struct RestoreView: View {
     var isImportingFile: Bool
     var passphrase: String
     var isRestoring: Bool
-    var focusedField: RestoreState.Field?
+    var focusedField: RestoreComponent.State.Field?
     var fileImportFailure: String?
     var restoreFailures: [String]
 
-    init(state: RestoreState) {
+    init(state: RestoreComponent.State) {
       file = state.file.map { .init(name: $0.name, size: $0.data.count) }
       isImportingFile = state.isImportingFile
       passphrase = state.passphrase
@@ -61,7 +61,7 @@ public struct RestoreView: View {
     }
   }
 
-  @ViewBuilder func fileSection(_ viewStore: ViewStore<ViewState, RestoreAction>) -> some View {
+  @ViewBuilder func fileSection(_ viewStore: ViewStore<ViewState, RestoreComponent.Action>) -> some View {
     Section {
       if let file = viewStore.file {
         HStack(alignment: .bottom) {
@@ -100,7 +100,7 @@ public struct RestoreView: View {
     }
   }
 
-  @ViewBuilder func restoreSection(_ viewStore: ViewStore<ViewState, RestoreAction>) -> some View {
+  @ViewBuilder func restoreSection(_ viewStore: ViewStore<ViewState, RestoreComponent.Action>) -> some View {
     Section {
       SecureField("Passphrase", text: viewStore.binding(
         get: \.passphrase,
@@ -152,7 +152,7 @@ public struct RestoreView: View {
 public struct RestoreView_Previews: PreviewProvider {
   public static var previews: some View {
     RestoreView(store: Store(
-      initialState: RestoreState(
+      initialState: RestoreComponent.State(
         file: .init(name: "preview", data: Data()),
         fileImportFailure: nil,
         restoreFailures: [
@@ -165,8 +165,7 @@ public struct RestoreView_Previews: PreviewProvider {
         passphrase: "",
         isRestoring: true
       ),
-      reducer: .empty,
-      environment: ()
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestComponent.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..f5f2e8afec9e1bf0ef52a40165221c21c538a914
--- /dev/null
+++ b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestComponent.swift
@@ -0,0 +1,138 @@
+import AppCore
+import Combine
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct SendRequestComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public init(
+      contact: XXClient.Contact,
+      myContact: XXClient.Contact? = nil,
+      sendUsername: Bool = true,
+      sendEmail: Bool = true,
+      sendPhone: Bool = true,
+      isSending: Bool = false,
+      failure: String? = nil
+    ) {
+      self.contact = contact
+      self.myContact = myContact
+      self.sendUsername = sendUsername
+      self.sendEmail = sendEmail
+      self.sendPhone = sendPhone
+      self.isSending = isSending
+      self.failure = failure
+    }
+
+    public var contact: XXClient.Contact
+    public var myContact: XXClient.Contact?
+    @BindableState public var sendUsername: Bool
+    @BindableState public var sendEmail: Bool
+    @BindableState public var sendPhone: Bool
+    public var isSending: Bool
+    public var failure: String?
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case start
+    case sendTapped
+    case sendSucceeded
+    case sendFailed(String)
+    case binding(BindingAction<State>)
+    case myContactFetched(XXClient.Contact)
+    case myContactFetchFailed(NSError)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      switch action {
+      case .start:
+        return Effect.run { subscriber in
+          do {
+            let contact = try messenger.myContact()
+            subscriber.send(.myContactFetched(contact))
+          } catch {
+            subscriber.send(.myContactFetchFailed(error as NSError))
+          }
+          subscriber.send(completion: .finished)
+          return AnyCancellable {}
+        }
+        .receive(on: mainQueue)
+        .subscribe(on: bgQueue)
+        .eraseToEffect()
+
+      case .myContactFetched(let contact):
+        state.myContact = contact
+        state.failure = nil
+        return .none
+
+      case .myContactFetchFailed(let failure):
+        state.myContact = nil
+        state.failure = failure.localizedDescription
+        return .none
+
+      case .sendTapped:
+        state.isSending = true
+        state.failure = nil
+        return .result { [state] in
+          func updateAuthStatus(_ authStatus: XXModels.Contact.AuthStatus) throws {
+            try db().bulkUpdateContacts(
+              .init(id: [try state.contact.getId()]),
+              .init(authStatus: authStatus)
+            )
+          }
+          do {
+            try updateAuthStatus(.requesting)
+            let myFacts = try state.myContact?.getFacts() ?? []
+            var includedFacts: [Fact] = []
+            if state.sendUsername, let fact = myFacts.get(.username) {
+              includedFacts.append(fact)
+            }
+            if state.sendEmail, let fact = myFacts.get(.email) {
+              includedFacts.append(fact)
+            }
+            if state.sendPhone, let fact = myFacts.get(.phone) {
+              includedFacts.append(fact)
+            }
+            _ = try messenger.e2e.tryGet().requestAuthenticatedChannel(
+              partner: state.contact,
+              myFacts: includedFacts
+            )
+            try updateAuthStatus(.requested)
+            return .success(.sendSucceeded)
+          } catch {
+            try? updateAuthStatus(.requestFailed)
+            return .success(.sendFailed(error.localizedDescription))
+          }
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .sendSucceeded:
+        state.isSending = false
+        state.failure = nil
+        return .none
+
+      case .sendFailed(let failure):
+        state.isSending = false
+        state.failure = failure
+        return .none
+
+      case .binding(_):
+        return .none
+      }
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift
deleted file mode 100644
index 18075179aedc62daff46847052e2bc0071f76b0a..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift
+++ /dev/null
@@ -1,158 +0,0 @@
-import AppCore
-import Combine
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct SendRequestState: Equatable {
-  public init(
-    contact: XXClient.Contact,
-    myContact: XXClient.Contact? = nil,
-    sendUsername: Bool = true,
-    sendEmail: Bool = true,
-    sendPhone: Bool = true,
-    isSending: Bool = false,
-    failure: String? = nil
-  ) {
-    self.contact = contact
-    self.myContact = myContact
-    self.sendUsername = sendUsername
-    self.sendEmail = sendEmail
-    self.sendPhone = sendPhone
-    self.isSending = isSending
-    self.failure = failure
-  }
-
-  public var contact: XXClient.Contact
-  public var myContact: XXClient.Contact?
-  @BindableState public var sendUsername: Bool
-  @BindableState public var sendEmail: Bool
-  @BindableState public var sendPhone: Bool
-  public var isSending: Bool
-  public var failure: String?
-}
-
-public enum SendRequestAction: Equatable, BindableAction {
-  case start
-  case sendTapped
-  case sendSucceeded
-  case sendFailed(String)
-  case binding(BindingAction<SendRequestState>)
-  case myContactFetched(XXClient.Contact)
-  case myContactFetchFailed(NSError)
-}
-
-public struct SendRequestEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension SendRequestEnvironment {
-  public static let unimplemented = SendRequestEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let sendRequestReducer = Reducer<SendRequestState, SendRequestAction, SendRequestEnvironment>
-{ state, action, env in
-  switch action {
-  case .start:
-    return Effect.run { subscriber in
-      do {
-        let contact = try env.messenger.myContact()
-        subscriber.send(.myContactFetched(contact))
-      } catch {
-        subscriber.send(.myContactFetchFailed(error as NSError))
-      }
-      subscriber.send(completion: .finished)
-      return AnyCancellable {}
-    }
-    .receive(on: env.mainQueue)
-    .subscribe(on: env.bgQueue)
-    .eraseToEffect()
-
-  case .myContactFetched(let contact):
-    state.myContact = contact
-    state.failure = nil
-    return .none
-
-  case .myContactFetchFailed(let failure):
-    state.myContact = nil
-    state.failure = failure.localizedDescription
-    return .none
-
-  case .sendTapped:
-    state.isSending = true
-    state.failure = nil
-    return .result { [state] in
-      func updateAuthStatus(_ authStatus: XXModels.Contact.AuthStatus) throws {
-        try env.db().bulkUpdateContacts(
-          .init(id: [try state.contact.getId()]),
-          .init(authStatus: authStatus)
-        )
-      }
-      do {
-        try updateAuthStatus(.requesting)
-        let myFacts = try state.myContact?.getFacts() ?? []
-        var includedFacts: [Fact] = []
-        if state.sendUsername, let fact = myFacts.get(.username) {
-          includedFacts.append(fact)
-        }
-        if state.sendEmail, let fact = myFacts.get(.email) {
-          includedFacts.append(fact)
-        }
-        if state.sendPhone, let fact = myFacts.get(.phone) {
-          includedFacts.append(fact)
-        }
-        _ = try env.messenger.e2e.tryGet().requestAuthenticatedChannel(
-          partner: state.contact,
-          myFacts: includedFacts
-        )
-        try updateAuthStatus(.requested)
-        return .success(.sendSucceeded)
-      } catch {
-        try? updateAuthStatus(.requestFailed)
-        return .success(.sendFailed(error.localizedDescription))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .sendSucceeded:
-    state.isSending = false
-    state.failure = nil
-    return .none
-
-  case .sendFailed(let failure):
-    state.isSending = false
-    state.failure = failure
-    return .none
-
-  case .binding(_):
-    return .none
-  }
-}
-.binding()
diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift
index 809d45be72436067d4e04c35bdef1995f7ed5a9d..90294a80303f1746d659aa8e3789e1b09e696275 100644
--- a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift
+++ b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestView.swift
@@ -4,11 +4,11 @@ import SwiftUI
 import XXClient
 
 public struct SendRequestView: View {
-  public init(store: Store<SendRequestState, SendRequestAction>) {
+  public init(store: StoreOf<SendRequestComponent>) {
     self.store = store
   }
 
-  let store: Store<SendRequestState, SendRequestAction>
+  let store: StoreOf<SendRequestComponent>
 
   struct ViewState: Equatable {
     var contactUsername: String?
@@ -23,7 +23,7 @@ public struct SendRequestView: View {
     var isSending: Bool
     var failure: String?
 
-    init(state: SendRequestState) {
+    init(state: SendRequestComponent.State) {
       contactUsername = try? state.contact.getFact(.username)?.value
       contactEmail = try? state.contact.getFact(.email)?.value
       contactPhone = try? state.contact.getFact(.phone)?.value
@@ -129,7 +129,7 @@ public struct SendRequestView_Previews: PreviewProvider {
   public static var previews: some View {
     NavigationView {
       SendRequestView(store: Store(
-        initialState: SendRequestState(
+        initialState: SendRequestComponent.State(
           contact: {
             var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
             contact.getFactsFromContact.run = { _ in
@@ -158,8 +158,7 @@ public struct SendRequestView_Previews: PreviewProvider {
           isSending: false,
           failure: "Something went wrong"
         ),
-        reducer: .empty,
-        environment: ()
+        reducer: EmptyReducer()
       ))
     }
   }
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchComponent.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..c274d15b4dd368656619491c8a05ecadd3f2bb32
--- /dev/null
+++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchComponent.swift
@@ -0,0 +1,146 @@
+import ComposableArchitecture
+import ComposablePresentation
+import ContactFeature
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+
+public struct UserSearchComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Field: String, Hashable {
+      case username
+      case email
+      case phone
+    }
+
+    public struct Result: Equatable, Identifiable {
+      public init(
+        id: Data,
+        xxContact: XXClient.Contact,
+        username: String? = nil,
+        email: String? = nil,
+        phone: String? = nil
+      ) {
+        self.id = id
+        self.xxContact = xxContact
+        self.username = username
+        self.email = email
+        self.phone = phone
+      }
+
+      public var id: Data
+      public var xxContact: XXClient.Contact
+      public var username: String?
+      public var email: String?
+      public var phone: String?
+
+      public var hasFacts: Bool {
+        username != nil || email != nil || phone != nil
+      }
+    }
+
+    public init(
+      focusedField: Field? = nil,
+      query: MessengerSearchContacts.Query = .init(),
+      isSearching: Bool = false,
+      failure: String? = nil,
+      results: IdentifiedArrayOf<Result> = [],
+      contact: ContactComponent.State? = nil
+    ) {
+      self.focusedField = focusedField
+      self.query = query
+      self.isSearching = isSearching
+      self.failure = failure
+      self.results = results
+      self.contact = contact
+    }
+
+    @BindableState public var focusedField: Field?
+    @BindableState public var query: MessengerSearchContacts.Query
+    public var isSearching: Bool
+    public var failure: String?
+    public var results: IdentifiedArrayOf<Result>
+    public var contact: ContactComponent.State?
+  }
+
+  public enum Action: Equatable, BindableAction {
+    case searchTapped
+    case didFail(String)
+    case didSucceed([Contact])
+    case didDismissContact
+    case resultTapped(id: Data)
+    case binding(BindingAction<State>)
+    case contact(ContactComponent.Action)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public var body: some ReducerProtocol<State, Action> {
+    BindingReducer()
+    Reduce { state, action in
+      switch action {
+      case .searchTapped:
+        state.focusedField = nil
+        state.isSearching = true
+        state.results = []
+        state.failure = nil
+        return .result { [query = state.query] in
+          do {
+            return .success(.didSucceed(try messenger.searchContacts(query: query)))
+          } catch {
+            return .success(.didFail(error.localizedDescription))
+          }
+        }
+        .subscribe(on: bgQueue)
+        .receive(on: mainQueue)
+        .eraseToEffect()
+
+      case .didSucceed(let contacts):
+        state.isSearching = false
+        state.failure = nil
+        state.results = IdentifiedArray(uniqueElements: contacts.compactMap { contact in
+          guard let id = try? contact.getId() else { return nil }
+          return State.Result(
+            id: id,
+            xxContact: contact,
+            username: try? contact.getFact(.username)?.value,
+            email: try? contact.getFact(.email)?.value,
+            phone: try? contact.getFact(.phone)?.value
+          )
+        })
+        return .none
+
+      case .didFail(let failure):
+        state.isSearching = false
+        state.failure = failure
+        state.results = []
+        return .none
+
+      case .didDismissContact:
+        state.contact = nil
+        return .none
+
+      case .resultTapped(let id):
+        state.contact = ContactComponent.State(
+          id: id,
+          xxContact: state.results[id: id]?.xxContact
+        )
+        return .none
+
+      case .binding(_), .contact(_):
+        return .none
+      }
+    }
+    .presenting(
+      state: .keyPath(\.contact),
+      id: .keyPath(\.?.id),
+      action: /Action.contact,
+      presented: { ContactComponent() }
+    )
+  }
+}
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
deleted file mode 100644
index f39353a78db32d57a1429d8f8a0b6e5fb46260e0..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchFeature.swift
+++ /dev/null
@@ -1,168 +0,0 @@
-import ComposableArchitecture
-import ComposablePresentation
-import ContactFeature
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-
-public struct UserSearchState: Equatable {
-  public enum Field: String, Hashable {
-    case username
-    case email
-    case phone
-  }
-
-  public struct Result: Equatable, Identifiable {
-    public init(
-      id: Data,
-      xxContact: XXClient.Contact,
-      username: String? = nil,
-      email: String? = nil,
-      phone: String? = nil
-    ) {
-      self.id = id
-      self.xxContact = xxContact
-      self.username = username
-      self.email = email
-      self.phone = phone
-    }
-
-    public var id: Data
-    public var xxContact: XXClient.Contact
-    public var username: String?
-    public var email: String?
-    public var phone: String?
-
-    public var hasFacts: Bool {
-      username != nil || email != nil || phone != nil
-    }
-  }
-
-  public init(
-    focusedField: Field? = nil,
-    query: MessengerSearchContacts.Query = .init(),
-    isSearching: Bool = false,
-    failure: String? = nil,
-    results: IdentifiedArrayOf<Result> = [],
-    contact: ContactState? = nil
-  ) {
-    self.focusedField = focusedField
-    self.query = query
-    self.isSearching = isSearching
-    self.failure = failure
-    self.results = results
-    self.contact = contact
-  }
-
-  @BindableState public var focusedField: Field?
-  @BindableState public var query: MessengerSearchContacts.Query
-  public var isSearching: Bool
-  public var failure: String?
-  public var results: IdentifiedArrayOf<Result>
-  public var contact: ContactState?
-}
-
-public enum UserSearchAction: Equatable, BindableAction {
-  case searchTapped
-  case didFail(String)
-  case didSucceed([Contact])
-  case didDismissContact
-  case resultTapped(id: Data)
-  case binding(BindingAction<UserSearchState>)
-  case contact(ContactAction)
-}
-
-public struct UserSearchEnvironment {
-  public init(
-    messenger: Messenger,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>,
-    contact: @escaping () -> ContactEnvironment
-  ) {
-    self.messenger = messenger
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-    self.contact = contact
-  }
-
-  public var messenger: Messenger
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-  public var contact: () -> ContactEnvironment
-}
-
-#if DEBUG
-extension UserSearchEnvironment {
-  public static let unimplemented = UserSearchEnvironment(
-    messenger: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented,
-    contact: { .unimplemented }
-  )
-}
-#endif
-
-public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSearchEnvironment>
-{ state, action, env in
-  switch action {
-  case .searchTapped:
-    state.focusedField = nil
-    state.isSearching = true
-    state.results = []
-    state.failure = nil
-    return .result { [query = state.query] in
-      do {
-        return .success(.didSucceed(try env.messenger.searchContacts(query: query)))
-      } catch {
-        return .success(.didFail(error.localizedDescription))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .didSucceed(let contacts):
-    state.isSearching = false
-    state.failure = nil
-    state.results = IdentifiedArray(uniqueElements: contacts.compactMap { contact in
-      guard let id = try? contact.getId() else { return nil }
-      return UserSearchState.Result(
-        id: id,
-        xxContact: contact,
-        username: try? contact.getFact(.username)?.value,
-        email: try? contact.getFact(.email)?.value,
-        phone: try? contact.getFact(.phone)?.value
-      )
-    })
-    return .none
-
-  case .didFail(let failure):
-    state.isSearching = false
-    state.failure = failure
-    state.results = []
-    return .none
-
-  case .didDismissContact:
-    state.contact = nil
-    return .none
-
-  case .resultTapped(let id):
-    state.contact = ContactState(
-      id: id,
-      xxContact: state.results[id: id]?.xxContact
-    )
-    return .none
-
-  case .binding(_), .contact(_):
-    return .none
-  }
-}
-.binding()
-.presenting(
-  contactReducer,
-  state: .keyPath(\.contact),
-  id: .keyPath(\.?.id),
-  action: /UserSearchAction.contact,
-  environment: { $0.contact() }
-)
diff --git a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
index 328ff98c2e0f79907da76927ce254322022ea07a..ea5f2ad74eb15cd65c2935e1e206ecb3515d5ccc 100644
--- a/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
+++ b/Examples/xx-messenger/Sources/UserSearchFeature/UserSearchView.swift
@@ -5,21 +5,21 @@ import SwiftUI
 import XXMessengerClient
 
 public struct UserSearchView: View {
-  public init(store: Store<UserSearchState, UserSearchAction>) {
+  public init(store: StoreOf<UserSearchComponent>) {
     self.store = store
   }
 
-  let store: Store<UserSearchState, UserSearchAction>
-  @FocusState var focusedField: UserSearchState.Field?
+  let store: StoreOf<UserSearchComponent>
+  @FocusState var focusedField: UserSearchComponent.State.Field?
 
   struct ViewState: Equatable {
-    var focusedField: UserSearchState.Field?
+    var focusedField: UserSearchComponent.State.Field?
     var query: MessengerSearchContacts.Query
     var isSearching: Bool
     var failure: String?
-    var results: IdentifiedArrayOf<UserSearchState.Result>
+    var results: IdentifiedArrayOf<UserSearchComponent.State.Result>
 
-    init(state: UserSearchState) {
+    init(state: UserSearchComponent.State) {
       focusedField = state.focusedField
       query = state.query
       isSearching = state.isSearching
@@ -35,7 +35,7 @@ public struct UserSearchView: View {
           TextField(
             text: viewStore.binding(
               get: { $0.query.username ?? "" },
-              send: { UserSearchAction.set(\.$query.username, $0.isEmpty ? nil : $0) }
+              send: { UserSearchComponent.Action.set(\.$query.username, $0.isEmpty ? nil : $0) }
             ),
             prompt: Text("Enter username"),
             label: { Text("Username") }
@@ -45,7 +45,7 @@ public struct UserSearchView: View {
           TextField(
             text: viewStore.binding(
               get: { $0.query.email ?? "" },
-              send: { UserSearchAction.set(\.$query.email, $0.isEmpty ? nil : $0) }
+              send: { UserSearchComponent.Action.set(\.$query.email, $0.isEmpty ? nil : $0) }
             ),
             prompt: Text("Enter email"),
             label: { Text("Email") }
@@ -55,7 +55,7 @@ public struct UserSearchView: View {
           TextField(
             text: viewStore.binding(
               get: { $0.query.phone ?? "" },
-              send: { UserSearchAction.set(\.$query.phone, $0.isEmpty ? nil : $0) }
+              send: { UserSearchComponent.Action.set(\.$query.phone, $0.isEmpty ? nil : $0) }
             ),
             prompt: Text("Enter phone"),
             label: { Text("Phone") }
@@ -124,7 +124,7 @@ public struct UserSearchView: View {
       .background(NavigationLinkWithStore(
         store.scope(
           state: \.contact,
-          action: UserSearchAction.contact
+          action: UserSearchComponent.Action.contact
         ),
         onDeactivate: { viewStore.send(.didDismissContact) },
         destination: ContactView.init(store:)
@@ -137,9 +137,8 @@ public struct UserSearchView: View {
 public struct UserSearchView_Previews: PreviewProvider {
   public static var previews: some View {
     UserSearchView(store: Store(
-      initialState: UserSearchState(),
-      reducer: .empty,
-      environment: ()
+      initialState: UserSearchComponent.State(),
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactComponent.swift b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..09fdb1585659abe9c77b873dd6917dcb17d75034
--- /dev/null
+++ b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactComponent.swift
@@ -0,0 +1,75 @@
+import AppCore
+import ComposableArchitecture
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+import XXMessengerClient
+import XXModels
+
+public struct VerifyContactComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public enum Result: Equatable {
+      case success(Bool)
+      case failure(String)
+    }
+
+    public init(
+      contact: XXClient.Contact,
+      isVerifying: Bool = false,
+      result: Result? = nil
+    ) {
+      self.contact = contact
+      self.isVerifying = isVerifying
+      self.result = result
+    }
+
+    public var contact: XXClient.Contact
+    public var isVerifying: Bool
+    public var result: Result?
+  }
+
+  public enum Action: Equatable {
+    case verifyTapped
+    case didVerify(State.Result)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
+    switch action {
+    case .verifyTapped:
+      state.isVerifying = true
+      state.result = nil
+      return Effect.result { [state] in
+        func updateStatus(_ status: XXModels.Contact.AuthStatus) throws {
+          try db().bulkUpdateContacts.callAsFunction(
+            .init(id: [try state.contact.getId()]),
+            .init(authStatus: status)
+          )
+        }
+        do {
+          try updateStatus(.verificationInProgress)
+          let result = try messenger.verifyContact(state.contact)
+          try updateStatus(result ? .verified : .verificationFailed)
+          return .success(.didVerify(.success(result)))
+        } catch {
+          try? updateStatus(.verificationFailed)
+          return .success(.didVerify(.failure(error.localizedDescription)))
+        }
+      }
+      .subscribe(on: bgQueue)
+      .receive(on: mainQueue)
+      .eraseToEffect()
+
+    case .didVerify(let result):
+      state.isVerifying = false
+      state.result = result
+      return .none
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactFeature.swift b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactFeature.swift
deleted file mode 100644
index 1663d155f59e9c8d7eb4b542de4255021620be4c..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactFeature.swift
+++ /dev/null
@@ -1,97 +0,0 @@
-import AppCore
-import ComposableArchitecture
-import Foundation
-import XCTestDynamicOverlay
-import XXClient
-import XXMessengerClient
-import XXModels
-
-public struct VerifyContactState: Equatable {
-  public enum Result: Equatable {
-    case success(Bool)
-    case failure(String)
-  }
-
-  public init(
-    contact: XXClient.Contact,
-    isVerifying: Bool = false,
-    result: Result? = nil
-  ) {
-    self.contact = contact
-    self.isVerifying = isVerifying
-    self.result = result
-  }
-
-  public var contact: XXClient.Contact
-  public var isVerifying: Bool
-  public var result: Result?
-}
-
-public enum VerifyContactAction: Equatable {
-  case verifyTapped
-  case didVerify(VerifyContactState.Result)
-}
-
-public struct VerifyContactEnvironment {
-  public init(
-    messenger: Messenger,
-    db: DBManagerGetDB,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.db = db
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var db: DBManagerGetDB
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension VerifyContactEnvironment {
-  public static let unimplemented = VerifyContactEnvironment(
-    messenger: .unimplemented,
-    db: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let verifyContactReducer = Reducer<VerifyContactState, VerifyContactAction, VerifyContactEnvironment>
-{ state, action, env in
-  switch action {
-  case .verifyTapped:
-    state.isVerifying = true
-    state.result = nil
-    return Effect.result { [state] in
-      func updateStatus(_ status: XXModels.Contact.AuthStatus) throws {
-        try env.db().bulkUpdateContacts.callAsFunction(
-          .init(id: [try state.contact.getId()]),
-          .init(authStatus: status)
-        )
-      }
-      do {
-        try updateStatus(.verificationInProgress)
-        let result = try env.messenger.verifyContact(state.contact)
-        try updateStatus(result ? .verified : .verificationFailed)
-        return .success(.didVerify(.success(result)))
-      } catch {
-        try? updateStatus(.verificationFailed)
-        return .success(.didVerify(.failure(error.localizedDescription)))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .didVerify(let result):
-    state.isVerifying = false
-    state.result = result
-    return .none
-  }
-}
diff --git a/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactView.swift b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactView.swift
index 32b81ab789a5408cb46f34574cca6bf7e12144c0..d2cb428c3a8189d4ddbcd6f7948573ad68f9963e 100644
--- a/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactView.swift
+++ b/Examples/xx-messenger/Sources/VerifyContactFeature/VerifyContactView.swift
@@ -2,20 +2,20 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct VerifyContactView: View {
-  public init(store: Store<VerifyContactState, VerifyContactAction>) {
+  public init(store: StoreOf<VerifyContactComponent>) {
     self.store = store
   }
 
-  let store: Store<VerifyContactState, VerifyContactAction>
+  let store: StoreOf<VerifyContactComponent>
 
   struct ViewState: Equatable {
     var username: String?
     var email: String?
     var phone: String?
     var isVerifying: Bool
-    var result: VerifyContactState.Result?
+    var result: VerifyContactComponent.State.Result?
 
-    init(state: VerifyContactState) {
+    init(state: VerifyContactComponent.State) {
       username = try? state.contact.getFact(.username)?.value
       email = try? state.contact.getFact(.email)?.value
       phone = try? state.contact.getFact(.phone)?.value
@@ -89,11 +89,10 @@ public struct VerifyContactView: View {
 public struct VerifyContactView_Previews: PreviewProvider {
   public static var previews: some View {
     VerifyContactView(store: Store(
-      initialState: VerifyContactState(
+      initialState: VerifyContactComponent.State(
         contact: .unimplemented("contact-data".data(using: .utf8)!)
       ),
-      reducer: .empty,
-      environment: ()
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeComponent.swift b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeComponent.swift
new file mode 100644
index 0000000000000000000000000000000000000000..55af67030deb51647f697d58252537ea6242af8a
--- /dev/null
+++ b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeComponent.swift
@@ -0,0 +1,64 @@
+import ComposableArchitecture
+import SwiftUI
+import XXMessengerClient
+
+public struct WelcomeComponent: ReducerProtocol {
+  public struct State: Equatable {
+    public init(
+      isCreatingCMix: Bool = false,
+      failure: String? = nil
+    ) {
+      self.isCreatingAccount = isCreatingCMix
+      self.failure = failure
+    }
+
+    public var isCreatingAccount: Bool
+    public var failure: String?
+  }
+
+  public enum Action: Equatable {
+    case newAccountTapped
+    case restoreTapped
+    case finished
+    case failed(String)
+  }
+
+  public init() {}
+
+  @Dependency(\.app.messenger) var messenger: Messenger
+  @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
+  @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
+
+  public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
+    switch action {
+    case .newAccountTapped:
+      state.isCreatingAccount = true
+      state.failure = nil
+      return .future { fulfill in
+        do {
+          try messenger.create()
+          fulfill(.success(.finished))
+        }
+        catch {
+          fulfill(.success(.failed(error.localizedDescription)))
+        }
+      }
+      .subscribe(on: bgQueue)
+      .receive(on: mainQueue)
+      .eraseToEffect()
+
+    case .restoreTapped:
+      return .none
+
+    case .finished:
+      state.isCreatingAccount = false
+      state.failure = nil
+      return .none
+
+    case .failed(let failure):
+      state.isCreatingAccount = false
+      state.failure = failure
+      return .none
+    }
+  }
+}
diff --git a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeFeature.swift b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeFeature.swift
deleted file mode 100644
index 66e9ef1b3492c1d8277fac263a9d76523769030c..0000000000000000000000000000000000000000
--- a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeFeature.swift
+++ /dev/null
@@ -1,83 +0,0 @@
-import ComposableArchitecture
-import SwiftUI
-import XXMessengerClient
-
-public struct WelcomeState: Equatable {
-  public init(
-    isCreatingCMix: Bool = false,
-    failure: String? = nil
-  ) {
-    self.isCreatingAccount = isCreatingCMix
-    self.failure = failure
-  }
-
-  public var isCreatingAccount: Bool
-  public var failure: String?
-}
-
-public enum WelcomeAction: Equatable {
-  case newAccountTapped
-  case restoreTapped
-  case finished
-  case failed(String)
-}
-
-public struct WelcomeEnvironment {
-  public init(
-    messenger: Messenger,
-    mainQueue: AnySchedulerOf<DispatchQueue>,
-    bgQueue: AnySchedulerOf<DispatchQueue>
-  ) {
-    self.messenger = messenger
-    self.mainQueue = mainQueue
-    self.bgQueue = bgQueue
-  }
-
-  public var messenger: Messenger
-  public var mainQueue: AnySchedulerOf<DispatchQueue>
-  public var bgQueue: AnySchedulerOf<DispatchQueue>
-}
-
-#if DEBUG
-extension WelcomeEnvironment {
-  public static let unimplemented = WelcomeEnvironment(
-    messenger: .unimplemented,
-    mainQueue: .unimplemented,
-    bgQueue: .unimplemented
-  )
-}
-#endif
-
-public let welcomeReducer = Reducer<WelcomeState, WelcomeAction, WelcomeEnvironment>
-{ state, action, env in
-  switch action {
-  case .newAccountTapped:
-    state.isCreatingAccount = true
-    state.failure = nil
-    return .future { fulfill in
-      do {
-        try env.messenger.create()
-        fulfill(.success(.finished))
-      }
-      catch {
-        fulfill(.success(.failed(error.localizedDescription)))
-      }
-    }
-    .subscribe(on: env.bgQueue)
-    .receive(on: env.mainQueue)
-    .eraseToEffect()
-
-  case .restoreTapped:
-    return .none
-
-  case .finished:
-    state.isCreatingAccount = false
-    state.failure = nil
-    return .none
-
-  case .failed(let failure):
-    state.isCreatingAccount = false
-    state.failure = failure
-    return .none
-  }
-}
diff --git a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift
index 64396fe1b6392267dc61efae6a8a73997698cd0c..0b4597e1de1bb3b803f1e1f2d76e8d7cc09022fd 100644
--- a/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift
+++ b/Examples/xx-messenger/Sources/WelcomeFeature/WelcomeView.swift
@@ -3,14 +3,14 @@ import ComposableArchitecture
 import SwiftUI
 
 public struct WelcomeView: View {
-  public init(store: Store<WelcomeState, WelcomeAction>) {
+  public init(store: StoreOf<WelcomeComponent>) {
     self.store = store
   }
 
-  let store: Store<WelcomeState, WelcomeAction>
+  let store: StoreOf<WelcomeComponent>
 
   struct ViewState: Equatable {
-    init(_ state: WelcomeState) {
+    init(_ state: WelcomeComponent.State) {
       isCreatingAccount = state.isCreatingAccount
       failure = state.failure
     }
@@ -69,9 +69,8 @@ public struct WelcomeView: View {
 public struct WelcomeView_Previews: PreviewProvider {
   public static var previews: some View {
     WelcomeView(store: Store(
-      initialState: WelcomeState(),
-      reducer: .empty,
-      environment: ()
+      initialState: WelcomeComponent.State(),
+      reducer: EmptyReducer()
     ))
   }
 }
diff --git a/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift b/Examples/xx-messenger/Tests/AppFeatureTests/AppComponentTests.swift
similarity index 56%
rename from Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift
rename to Examples/xx-messenger/Tests/AppFeatureTests/AppComponentTests.swift
index 5ec16fc51cbb5f6b7ca0ce23696860b4460a8df3..7533666ecc857876698f0478e688257cb2c501e6 100644
--- a/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/AppFeatureTests/AppComponentTests.swift
@@ -8,37 +8,59 @@ import XCTest
 import XXClient
 @testable import AppFeature
 
-final class AppFeatureTests: XCTestCase {
+final class AppComponentTests: XCTestCase {
+  func testSetupLogging() {
+    var actions: [Action] = []
+
+    let store = TestStore(
+      initialState: AppComponent.State(),
+      reducer: AppComponent()
+    )
+    store.dependencies.app.messenger.setLogLevel.run = { level in
+      actions.append(.didSetLogLevel(level))
+      return true
+    }
+    store.dependencies.app.messenger.startLogging.run = {
+      actions.append(.didStartLogging)
+    }
+
+    store.send(.setupLogging)
+
+    XCTAssertNoDifference(actions, [
+      .didSetLogLevel(.debug),
+      .didStartLogging,
+    ])
+  }
+
   func testStartWithoutMessengerCreated() {
     var actions: [Action]!
 
     let store = TestStore(
-      initialState: AppState(),
-      reducer: appReducer,
-      environment: .unimplemented
+      initialState: AppComponent.State(),
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { false }
-    store.environment.messenger.isLoaded.run = { false }
-    store.environment.messenger.isCreated.run = { false }
-    store.environment.dbManager.makeDB.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { false }
+    store.dependencies.app.messenger.isLoaded.run = { false }
+    store.dependencies.app.messenger.isCreated.run = { false }
+    store.dependencies.app.dbManager.makeDB.run = {
       actions.append(.didMakeDB)
     }
-    store.environment.authHandler.run = { _ in
+    store.dependencies.app.authHandler.run = { _ in
       actions.append(.didStartAuthHandler)
       return Cancellable {}
     }
-    store.environment.messageListener.run = { _ in
+    store.dependencies.app.messageListener.run = { _ in
       actions.append(.didStartMessageListener)
       return Cancellable {}
     }
-    store.environment.receiveFileHandler.run = { _ in
+    store.dependencies.app.receiveFileHandler.run = { _ in
       actions.append(.didStartReceiveFileHandler)
       return Cancellable {}
     }
-    store.environment.messenger.registerBackupCallback.run = { _ in
+    store.dependencies.app.messenger.registerBackupCallback.run = { _ in
       actions.append(.didRegisterBackupCallback)
       return Cancellable {}
     }
@@ -46,8 +68,8 @@ final class AppFeatureTests: XCTestCase {
     actions = []
     store.send(.start)
 
-    store.receive(.set(\.$screen, .welcome(WelcomeState()))) {
-      $0.screen = .welcome(WelcomeState())
+    store.receive(.set(\.$screen, .welcome(WelcomeComponent.State()))) {
+      $0.screen = .welcome(WelcomeComponent.State())
     }
     XCTAssertNoDifference(actions, [
       .didMakeDB,
@@ -64,35 +86,34 @@ final class AppFeatureTests: XCTestCase {
     var actions: [Action]!
 
     let store = TestStore(
-      initialState: AppState(),
-      reducer: appReducer,
-      environment: .unimplemented
+      initialState: AppComponent.State(),
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { false }
-    store.environment.messenger.isLoaded.run = { false }
-    store.environment.messenger.isCreated.run = { true }
-    store.environment.dbManager.makeDB.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { false }
+    store.dependencies.app.messenger.isLoaded.run = { false }
+    store.dependencies.app.messenger.isCreated.run = { true }
+    store.dependencies.app.dbManager.makeDB.run = {
       actions.append(.didMakeDB)
     }
-    store.environment.messenger.load.run = {
+    store.dependencies.app.messenger.load.run = {
       actions.append(.didLoadMessenger)
     }
-    store.environment.authHandler.run = { _ in
+    store.dependencies.app.authHandler.run = { _ in
       actions.append(.didStartAuthHandler)
       return Cancellable {}
     }
-    store.environment.messageListener.run = { _ in
+    store.dependencies.app.messageListener.run = { _ in
       actions.append(.didStartMessageListener)
       return Cancellable {}
     }
-    store.environment.receiveFileHandler.run = { _ in
+    store.dependencies.app.receiveFileHandler.run = { _ in
       actions.append(.didStartReceiveFileHandler)
       return Cancellable {}
     }
-    store.environment.messenger.registerBackupCallback.run = { _ in
+    store.dependencies.app.messenger.registerBackupCallback.run = { _ in
       actions.append(.didRegisterBackupCallback)
       return Cancellable {}
     }
@@ -100,8 +121,8 @@ final class AppFeatureTests: XCTestCase {
     actions = []
     store.send(.start)
 
-    store.receive(.set(\.$screen, .home(HomeState()))) {
-      $0.screen = .home(HomeState())
+    store.receive(.set(\.$screen, .home(HomeComponent.State()))) {
+      $0.screen = .home(HomeComponent.State())
     }
     XCTAssertNoDifference(actions, [
       .didMakeDB,
@@ -119,34 +140,33 @@ final class AppFeatureTests: XCTestCase {
     var actions: [Action]!
 
     let store = TestStore(
-      initialState: AppState(
-        screen: .welcome(WelcomeState())
+      initialState: AppComponent.State(
+        screen: .welcome(WelcomeComponent.State())
       ),
-      reducer: appReducer,
-      environment: .unimplemented
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { true }
-    store.environment.messenger.isLoaded.run = { false }
-    store.environment.messenger.isCreated.run = { true }
-    store.environment.messenger.load.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { true }
+    store.dependencies.app.messenger.isLoaded.run = { false }
+    store.dependencies.app.messenger.isCreated.run = { true }
+    store.dependencies.app.messenger.load.run = {
       actions.append(.didLoadMessenger)
     }
-    store.environment.authHandler.run = { _ in
+    store.dependencies.app.authHandler.run = { _ in
       actions.append(.didStartAuthHandler)
       return Cancellable {}
     }
-    store.environment.messageListener.run = { _ in
+    store.dependencies.app.messageListener.run = { _ in
       actions.append(.didStartMessageListener)
       return Cancellable {}
     }
-    store.environment.receiveFileHandler.run = { _ in
+    store.dependencies.app.receiveFileHandler.run = { _ in
       actions.append(.didStartReceiveFileHandler)
       return Cancellable {}
     }
-    store.environment.messenger.registerBackupCallback.run = { _ in
+    store.dependencies.app.messenger.registerBackupCallback.run = { _ in
       actions.append(.didRegisterBackupCallback)
       return Cancellable {}
     }
@@ -156,8 +176,8 @@ final class AppFeatureTests: XCTestCase {
       $0.screen = .loading
     }
 
-    store.receive(.set(\.$screen, .home(HomeState()))) {
-      $0.screen = .home(HomeState())
+    store.receive(.set(\.$screen, .home(HomeComponent.State()))) {
+      $0.screen = .home(HomeComponent.State())
     }
     XCTAssertNoDifference(actions, [
       .didStartAuthHandler,
@@ -174,34 +194,33 @@ final class AppFeatureTests: XCTestCase {
     var actions: [Action]!
 
     let store = TestStore(
-      initialState: AppState(
-        screen: .restore(RestoreState())
+      initialState: AppComponent.State(
+        screen: .restore(RestoreComponent.State())
       ),
-      reducer: appReducer,
-      environment: .unimplemented
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { true }
-    store.environment.messenger.isLoaded.run = { false }
-    store.environment.messenger.isCreated.run = { true }
-    store.environment.messenger.load.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { true }
+    store.dependencies.app.messenger.isLoaded.run = { false }
+    store.dependencies.app.messenger.isCreated.run = { true }
+    store.dependencies.app.messenger.load.run = {
       actions.append(.didLoadMessenger)
     }
-    store.environment.authHandler.run = { _ in
+    store.dependencies.app.authHandler.run = { _ in
       actions.append(.didStartAuthHandler)
       return Cancellable {}
     }
-    store.environment.messageListener.run = { _ in
+    store.dependencies.app.messageListener.run = { _ in
       actions.append(.didStartMessageListener)
       return Cancellable {}
     }
-    store.environment.receiveFileHandler.run = { _ in
+    store.dependencies.app.receiveFileHandler.run = { _ in
       actions.append(.didStartReceiveFileHandler)
       return Cancellable {}
     }
-    store.environment.messenger.registerBackupCallback.run = { _ in
+    store.dependencies.app.messenger.registerBackupCallback.run = { _ in
       actions.append(.didRegisterBackupCallback)
       return Cancellable {}
     }
@@ -211,8 +230,8 @@ final class AppFeatureTests: XCTestCase {
       $0.screen = .loading
     }
 
-    store.receive(.set(\.$screen, .home(HomeState()))) {
-      $0.screen = .home(HomeState())
+    store.receive(.set(\.$screen, .home(HomeComponent.State()))) {
+      $0.screen = .home(HomeComponent.State())
     }
     XCTAssertNoDifference(actions, [
       .didStartAuthHandler,
@@ -229,31 +248,30 @@ final class AppFeatureTests: XCTestCase {
     var actions: [Action]!
 
     let store = TestStore(
-      initialState: AppState(
-        screen: .home(HomeState())
+      initialState: AppComponent.State(
+        screen: .home(HomeComponent.State())
       ),
-      reducer: appReducer,
-      environment: .unimplemented
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { true }
-    store.environment.messenger.isLoaded.run = { false }
-    store.environment.messenger.isCreated.run = { false }
-    store.environment.authHandler.run = { _ in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { true }
+    store.dependencies.app.messenger.isLoaded.run = { false }
+    store.dependencies.app.messenger.isCreated.run = { false }
+    store.dependencies.app.authHandler.run = { _ in
       actions.append(.didStartAuthHandler)
       return Cancellable {}
     }
-    store.environment.messageListener.run = { _ in
+    store.dependencies.app.messageListener.run = { _ in
       actions.append(.didStartMessageListener)
       return Cancellable {}
     }
-    store.environment.receiveFileHandler.run = { _ in
+    store.dependencies.app.receiveFileHandler.run = { _ in
       actions.append(.didStartReceiveFileHandler)
       return Cancellable {}
     }
-    store.environment.messenger.registerBackupCallback.run = { _ in
+    store.dependencies.app.messenger.registerBackupCallback.run = { _ in
       actions.append(.didRegisterBackupCallback)
       return Cancellable {}
     }
@@ -263,8 +281,8 @@ final class AppFeatureTests: XCTestCase {
       $0.screen = .loading
     }
 
-    store.receive(.set(\.$screen, .welcome(WelcomeState()))) {
-      $0.screen = .welcome(WelcomeState())
+    store.receive(.set(\.$screen, .welcome(WelcomeComponent.State()))) {
+      $0.screen = .welcome(WelcomeComponent.State())
     }
     XCTAssertNoDifference(actions, [
       .didStartAuthHandler,
@@ -278,15 +296,14 @@ final class AppFeatureTests: XCTestCase {
 
   func testWelcomeRestoreTapped() {
     let store = TestStore(
-      initialState: AppState(
-        screen: .welcome(WelcomeState())
+      initialState: AppComponent.State(
+        screen: .welcome(WelcomeComponent.State())
       ),
-      reducer: appReducer,
-      environment: .unimplemented
+      reducer: AppComponent()
     )
 
     store.send(.welcome(.restoreTapped)) {
-      $0.screen = .restore(RestoreState())
+      $0.screen = .restore(RestoreComponent.State())
     }
   }
 
@@ -294,11 +311,10 @@ final class AppFeatureTests: XCTestCase {
     let failure = "Something went wrong"
 
     let store = TestStore(
-      initialState: AppState(
-        screen: .welcome(WelcomeState())
+      initialState: AppComponent.State(
+        screen: .welcome(WelcomeComponent.State())
       ),
-      reducer: appReducer,
-      environment: .unimplemented
+      reducer: AppComponent()
     )
 
     store.send(.welcome(.failed(failure))) {
@@ -311,15 +327,14 @@ final class AppFeatureTests: XCTestCase {
     let error = Failure()
 
     let store = TestStore(
-      initialState: AppState(),
-      reducer: appReducer,
-      environment: .unimplemented
+      initialState: AppComponent.State(),
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { false }
-    store.environment.dbManager.makeDB.run = { throw error }
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { false }
+    store.dependencies.app.dbManager.makeDB.run = { throw error }
 
     store.send(.start)
 
@@ -337,30 +352,29 @@ final class AppFeatureTests: XCTestCase {
     var actions: [Action]!
 
     let store = TestStore(
-      initialState: AppState(),
-      reducer: appReducer,
-      environment: .unimplemented
+      initialState: AppComponent.State(),
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { true }
-    store.environment.messenger.isLoaded.run = { false }
-    store.environment.messenger.isCreated.run = { true }
-    store.environment.messenger.load.run = { throw error }
-    store.environment.authHandler.run = { _ in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { true }
+    store.dependencies.app.messenger.isLoaded.run = { false }
+    store.dependencies.app.messenger.isCreated.run = { true }
+    store.dependencies.app.messenger.load.run = { throw error }
+    store.dependencies.app.authHandler.run = { _ in
       actions.append(.didStartAuthHandler)
       return Cancellable {}
     }
-    store.environment.messageListener.run = { _ in
+    store.dependencies.app.messageListener.run = { _ in
       actions.append(.didStartMessageListener)
       return Cancellable {}
     }
-    store.environment.receiveFileHandler.run = { _ in
+    store.dependencies.app.receiveFileHandler.run = { _ in
       actions.append(.didStartReceiveFileHandler)
       return Cancellable {}
     }
-    store.environment.messenger.registerBackupCallback.run = { _ in
+    store.dependencies.app.messenger.registerBackupCallback.run = { _ in
       actions.append(.didRegisterBackupCallback)
       return Cancellable {}
     }
@@ -390,56 +404,55 @@ final class AppFeatureTests: XCTestCase {
     var backupCallback: [UpdateBackupFunc] = []
 
     let store = TestStore(
-      initialState: AppState(),
-      reducer: appReducer,
-      environment: .unimplemented
+      initialState: AppComponent.State(),
+      reducer: AppComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.dbManager.hasDB.run = { true }
-    store.environment.messenger.isLoaded.run = { true }
-    store.environment.messenger.isCreated.run = { true }
-    store.environment.authHandler.run = { onError in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.hasDB.run = { true }
+    store.dependencies.app.messenger.isLoaded.run = { true }
+    store.dependencies.app.messenger.isCreated.run = { true }
+    store.dependencies.app.authHandler.run = { onError in
       authHandlerOnError.append(onError)
       actions.append(.didStartAuthHandler)
       return Cancellable {
         actions.append(.didCancelAuthHandler)
       }
     }
-    store.environment.messageListener.run = { onError in
+    store.dependencies.app.messageListener.run = { onError in
       messageListenerOnError.append(onError)
       actions.append(.didStartMessageListener)
       return Cancellable {
         actions.append(.didCancelMessageListener)
       }
     }
-    store.environment.receiveFileHandler.run = { onError in
+    store.dependencies.app.receiveFileHandler.run = { onError in
       fileHandlerOnError.append(onError)
       actions.append(.didStartReceiveFileHandler)
       return Cancellable {
         actions.append(.didCancelReceiveFileHandler)
       }
     }
-    store.environment.messenger.registerBackupCallback.run = { callback in
+    store.dependencies.app.messenger.registerBackupCallback.run = { callback in
       backupCallback.append(callback)
       actions.append(.didRegisterBackupCallback)
       return Cancellable {
         actions.append(.didCancelBackupCallback)
       }
     }
-    store.environment.log.run = { msg, _, _, _ in
+    store.dependencies.app.log.run = { msg, _, _, _ in
       actions.append(.didLog(msg))
     }
-    store.environment.backupStorage.store = { data in
+    store.dependencies.app.backupStorage.store = { data in
       actions.append(.didStoreBackup(data))
     }
 
     actions = []
     store.send(.start)
 
-    store.receive(.set(\.$screen, .home(HomeState()))) {
-      $0.screen = .home(HomeState())
+    store.receive(.set(\.$screen, .home(HomeComponent.State()))) {
+      $0.screen = .home(HomeComponent.State())
     }
     XCTAssertNoDifference(actions, [
       .didStartAuthHandler,
@@ -453,8 +466,8 @@ final class AppFeatureTests: XCTestCase {
       $0.screen = .loading
     }
 
-    store.receive(.set(\.$screen, .home(HomeState()))) {
-      $0.screen = .home(HomeState())
+    store.receive(.set(\.$screen, .home(HomeComponent.State()))) {
+      $0.screen = .home(HomeComponent.State())
     }
     XCTAssertNoDifference(actions, [
       .didCancelAuthHandler,
@@ -524,4 +537,6 @@ private enum Action: Equatable {
   case didCancelBackupCallback
   case didLog(Logger.Message)
   case didStoreBackup(Data)
+  case didSetLogLevel(LogLevel)
+  case didStartLogging
 }
diff --git a/Examples/xx-messenger/Tests/BackupFeatureTests/BackupFeatureTests.swift b/Examples/xx-messenger/Tests/BackupFeatureTests/BackupComponentTests.swift
similarity index 70%
rename from Examples/xx-messenger/Tests/BackupFeatureTests/BackupFeatureTests.swift
rename to Examples/xx-messenger/Tests/BackupFeatureTests/BackupComponentTests.swift
index d0c69eb87e2efea29f635fce84656b04d2e35cba..e409a1b9241f2a342f77fc3dca23174ca06c1c15 100644
--- a/Examples/xx-messenger/Tests/BackupFeatureTests/BackupFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/BackupFeatureTests/BackupComponentTests.swift
@@ -14,19 +14,18 @@ final class BackupFeatureTests: XCTestCase {
     )
 
     let store = TestStore(
-      initialState: BackupState(),
-      reducer: backupReducer,
-      environment: .unimplemented
+      initialState: BackupComponent.State(),
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
-    store.environment.backupStorage.stored = {
+    store.dependencies.app.backupStorage.stored = {
       storedBackup
     }
-    store.environment.backupStorage.observe = {
+    store.dependencies.app.backupStorage.observe = {
       let id = UUID()
       observers[id] = $0
       return Cancellable { observers[id] = nil }
@@ -68,22 +67,21 @@ final class BackupFeatureTests: XCTestCase {
     let passphrase = "backup-password"
 
     let store = TestStore(
-      initialState: BackupState(),
-      reducer: backupReducer,
-      environment: .unimplemented
+      initialState: BackupComponent.State(),
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.myContact.run = { includeFacts in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.myContact.run = { includeFacts in
       actions.append(.didGetMyContact(includingFacts: includeFacts))
       var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
       contact.getFactsFromContact.run = { _ in [Fact(type: .username, value: username)] }
       return contact
     }
-    store.environment.messenger.startBackup.run = { passphrase, params in
+    store.dependencies.app.messenger.startBackup.run = { passphrase, params in
       actions.append(.didStartBackup(passphrase: passphrase, params: params))
     }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
 
@@ -124,20 +122,19 @@ final class BackupFeatureTests: XCTestCase {
     var isBackupRunning: [Bool] = [false]
 
     let store = TestStore(
-      initialState: BackupState(
+      initialState: BackupComponent.State(
         passphrase: "1234"
       ),
-      reducer: backupReducer,
-      environment: .unimplemented
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.myContact.run = { _ in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.myContact.run = { _ in
       var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
       contact.getFactsFromContact.run = { _ in [] }
       return contact
     }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
 
@@ -145,7 +142,7 @@ final class BackupFeatureTests: XCTestCase {
       $0.isStarting = true
     }
 
-    let failure = BackupState.Error.contactUsernameMissing
+    let failure = BackupComponent.State.Error.contactUsernameMissing
     store.receive(.didStart(failure: failure as NSError)) {
       $0.isRunning = false
       $0.isStarting = false
@@ -159,16 +156,15 @@ final class BackupFeatureTests: XCTestCase {
     var isBackupRunning: [Bool] = [false]
 
     let store = TestStore(
-      initialState: BackupState(
+      initialState: BackupComponent.State(
         passphrase: "1234"
       ),
-      reducer: backupReducer,
-      environment: .unimplemented
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.myContact.run = { _ in throw failure }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.myContact.run = { _ in throw failure }
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
 
@@ -189,23 +185,22 @@ final class BackupFeatureTests: XCTestCase {
     var isBackupRunning: [Bool] = [false]
 
     let store = TestStore(
-      initialState: BackupState(
+      initialState: BackupComponent.State(
         passphrase: "1234"
       ),
-      reducer: backupReducer,
-      environment: .unimplemented
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.myContact.run = { _ in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.myContact.run = { _ in
       var contact = Contact.unimplemented("data".data(using: .utf8)!)
       contact.getFactsFromContact.run = { _ in [Fact(type: .username, value: "username")] }
       return contact
     }
-    store.environment.messenger.startBackup.run = { _, _ in
+    store.dependencies.app.messenger.startBackup.run = { _, _ in
       throw failure
     }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
 
@@ -225,16 +220,15 @@ final class BackupFeatureTests: XCTestCase {
     var isBackupRunning: [Bool] = [true]
 
     let store = TestStore(
-      initialState: BackupState(),
-      reducer: backupReducer,
-      environment: .unimplemented
+      initialState: BackupComponent.State(),
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.resumeBackup.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.resumeBackup.run = {
       actions.append(.didResumeBackup)
     }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
 
@@ -260,16 +254,15 @@ final class BackupFeatureTests: XCTestCase {
     var isBackupRunning: [Bool] = [false]
 
     let store = TestStore(
-      initialState: BackupState(),
-      reducer: backupReducer,
-      environment: .unimplemented
+      initialState: BackupComponent.State(),
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.resumeBackup.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.resumeBackup.run = {
       throw failure
     }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
 
@@ -289,19 +282,18 @@ final class BackupFeatureTests: XCTestCase {
     var isBackupRunning: [Bool] = [false]
 
     let store = TestStore(
-      initialState: BackupState(),
-      reducer: backupReducer,
-      environment: .unimplemented
+      initialState: BackupComponent.State(),
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.stopBackup.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.stopBackup.run = {
       actions.append(.didStopBackup)
     }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
-    store.environment.backupStorage.remove = {
+    store.dependencies.app.backupStorage.remove = {
       actions.append(.didRemoveBackup)
     }
 
@@ -330,16 +322,15 @@ final class BackupFeatureTests: XCTestCase {
     var isBackupRunning: [Bool] = [true]
 
     let store = TestStore(
-      initialState: BackupState(),
-      reducer: backupReducer,
-      environment: .unimplemented
+      initialState: BackupComponent.State(),
+      reducer: BackupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.stopBackup.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.stopBackup.run = {
       throw failure
     }
-    store.environment.messenger.isBackupRunning.run = {
+    store.dependencies.app.messenger.isBackupRunning.run = {
       isBackupRunning.removeFirst()
     }
 
@@ -356,11 +347,10 @@ final class BackupFeatureTests: XCTestCase {
 
   func testAlertDismissed() {
     let store = TestStore(
-      initialState: BackupState(
+      initialState: BackupComponent.State(
         alert: .error(NSError(domain: "test", code: 0))
       ),
-      reducer: backupReducer,
-      environment: .unimplemented
+      reducer: BackupComponent()
     )
 
     store.send(.alertDismissed) {
@@ -372,14 +362,13 @@ final class BackupFeatureTests: XCTestCase {
     let backupData = "backup-data".data(using: .utf8)!
 
     let store = TestStore(
-      initialState: BackupState(
+      initialState: BackupComponent.State(
         backup: .init(
           date: Date(),
           data: backupData
         )
       ),
-      reducer: backupReducer,
-      environment: .unimplemented
+      reducer: BackupComponent()
     )
 
     store.send(.exportTapped) {
diff --git a/Examples/xx-messenger/Tests/ChatFeatureTests/ChatFeatureTests.swift b/Examples/xx-messenger/Tests/ChatFeatureTests/ChatComponentTests.swift
similarity index 79%
rename from Examples/xx-messenger/Tests/ChatFeatureTests/ChatFeatureTests.swift
rename to Examples/xx-messenger/Tests/ChatFeatureTests/ChatComponentTests.swift
index 01d0ed8faa3c37b0a2005ace0b7c98d13b2e6590..c8f5952864c6f77f04ec42b53b5875a317ee37a4 100644
--- a/Examples/xx-messenger/Tests/ChatFeatureTests/ChatFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ChatFeatureTests/ChatComponentTests.swift
@@ -8,15 +8,14 @@ import XXMessengerClient
 import XXModels
 @testable import ChatFeature
 
-final class ChatFeatureTests: XCTestCase {
+final class ChatComponentTests: XCTestCase {
   func testStart() {
     let contactId = "contact-id".data(using: .utf8)!
     let myContactId = "my-contact-id".data(using: .utf8)!
 
     let store = TestStore(
-      initialState: ChatState(id: .contact(contactId)),
-      reducer: chatReducer,
-      environment: .unimplemented
+      initialState: ChatComponent.State(id: .contact(contactId)),
+      reducer: ChatComponent()
     )
 
     var didFetchMessagesWithQuery: [XXModels.Message.Query] = []
@@ -24,9 +23,9 @@ final class ChatFeatureTests: XCTestCase {
     var didFetchFileTransfersWithQuery: [XXModels.FileTransfer.Query] = []
     let fileTransfersPublisher = PassthroughSubject<[XXModels.FileTransfer], Error>()
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -35,7 +34,7 @@ final class ChatFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchMessagesPublisher.run = { query in
         didFetchMessagesWithQuery.append(query)
@@ -113,7 +112,7 @@ final class ChatFeatureTests: XCTestCase {
       sentFileTransfer,
     ])
 
-    let expectedMessages = IdentifiedArrayOf<ChatState.Message>(uniqueElements: [
+    let expectedMessages = IdentifiedArrayOf<ChatComponent.State.Message>(uniqueElements: [
       .init(
         id: 1,
         date: Date(timeIntervalSince1970: 1),
@@ -142,17 +141,16 @@ final class ChatFeatureTests: XCTestCase {
 
   func testStartFailure() {
     let store = TestStore(
-      initialState: ChatState(id: .contact("contact-id".data(using: .utf8)!)),
-      reducer: chatReducer,
-      environment: .unimplemented
+      initialState: ChatComponent.State(id: .contact("contact-id".data(using: .utf8)!)),
+      reducer: ChatComponent()
     )
 
     struct Failure: Error {}
     let error = Failure()
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -176,14 +174,13 @@ final class ChatFeatureTests: XCTestCase {
     var sendMessageCompletion: SendMessage.Completion?
 
     let store = TestStore(
-      initialState: ChatState(id: .contact("contact-id".data(using: .utf8)!)),
-      reducer: chatReducer,
-      environment: .unimplemented
+      initialState: ChatComponent.State(id: .contact("contact-id".data(using: .utf8)!)),
+      reducer: ChatComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.sendMessage.run = { text, recipientId, _, completion in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.sendMessage.run = { text, recipientId, _, completion in
       didSendMessageWithParams.append(.init(text: text, recipientId: recipientId))
       sendMessageCompletion = completion
     }
@@ -208,17 +205,16 @@ final class ChatFeatureTests: XCTestCase {
     var sendMessageCompletion: SendMessage.Completion?
 
     let store = TestStore(
-      initialState: ChatState(
+      initialState: ChatComponent.State(
         id: .contact("contact-id".data(using: .utf8)!),
         text: "Hello"
       ),
-      reducer: chatReducer,
-      environment: .unimplemented
+      reducer: ChatComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.sendMessage.run = { _, _, onError, completion in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.sendMessage.run = { _, _, onError, completion in
       sendMessageOnError = onError
       sendMessageCompletion = completion
     }
@@ -250,14 +246,13 @@ final class ChatFeatureTests: XCTestCase {
     var sendImageCompletion: SendImage.Completion?
 
     let store = TestStore(
-      initialState: ChatState(id: .contact("contact-id".data(using: .utf8)!)),
-      reducer: chatReducer,
-      environment: .unimplemented
+      initialState: ChatComponent.State(id: .contact("contact-id".data(using: .utf8)!)),
+      reducer: ChatComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.sendImage.run = { image, recipientId, _, completion in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.sendImage.run = { image, recipientId, _, completion in
       didSendImageWithParams.append(.init(image: image, recipientId: recipientId))
       sendImageCompletion = completion
     }
@@ -277,16 +272,15 @@ final class ChatFeatureTests: XCTestCase {
     var sendImageCompletion: SendImage.Completion?
 
     let store = TestStore(
-      initialState: ChatState(
+      initialState: ChatComponent.State(
         id: .contact("contact-id".data(using: .utf8)!)
       ),
-      reducer: chatReducer,
-      environment: .unimplemented
+      reducer: ChatComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.sendImage.run = { _, _, onError, completion in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.sendImage.run = { _, _, onError, completion in
       sendImageOnError = onError
       sendImageCompletion = completion
     }
diff --git a/Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthFeatureTests.swift b/Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthComponentTests.swift
similarity index 80%
rename from Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthFeatureTests.swift
rename to Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthComponentTests.swift
index a13645f2f29d6fd2ec3fe87353105cf3adf8df86..78031cb58485f6b81f7dee35fbf5e375c9934afa 100644
--- a/Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/CheckContactAuthFeatureTests/CheckContactAuthComponentTests.swift
@@ -5,27 +5,26 @@ import XXClient
 import XXModels
 @testable import CheckContactAuthFeature
 
-final class CheckContactAuthFeatureTests: XCTestCase {
+final class CheckContactAuthComponentTests: XCTestCase {
   func testCheck() {
     var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
     let contactId = "contact-id".data(using: .utf8)!
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: CheckContactAuthState(
+      initialState: CheckContactAuthComponent.State(
         contact: contact
       ),
-      reducer: checkContactAuthReducer,
-      environment: .unimplemented
+      reducer: CheckContactAuthComponent()
     )
 
     var didCheckPartnerId: [Data] = []
     var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
     var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.hasAuthenticatedChannel.run = { partnerId in
         didCheckPartnerId.append(partnerId)
@@ -33,7 +32,7 @@ final class CheckContactAuthFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContactsWithQuery.append(query)
@@ -64,20 +63,19 @@ final class CheckContactAuthFeatureTests: XCTestCase {
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: CheckContactAuthState(
+      initialState: CheckContactAuthComponent.State(
         contact: contact
       ),
-      reducer: checkContactAuthReducer,
-      environment: .unimplemented
+      reducer: CheckContactAuthComponent()
     )
 
     var didCheckPartnerId: [Data] = []
     var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
     var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.hasAuthenticatedChannel.run = { partnerId in
         didCheckPartnerId.append(partnerId)
@@ -85,7 +83,7 @@ final class CheckContactAuthFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContactsWithQuery.append(query)
@@ -116,19 +114,18 @@ final class CheckContactAuthFeatureTests: XCTestCase {
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: CheckContactAuthState(
+      initialState: CheckContactAuthComponent.State(
         contact: contact
       ),
-      reducer: checkContactAuthReducer,
-      environment: .unimplemented
+      reducer: CheckContactAuthComponent()
     )
 
     struct Failure: Error {}
     let error = Failure()
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.hasAuthenticatedChannel.run = { _ in throw error }
       return e2e
diff --git a/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift b/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestComponentTests.swift
similarity index 82%
rename from Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift
rename to Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestComponentTests.swift
index bc84224a8671219aeb7dbed33f5f8552b3302d59..263363f14b47b76f72c4945932c3e003ab200702 100644
--- a/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ConfirmRequestFeatureTests/ConfirmRequestComponentTests.swift
@@ -5,27 +5,26 @@ import XXClient
 import XXModels
 @testable import ConfirmRequestFeature
 
-final class ConfirmRequestFeatureTests: XCTestCase {
+final class ConfirmRequestComponentTests: XCTestCase {
   func testConfirm() {
     var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
     let contactId = "contact-id".data(using: .utf8)!
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: ConfirmRequestState(
+      initialState: ConfirmRequestComponent.State(
         contact: contact
       ),
-      reducer: confirmRequestReducer,
-      environment: .unimplemented
+      reducer: ConfirmRequestComponent()
     )
 
     var didConfirmRequestFromContact: [XXClient.Contact] = []
     var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
     var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.confirmReceivedRequest.run = { contact in
         didConfirmRequestFromContact.append(contact)
@@ -33,7 +32,7 @@ final class ConfirmRequestFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContactsWithQuery.append(query)
@@ -70,11 +69,10 @@ final class ConfirmRequestFeatureTests: XCTestCase {
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: ConfirmRequestState(
+      initialState: ConfirmRequestComponent.State(
         contact: contact
       ),
-      reducer: confirmRequestReducer,
-      environment: .unimplemented
+      reducer: ConfirmRequestComponent()
     )
 
     struct Failure: Error {}
@@ -83,14 +81,14 @@ final class ConfirmRequestFeatureTests: XCTestCase {
     var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
     var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.confirmReceivedRequest.run = { _ in throw error }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContactsWithQuery.append(query)
diff --git a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactComponentTests.swift
similarity index 70%
rename from Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
rename to Examples/xx-messenger/Tests/ContactFeatureTests/ContactComponentTests.swift
index eba41b7ab57d1dddfc0827747760a9bce224bdab..04caf49dbcb5205ee5dd081398983c72a6694011 100644
--- a/Examples/xx-messenger/Tests/ContactFeatureTests/ContactFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ContactFeatureTests/ContactComponentTests.swift
@@ -16,19 +16,18 @@ import XXModels
 final class ContactFeatureTests: XCTestCase {
   func testStart() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     var dbDidFetchContacts: [XXModels.Contact.Query] = []
     let dbContactsPublisher = PassthroughSubject<[XXModels.Contact], Error>()
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.db.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContactsPublisher.run = { query in
         dbDidFetchContacts.append(query)
@@ -68,20 +67,19 @@ final class ContactFeatureTests: XCTestCase {
     }
 
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
         dbContact: dbContact,
         xxContact: xxContact
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     var dbDidSaveContact: [XXModels.Contact] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.db.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.saveContact.run = { contact in
         dbDidSaveContact.append(contact)
@@ -104,27 +102,25 @@ final class ContactFeatureTests: XCTestCase {
   func testLookupTapped() {
     let contactId = "contact-id".data(using: .utf8)!
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: contactId
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.lookupTapped) {
-      $0.lookup = ContactLookupState(id: contactId)
+      $0.lookup = ContactLookupComponent.State(id: contactId)
     }
   }
 
   func testLookupDismissed() {
     let contactId = "contact-id".data(using: .utf8)!
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: contactId,
-        lookup: ContactLookupState(id: contactId)
+        lookup: ContactLookupComponent.State(id: contactId)
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.lookupDismissed) {
@@ -136,12 +132,11 @@ final class ContactFeatureTests: XCTestCase {
     let contactId = "contact-id".data(using: .utf8)!
     let contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: contactId,
-        lookup: ContactLookupState(id: contactId)
+        lookup: ContactLookupComponent.State(id: contactId)
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.lookup(.didLookup(contact))) {
@@ -155,16 +150,15 @@ final class ContactFeatureTests: XCTestCase {
     dbContact.marshaled = "contact-data".data(using: .utf8)!
 
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: dbContact.id,
         dbContact: dbContact
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.sendRequestTapped) {
-      $0.sendRequest = SendRequestState(contact: .live(dbContact.marshaled!))
+      $0.sendRequest = SendRequestComponent.State(contact: .live(dbContact.marshaled!))
     }
   }
 
@@ -172,29 +166,27 @@ final class ContactFeatureTests: XCTestCase {
     let xxContact = XXClient.Contact.unimplemented("contact-id".data(using: .utf8)!)
 
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
         xxContact: xxContact
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.sendRequestTapped) {
-      $0.sendRequest = SendRequestState(contact: xxContact)
+      $0.sendRequest = SendRequestComponent.State(contact: xxContact)
     }
   }
 
   func testSendRequestDismissed() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
-        sendRequest: SendRequestState(
+        sendRequest: SendRequestComponent.State(
           contact: .unimplemented("contact-id".data(using: .utf8)!)
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.sendRequestDismissed) {
@@ -204,14 +196,13 @@ final class ContactFeatureTests: XCTestCase {
 
   func testSendRequestSucceeded() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
-        sendRequest: SendRequestState(
+        sendRequest: SendRequestComponent.State(
           contact: .unimplemented("contact-id".data(using: .utf8)!)
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.sendRequest(.sendSucceeded)) {
@@ -222,19 +213,18 @@ final class ContactFeatureTests: XCTestCase {
   func testVerifyContactTapped() {
     let contactData = "contact-data".data(using: .utf8)!
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: Data(),
         dbContact: XXModels.Contact(
           id: Data(),
           marshaled: contactData
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.verifyContactTapped) {
-      $0.verifyContact = VerifyContactState(
+      $0.verifyContact = VerifyContactComponent.State(
         contact: .unimplemented(contactData)
       )
     }
@@ -242,14 +232,13 @@ final class ContactFeatureTests: XCTestCase {
 
   func testVerifyContactDismissed() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
-        verifyContact: VerifyContactState(
+        verifyContact: VerifyContactComponent.State(
           contact: .unimplemented("contact-data".data(using: .utf8)!)
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.verifyContactDismissed) {
@@ -260,19 +249,18 @@ final class ContactFeatureTests: XCTestCase {
   func testCheckAuthTapped() {
     let contactData = "contact-data".data(using: .utf8)!
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: Data(),
         dbContact: XXModels.Contact(
           id: Data(),
           marshaled: contactData
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.checkAuthTapped) {
-      $0.checkAuth = CheckContactAuthState(
+      $0.checkAuth = CheckContactAuthComponent.State(
         contact: .unimplemented(contactData)
       )
     }
@@ -280,14 +268,13 @@ final class ContactFeatureTests: XCTestCase {
 
   func testCheckAuthDismissed() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
-        checkAuth: CheckContactAuthState(
+        checkAuth: CheckContactAuthComponent.State(
           contact: .unimplemented("contact-data".data(using: .utf8)!)
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.checkAuthDismissed) {
@@ -298,19 +285,18 @@ final class ContactFeatureTests: XCTestCase {
   func testResetAuthTapped() {
     let contactData = "contact-data".data(using: .utf8)!
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: Data(),
         dbContact: XXModels.Contact(
           id: Data(),
           marshaled: contactData
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.resetAuthTapped) {
-      $0.resetAuth = ResetAuthState(
+      $0.resetAuth = ResetAuthComponent.State(
         partner: .unimplemented(contactData)
       )
     }
@@ -318,14 +304,13 @@ final class ContactFeatureTests: XCTestCase {
 
   func testResetAuthDismissed() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: Data(),
-        resetAuth: ResetAuthState(
+        resetAuth: ResetAuthComponent.State(
           partner: .unimplemented(Data())
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.resetAuthDismissed) {
@@ -336,19 +321,18 @@ final class ContactFeatureTests: XCTestCase {
   func testConfirmRequestTapped() {
     let contactData = "contact-data".data(using: .utf8)!
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: Data(),
         dbContact: XXModels.Contact(
           id: Data(),
           marshaled: contactData
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.confirmRequestTapped) {
-      $0.confirmRequest = ConfirmRequestState(
+      $0.confirmRequest = ConfirmRequestComponent.State(
         contact: .unimplemented(contactData)
       )
     }
@@ -356,14 +340,13 @@ final class ContactFeatureTests: XCTestCase {
 
   func testConfirmRequestDismissed() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
-        confirmRequest: ConfirmRequestState(
+        confirmRequest: ConfirmRequestComponent.State(
           contact: .unimplemented("contact-data".data(using: .utf8)!)
         )
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.confirmRequestDismissed) {
@@ -374,26 +357,24 @@ final class ContactFeatureTests: XCTestCase {
   func testChatTapped() {
     let contactId = "contact-id".data(using: .utf8)!
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: contactId
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.chatTapped) {
-      $0.chat = ChatState(id: .contact(contactId))
+      $0.chat = ChatComponent.State(id: .contact(contactId))
     }
   }
 
   func testChatDismissed() {
     let store = TestStore(
-      initialState: ContactState(
+      initialState: ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
-        chat: ChatState(id: .contact("contact-id".data(using: .utf8)!))
+        chat: ChatComponent.State(id: .contact("contact-id".data(using: .utf8)!))
       ),
-      reducer: contactReducer,
-      environment: .unimplemented
+      reducer: ContactComponent()
     )
 
     store.send(.chatDismissed) {
diff --git a/Examples/xx-messenger/Tests/ContactLookupFeatureTests/ContactLookupFeatureTests.swift b/Examples/xx-messenger/Tests/ContactLookupFeatureTests/ContactLookupComponentTests.swift
similarity index 62%
rename from Examples/xx-messenger/Tests/ContactLookupFeatureTests/ContactLookupFeatureTests.swift
rename to Examples/xx-messenger/Tests/ContactLookupFeatureTests/ContactLookupComponentTests.swift
index 76dde8d07350e240ccf86acdbef175834851b56f..26f2a0f49f319f88211bc6e50d04be6f4474cf57 100644
--- a/Examples/xx-messenger/Tests/ContactLookupFeatureTests/ContactLookupFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ContactLookupFeatureTests/ContactLookupComponentTests.swift
@@ -3,20 +3,19 @@ import XCTest
 import XXClient
 @testable import ContactLookupFeature
 
-final class ContactLookupFeatureTests: XCTestCase {
+final class ContactLookupComponentTests: XCTestCase {
   func testLookup() {
     let id: Data = "1234".data(using: .utf8)!
     var didLookupId: [Data] = []
     let lookedUpContact = Contact.unimplemented("123data".data(using: .utf8)!)
 
     let store = TestStore(
-      initialState: ContactLookupState(id: id),
-      reducer: contactLookupReducer,
-      environment: .unimplemented
+      initialState: ContactLookupComponent.State(id: id),
+      reducer: ContactLookupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.lookupContact.run = { id in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.lookupContact.run = { id in
       didLookupId.append(id)
       return lookedUpContact
     }
@@ -39,13 +38,12 @@ final class ContactLookupFeatureTests: XCTestCase {
     let failure = NSError(domain: "test", code: 0)
 
     let store = TestStore(
-      initialState: ContactLookupState(id: id),
-      reducer: contactLookupReducer,
-      environment: .unimplemented
+      initialState: ContactLookupComponent.State(id: id),
+      reducer: ContactLookupComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.lookupContact.run = { _ in throw failure }
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.lookupContact.run = { _ in throw failure }
 
     store.send(.lookupTapped) {
       $0.isLookingUp = true
diff --git a/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsFeatureTests.swift b/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsComponentTests.swift
similarity index 70%
rename from Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsFeatureTests.swift
rename to Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsComponentTests.swift
index 9b3ac0d080ad8e23b8359ab8b3cf073433bed7d8..e0732e698e2756cf77de98ec25d47eea9f375757 100644
--- a/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ContactsFeatureTests/ContactsComponentTests.swift
@@ -9,21 +9,20 @@ import XXMessengerClient
 import XXModels
 @testable import ContactsFeature
 
-final class ContactsFeatureTests: XCTestCase {
+final class ContactsComponentTests: XCTestCase {
   func testStart() {
     let store = TestStore(
-      initialState: ContactsState(),
-      reducer: contactsReducer,
-      environment: .unimplemented
+      initialState: ContactsComponent.State(),
+      reducer: ContactsComponent()
     )
 
     let myId = "2".data(using: .utf8)!
     var didFetchContacts: [XXModels.Contact.Query] = []
     let contactsPublisher = PassthroughSubject<[XXModels.Contact], Error>()
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -32,7 +31,7 @@ final class ContactsFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContactsPublisher.run = { query in
         didFetchContacts.append(query)
@@ -67,28 +66,26 @@ final class ContactsFeatureTests: XCTestCase {
 
   func testSelectContact() {
     let store = TestStore(
-      initialState: ContactsState(),
-      reducer: contactsReducer,
-      environment: .unimplemented
+      initialState: ContactsComponent.State(),
+      reducer: ContactsComponent()
     )
 
     let contact = XXModels.Contact(id: "id".data(using: .utf8)!)
 
     store.send(.contactSelected(contact)) {
-      $0.contact = ContactState(id: contact.id, dbContact: contact)
+      $0.contact = ContactComponent.State(id: contact.id, dbContact: contact)
     }
   }
 
   func testDismissContact() {
     let store = TestStore(
-      initialState: ContactsState(
-        contact: ContactState(
+      initialState: ContactsComponent.State(
+        contact: ContactComponent.State(
           id: "id".data(using: .utf8)!,
           dbContact: Contact(id: "id".data(using: .utf8)!)
         )
       ),
-      reducer: contactsReducer,
-      environment: .unimplemented
+      reducer: ContactsComponent()
     )
 
     store.send(.contactDismissed) {
@@ -98,23 +95,21 @@ final class ContactsFeatureTests: XCTestCase {
 
   func testSelectMyContact() {
     let store = TestStore(
-      initialState: ContactsState(),
-      reducer: contactsReducer,
-      environment: .unimplemented
+      initialState: ContactsComponent.State(),
+      reducer: ContactsComponent()
     )
 
     store.send(.myContactSelected) {
-      $0.myContact = MyContactState()
+      $0.myContact = MyContactComponent.State()
     }
   }
 
   func testDismissMyContact() {
     let store = TestStore(
-      initialState: ContactsState(
-        myContact: MyContactState()
+      initialState: ContactsComponent.State(
+        myContact: MyContactComponent.State()
       ),
-      reducer: contactsReducer,
-      environment: .unimplemented
+      reducer: ContactsComponent()
     )
 
     store.send(.myContactDismissed) {
diff --git a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeComponentTests.swift
similarity index 59%
rename from Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift
rename to Examples/xx-messenger/Tests/HomeFeatureTests/HomeComponentTests.swift
index 6c23ff8b718191771ce8dbf30ef4777cfd7303f9..3bd49a4283b9b4e535c40fb8c9279a5482aa3d16 100644
--- a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeComponentTests.swift
@@ -11,12 +11,11 @@ import XXMessengerClient
 import XXModels
 @testable import HomeFeature
 
-final class HomeFeatureTests: XCTestCase {
+final class HomeComponentTests: XCTestCase {
   func testMessengerStartUnregistered() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     var messengerDidStartWithTimeout: [Int] = []
@@ -24,17 +23,17 @@ final class HomeFeatureTests: XCTestCase {
     var messengerDidListenForMessages = 0
     var messengerDidStartFileTransfer = 0
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
-    store.environment.messenger.isConnected.run = { false }
-    store.environment.messenger.connect.run = { messengerDidConnect += 1 }
-    store.environment.messenger.isListeningForMessages.run = { false }
-    store.environment.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 }
-    store.environment.messenger.isFileTransferRunning.run = { false }
-    store.environment.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 }
-    store.environment.messenger.isLoggedIn.run = { false }
-    store.environment.messenger.isRegistered.run = { false }
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
+    store.dependencies.app.messenger.isConnected.run = { false }
+    store.dependencies.app.messenger.connect.run = { messengerDidConnect += 1 }
+    store.dependencies.app.messenger.isListeningForMessages.run = { false }
+    store.dependencies.app.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 }
+    store.dependencies.app.messenger.isFileTransferRunning.run = { false }
+    store.dependencies.app.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 }
+    store.dependencies.app.messenger.isLoggedIn.run = { false }
+    store.dependencies.app.messenger.isRegistered.run = { false }
 
     store.send(.messenger(.start))
 
@@ -45,15 +44,14 @@ final class HomeFeatureTests: XCTestCase {
 
     store.receive(.networkMonitor(.stop))
     store.receive(.messenger(.didStartUnregistered)) {
-      $0.register = RegisterState()
+      $0.register = RegisterComponent.State()
     }
   }
 
   func testMessengerStartRegistered() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     var messengerDidStartWithTimeout: [Int] = []
@@ -63,21 +61,21 @@ final class HomeFeatureTests: XCTestCase {
     var messengerDidLogIn = 0
     var messengerDidResumeBackup = 0
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
-    store.environment.messenger.isConnected.run = { false }
-    store.environment.messenger.connect.run = { messengerDidConnect += 1 }
-    store.environment.messenger.isListeningForMessages.run = { false }
-    store.environment.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 }
-    store.environment.messenger.isFileTransferRunning.run = { false }
-    store.environment.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 }
-    store.environment.messenger.isLoggedIn.run = { false }
-    store.environment.messenger.isRegistered.run = { true }
-    store.environment.messenger.logIn.run = { messengerDidLogIn += 1 }
-    store.environment.messenger.isBackupRunning.run = { false }
-    store.environment.messenger.resumeBackup.run = { messengerDidResumeBackup += 1 }
-    store.environment.messenger.cMix.get = {
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
+    store.dependencies.app.messenger.isConnected.run = { false }
+    store.dependencies.app.messenger.connect.run = { messengerDidConnect += 1 }
+    store.dependencies.app.messenger.isListeningForMessages.run = { false }
+    store.dependencies.app.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 }
+    store.dependencies.app.messenger.isFileTransferRunning.run = { false }
+    store.dependencies.app.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 }
+    store.dependencies.app.messenger.isLoggedIn.run = { false }
+    store.dependencies.app.messenger.isRegistered.run = { true }
+    store.dependencies.app.messenger.logIn.run = { messengerDidLogIn += 1 }
+    store.dependencies.app.messenger.isBackupRunning.run = { false }
+    store.dependencies.app.messenger.resumeBackup.run = { messengerDidResumeBackup += 1 }
+    store.dependencies.app.messenger.cMix.get = {
       var cMix: CMix = .unimplemented
       cMix.addHealthCallback.run = { _ in Cancellable {} }
       cMix.getNodeRegistrationStatus.run = {
@@ -105,27 +103,26 @@ final class HomeFeatureTests: XCTestCase {
 
   func testRegisterFinished() {
     let store = TestStore(
-      initialState: HomeState(
-        register: RegisterState()
+      initialState: HomeComponent.State(
+        register: RegisterComponent.State()
       ),
-      reducer: homeReducer,
-      environment: .unimplemented
+      reducer: HomeComponent()
     )
 
     var messengerDidStartWithTimeout: [Int] = []
     var messengerDidLogIn = 0
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
-    store.environment.messenger.isConnected.run = { true }
-    store.environment.messenger.isListeningForMessages.run = { true }
-    store.environment.messenger.isFileTransferRunning.run = { true }
-    store.environment.messenger.isLoggedIn.run = { false }
-    store.environment.messenger.isRegistered.run = { true }
-    store.environment.messenger.logIn.run = { messengerDidLogIn += 1 }
-    store.environment.messenger.isBackupRunning.run = { true }
-    store.environment.messenger.cMix.get = {
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
+    store.dependencies.app.messenger.isConnected.run = { true }
+    store.dependencies.app.messenger.isListeningForMessages.run = { true }
+    store.dependencies.app.messenger.isFileTransferRunning.run = { true }
+    store.dependencies.app.messenger.isLoggedIn.run = { false }
+    store.dependencies.app.messenger.isRegistered.run = { true }
+    store.dependencies.app.messenger.logIn.run = { messengerDidLogIn += 1 }
+    store.dependencies.app.messenger.isBackupRunning.run = { true }
+    store.dependencies.app.messenger.cMix.get = {
       var cMix: CMix = .unimplemented
       cMix.addHealthCallback.run = { _ in Cancellable {} }
       cMix.getNodeRegistrationStatus.run = {
@@ -153,17 +150,16 @@ final class HomeFeatureTests: XCTestCase {
 
   func testMessengerStartFailure() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     struct Failure: Error {}
     let error = Failure()
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.start.run = { _ in throw error }
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.start.run = { _ in throw error }
 
     store.send(.messenger(.start))
 
@@ -175,19 +171,18 @@ final class HomeFeatureTests: XCTestCase {
 
   func testMessengerStartConnectFailure() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     struct Failure: Error {}
     let error = Failure()
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.start.run = { _ in }
-    store.environment.messenger.isConnected.run = { false }
-    store.environment.messenger.connect.run = { throw error }
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.start.run = { _ in }
+    store.dependencies.app.messenger.isConnected.run = { false }
+    store.dependencies.app.messenger.connect.run = { throw error }
 
     store.send(.messenger(.start))
 
@@ -199,22 +194,21 @@ final class HomeFeatureTests: XCTestCase {
 
   func testMessengerStartIsRegisteredFailure() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     struct Failure: Error {}
     let error = Failure()
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.start.run = { _ in }
-    store.environment.messenger.isConnected.run = { true }
-    store.environment.messenger.isListeningForMessages.run = { true }
-    store.environment.messenger.isFileTransferRunning.run = { true }
-    store.environment.messenger.isLoggedIn.run = { false }
-    store.environment.messenger.isRegistered.run = { throw error }
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.start.run = { _ in }
+    store.dependencies.app.messenger.isConnected.run = { true }
+    store.dependencies.app.messenger.isListeningForMessages.run = { true }
+    store.dependencies.app.messenger.isFileTransferRunning.run = { true }
+    store.dependencies.app.messenger.isLoggedIn.run = { false }
+    store.dependencies.app.messenger.isRegistered.run = { throw error }
 
     store.send(.messenger(.start))
 
@@ -226,23 +220,22 @@ final class HomeFeatureTests: XCTestCase {
 
   func testMessengerStartLogInFailure() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     struct Failure: Error {}
     let error = Failure()
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.start.run = { _ in }
-    store.environment.messenger.isConnected.run = { true }
-    store.environment.messenger.isListeningForMessages.run = { true }
-    store.environment.messenger.isFileTransferRunning.run = { true }
-    store.environment.messenger.isLoggedIn.run = { false }
-    store.environment.messenger.isRegistered.run = { true }
-    store.environment.messenger.logIn.run = { throw error }
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.start.run = { _ in }
+    store.dependencies.app.messenger.isConnected.run = { true }
+    store.dependencies.app.messenger.isListeningForMessages.run = { true }
+    store.dependencies.app.messenger.isFileTransferRunning.run = { true }
+    store.dependencies.app.messenger.isLoggedIn.run = { false }
+    store.dependencies.app.messenger.isRegistered.run = { true }
+    store.dependencies.app.messenger.logIn.run = { throw error }
 
     store.send(.messenger(.start))
 
@@ -254,9 +247,8 @@ final class HomeFeatureTests: XCTestCase {
 
   func testNetworkMonitorStart() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     let bgQueue = DispatchQueue.test
@@ -271,9 +263,9 @@ final class HomeFeatureTests: XCTestCase {
       .init(registered: 2, total: 12),
     ]
 
-    store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
-    store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
-    store.environment.messenger.cMix.get = {
+    store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
+    store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
+    store.dependencies.app.messenger.cMix.get = {
       var cMix: CMix = .unimplemented
       cMix.addHealthCallback.run = { callback in
         cMixDidAddHealthCallback.append(callback)
@@ -339,9 +331,8 @@ final class HomeFeatureTests: XCTestCase {
 
   func testAccountDeletion() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     var dbDidFetchContacts: [XXModels.Contact.Query] = []
@@ -349,9 +340,9 @@ final class HomeFeatureTests: XCTestCase {
     var messengerDidDestroy = 0
     var didRemoveDB = 0
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
@@ -360,7 +351,7 @@ final class HomeFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.dbManager.getDB.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContacts.run = { query in
         dbDidFetchContacts.append(query)
@@ -374,17 +365,17 @@ final class HomeFeatureTests: XCTestCase {
       }
       return db
     }
-    store.environment.dbManager.removeDB.run = {
+    store.dependencies.app.dbManager.removeDB.run = {
       didRemoveDB += 1
     }
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.permanentDeleteAccount.run = { usernameFact in
         udDidPermanentDeleteAccount.append(usernameFact)
       }
       return ud
     }
-    store.environment.messenger.destroy.run = {
+    store.dependencies.app.messenger.destroy.run = {
       messengerDidDestroy += 1
     }
 
@@ -412,17 +403,16 @@ final class HomeFeatureTests: XCTestCase {
 
   func testAccountDeletionFailure() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     struct Failure: Error {}
     let error = Failure()
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
@@ -444,11 +434,10 @@ final class HomeFeatureTests: XCTestCase {
 
   func testDidDismissAlert() {
     let store = TestStore(
-      initialState: HomeState(
+      initialState: HomeComponent.State(
         alert: AlertState(title: TextState(""))
       ),
-      reducer: homeReducer,
-      environment: .unimplemented
+      reducer: HomeComponent()
     )
 
     store.send(.didDismissAlert) {
@@ -458,11 +447,10 @@ final class HomeFeatureTests: XCTestCase {
 
   func testDidDismissRegister() {
     let store = TestStore(
-      initialState: HomeState(
-        register: RegisterState()
+      initialState: HomeComponent.State(
+        register: RegisterComponent.State()
       ),
-      reducer: homeReducer,
-      environment: .unimplemented
+      reducer: HomeComponent()
     )
 
     store.send(.didDismissRegister) {
@@ -472,23 +460,21 @@ final class HomeFeatureTests: XCTestCase {
 
   func testUserSearchButtonTapped() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     store.send(.userSearchButtonTapped) {
-      $0.userSearch = UserSearchState()
+      $0.userSearch = UserSearchComponent.State()
     }
   }
 
   func testDidDismissUserSearch() {
     let store = TestStore(
-      initialState: HomeState(
-        userSearch: UserSearchState()
+      initialState: HomeComponent.State(
+        userSearch: UserSearchComponent.State()
       ),
-      reducer: homeReducer,
-      environment: .unimplemented
+      reducer: HomeComponent()
     )
 
     store.send(.didDismissUserSearch) {
@@ -498,23 +484,21 @@ final class HomeFeatureTests: XCTestCase {
 
   func testContactsButtonTapped() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     store.send(.contactsButtonTapped) {
-      $0.contacts = ContactsState()
+      $0.contacts = ContactsComponent.State()
     }
   }
 
   func testDidDismissContacts() {
     let store = TestStore(
-      initialState: HomeState(
-        contacts: ContactsState()
+      initialState: HomeComponent.State(
+        contacts: ContactsComponent.State()
       ),
-      reducer: homeReducer,
-      environment: .unimplemented
+      reducer: HomeComponent()
     )
 
     store.send(.didDismissContacts) {
@@ -524,23 +508,21 @@ final class HomeFeatureTests: XCTestCase {
 
   func testBackupButtonTapped() {
     let store = TestStore(
-      initialState: HomeState(),
-      reducer: homeReducer,
-      environment: .unimplemented
+      initialState: HomeComponent.State(),
+      reducer: HomeComponent()
     )
 
     store.send(.backupButtonTapped) {
-      $0.backup = BackupState()
+      $0.backup = BackupComponent.State()
     }
   }
 
   func testDidDismissBackup() {
     let store = TestStore(
-      initialState: HomeState(
-        backup: BackupState()
+      initialState: HomeComponent.State(
+        backup: BackupComponent.State()
       ),
-      reducer: homeReducer,
-      environment: .unimplemented
+      reducer: HomeComponent()
     )
 
     store.send(.didDismissBackup) {
diff --git a/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactFeatureTests.swift b/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactComponentTests.swift
similarity index 80%
rename from Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactFeatureTests.swift
rename to Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactComponentTests.swift
index 830e97156363b151fbff21276169d8dfc2095720..358db1c2bd3a6633072249d10d6efacf245366f3 100644
--- a/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactComponentTests.swift
@@ -7,22 +7,21 @@ import XXMessengerClient
 import XXModels
 @testable import MyContactFeature
 
-final class MyContactFeatureTests: XCTestCase {
+final class MyContactComponentTests: XCTestCase {
   func testStart() {
     let contactId = "contact-id".data(using: .utf8)!
 
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
     var dbDidFetchContacts: [XXModels.Contact.Query] = []
     let dbContactsPublisher = PassthroughSubject<[XXModels.Contact], Error>()
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -31,7 +30,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContactsPublisher.run = { query in
         dbDidFetchContacts.append(query)
@@ -65,14 +64,13 @@ final class MyContactFeatureTests: XCTestCase {
     var didSendRegisterFact: [Fact] = []
 
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.sendRegisterFact.run = { fact in
         didSendRegisterFact.append(fact)
@@ -110,14 +108,13 @@ final class MyContactFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.sendRegisterFact.run = { _ in throw failure }
       return ud
@@ -149,17 +146,16 @@ final class MyContactFeatureTests: XCTestCase {
     var didSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         email: email,
         emailConfirmationID: confirmationID
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.confirmFact.run = { id, code in
         didConfirmWithID.append(id)
@@ -167,7 +163,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return ud
     }
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -176,7 +172,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContacts.run = { query in
         didFetchContacts.append(query)
@@ -228,16 +224,15 @@ final class MyContactFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         emailConfirmationID: "123"
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.confirmFact.run = { _, _ in throw failure }
       return ud
@@ -266,21 +261,20 @@ final class MyContactFeatureTests: XCTestCase {
     var didSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         contact: dbContact
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.removeFact.run = { didRemoveFact.append($0) }
       return ud
     }
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -289,7 +283,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContacts.run = { query in
         didFetchContacts.append(query)
@@ -322,16 +316,15 @@ final class MyContactFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         contact: .init(id: Data(), email: "test@email.com")
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.removeFact.run = { _ in throw failure }
       return ud
@@ -357,14 +350,13 @@ final class MyContactFeatureTests: XCTestCase {
     var didSendRegisterFact: [Fact] = []
 
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.sendRegisterFact.run = { fact in
         didSendRegisterFact.append(fact)
@@ -402,14 +394,13 @@ final class MyContactFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.sendRegisterFact.run = { _ in throw failure }
       return ud
@@ -441,17 +432,16 @@ final class MyContactFeatureTests: XCTestCase {
     var didSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         phone: phone,
         phoneConfirmationID: confirmationID
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.confirmFact.run = { id, code in
         didConfirmWithID.append(id)
@@ -459,7 +449,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return ud
     }
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -468,7 +458,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContacts.run = { query in
         didFetchContacts.append(query)
@@ -520,16 +510,15 @@ final class MyContactFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         phoneConfirmationID: "123"
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.confirmFact.run = { _, _ in throw failure }
       return ud
@@ -558,21 +547,20 @@ final class MyContactFeatureTests: XCTestCase {
     var didSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         contact: dbContact
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.removeFact.run = { didRemoveFact.append($0) }
       return ud
     }
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -581,7 +569,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContacts.run = { query in
         didFetchContacts.append(query)
@@ -614,16 +602,15 @@ final class MyContactFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: MyContactState(
+      initialState: MyContactComponent.State(
         contact: .init(id: Data(), phone: "+123456789")
       ),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.removeFact.run = { _ in throw failure }
       return ud
@@ -653,14 +640,13 @@ final class MyContactFeatureTests: XCTestCase {
     var didSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -669,7 +655,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.getFacts.run = {
         [
@@ -680,7 +666,7 @@ final class MyContactFeatureTests: XCTestCase {
       }
       return ud
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContacts.run = { query in
         didFetchContacts.append(query)
@@ -714,14 +700,13 @@ final class MyContactFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -746,9 +731,8 @@ final class MyContactFeatureTests: XCTestCase {
 
   func testErrorAlert() {
     let store = TestStore(
-      initialState: MyContactState(),
-      reducer: myContactReducer,
-      environment: .unimplemented
+      initialState: MyContactComponent.State(),
+      reducer: MyContactComponent()
     )
 
     let failure = "Something went wrong"
diff --git a/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterComponentTests.swift
similarity index 76%
rename from Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift
rename to Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterComponentTests.swift
index e0ffc5ef4fe9a3bc5f9d9f91d288025cd1065c85..0be4647e1ffa87e38cec2d505e472548ad4a81b8 100644
--- a/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/RegisterFeatureTests/RegisterComponentTests.swift
@@ -6,7 +6,7 @@ import XXMessengerClient
 import XXModels
 @testable import RegisterFeature
 
-final class RegisterFeatureTests: XCTestCase {
+final class RegisterComponentTests: XCTestCase {
   func testRegister() throws {
     let now = Date()
     let username = "registering-username"
@@ -26,24 +26,23 @@ final class RegisterFeatureTests: XCTestCase {
     var dbDidSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: RegisterState(),
-      reducer: registerReducer,
-      environment: .unimplemented
+      initialState: RegisterComponent.State(),
+      reducer: RegisterComponent()
     )
-    store.environment.now = { now }
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.register.run = { username in
+    store.dependencies.app.now = { now }
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.register.run = { username in
       messengerDidRegisterUsername.append(username)
     }
-    store.environment.messenger.myContact.run = { includeFacts in
+    store.dependencies.app.messenger.myContact.run = { includeFacts in
       didGetMyContact.append(includeFacts)
       var contact = XXClient.Contact.unimplemented(myContactData)
       contact.getIdFromContact.run = { _ in myContactId }
       contact.getFactsFromContact.run = { _ in myContactFacts }
       return contact
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.saveContact.run = { contact in
         dbDidSaveContact.append(contact)
@@ -87,17 +86,16 @@ final class RegisterFeatureTests: XCTestCase {
     let error = Error()
 
     let store = TestStore(
-      initialState: RegisterState(),
-      reducer: registerReducer,
-      environment: .unimplemented
+      initialState: RegisterComponent.State(),
+      reducer: RegisterComponent()
     )
 
     let mainQueue = DispatchQueue.test
     let bgQueue = DispatchQueue.test
 
-    store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
-    store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
-    store.environment.db.run = { throw error }
+    store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
+    store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
+    store.dependencies.app.dbManager.getDB.run = { throw error }
 
     store.send(.registerTapped) {
       $0.isRegistering = true
@@ -117,18 +115,17 @@ final class RegisterFeatureTests: XCTestCase {
     let error = Error()
 
     let store = TestStore(
-      initialState: RegisterState(),
-      reducer: registerReducer,
-      environment: .unimplemented
+      initialState: RegisterComponent.State(),
+      reducer: RegisterComponent()
     )
 
     let mainQueue = DispatchQueue.test
     let bgQueue = DispatchQueue.test
 
-    store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
-    store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
-    store.environment.db.run = { .unimplemented }
-    store.environment.messenger.register.run = { _ in throw error }
+    store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
+    store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
+    store.dependencies.app.dbManager.getDB.run = { .unimplemented }
+    store.dependencies.app.messenger.register.run = { _ in throw error }
 
     store.send(.registerTapped) {
       $0.isRegistering = true
@@ -162,26 +159,25 @@ final class RegisterFeatureTests: XCTestCase {
     var dbDidSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: RegisterState(
+      initialState: RegisterComponent.State(
         username: username
       ),
-      reducer: registerReducer,
-      environment: .unimplemented
+      reducer: RegisterComponent()
     )
-    store.environment.now = { now }
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.register.run = { username in
+    store.dependencies.app.now = { now }
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.register.run = { username in
       messengerDidRegisterUsername.append(username)
     }
-    store.environment.messenger.myContact.run = { includeFacts in
+    store.dependencies.app.messenger.myContact.run = { includeFacts in
       didGetMyContact.append(includeFacts)
       var contact = XXClient.Contact.unimplemented(myContactData)
       contact.getIdFromContact.run = { _ in myContactId }
       contact.getFactsFromContact.run = { _ in myContactFacts }
       return contact
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.saveContact.run = { contact in
         dbDidSaveContact.append(contact)
@@ -207,7 +203,7 @@ final class RegisterFeatureTests: XCTestCase {
       )
     ])
 
-    let failure = RegisterState.Error.usernameMismatch(
+    let failure = RegisterComponent.State.Error.usernameMismatch(
       registering: username,
       registered: myContactUsername
     )
diff --git a/Examples/xx-messenger/Tests/ResetAuthFeatureTests/ResetAuthFeatureTests.swift b/Examples/xx-messenger/Tests/ResetAuthFeatureTests/ResetAuthComponentTests.swift
similarity index 71%
rename from Examples/xx-messenger/Tests/ResetAuthFeatureTests/ResetAuthFeatureTests.swift
rename to Examples/xx-messenger/Tests/ResetAuthFeatureTests/ResetAuthComponentTests.swift
index 46b5dc9e402d5c2cb71a796790b205732876a658..03ec62686846d64a50ede08c39bc25255cc64558 100644
--- a/Examples/xx-messenger/Tests/ResetAuthFeatureTests/ResetAuthFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/ResetAuthFeatureTests/ResetAuthComponentTests.swift
@@ -4,7 +4,7 @@ import XCTest
 import XXClient
 @testable import ResetAuthFeature
 
-final class ResetAuthFeatureTests: XCTestCase {
+final class ResetAuthComponentTests: XCTestCase {
   func testReset() {
     let partnerData = "contact-data".data(using: .utf8)!
     let partner = Contact.unimplemented(partnerData)
@@ -12,15 +12,14 @@ final class ResetAuthFeatureTests: XCTestCase {
     var didResetAuthChannel: [Contact] = []
 
     let store = TestStore(
-      initialState: ResetAuthState(
+      initialState: ResetAuthComponent.State(
         partner: partner
       ),
-      reducer: resetAuthReducer,
-      environment: .unimplemented
+      reducer: ResetAuthComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.resetAuthenticatedChannel.run = { contact in
         didResetAuthChannel.append(contact)
@@ -46,15 +45,14 @@ final class ResetAuthFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: ResetAuthState(
+      initialState: ResetAuthComponent.State(
         partner: .unimplemented(Data())
       ),
-      reducer: resetAuthReducer,
-      environment: .unimplemented
+      reducer: ResetAuthComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.resetAuthenticatedChannel.run = { _ in throw failure }
       return e2e
diff --git a/Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreFeatureTests.swift b/Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreComponentTests.swift
similarity index 79%
rename from Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreFeatureTests.swift
rename to Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreComponentTests.swift
index 716d7650255b277cad37fb40ad5a73c4aee5bace..8a15fb747fb0124bd972593a8a81233d38c4c89c 100644
--- a/Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/RestoreFeatureTests/RestoreComponentTests.swift
@@ -6,19 +6,18 @@ import XXMessengerClient
 import XXModels
 @testable import RestoreFeature
 
-final class RestoreFeatureTests: XCTestCase {
+final class RestoreComponentTests: XCTestCase {
   func testFileImport() {
     let fileURL = URL(string: "file-url")!
     var didLoadDataFromURL: [URL] = []
     let dataFromURL = "data-from-url".data(using: .utf8)!
 
     let store = TestStore(
-      initialState: RestoreState(),
-      reducer: restoreReducer,
-      environment: .unimplemented
+      initialState: RestoreComponent.State(),
+      reducer: RestoreComponent()
     )
 
-    store.environment.loadData.load = { url in
+    store.dependencies.app.loadData.load = { url in
       didLoadDataFromURL.append(url)
       return dataFromURL
     }
@@ -41,11 +40,10 @@ final class RestoreFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: RestoreState(
+      initialState: RestoreComponent.State(
         isImportingFile: true
       ),
-      reducer: restoreReducer,
-      environment: .unimplemented
+      reducer: RestoreComponent()
     )
 
     store.send(.fileImport(.failure(failure as NSError))) {
@@ -60,14 +58,13 @@ final class RestoreFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: RestoreState(
+      initialState: RestoreComponent.State(
         isImportingFile: true
       ),
-      reducer: restoreReducer,
-      environment: .unimplemented
+      reducer: RestoreComponent()
     )
 
-    store.environment.loadData.load = { _ in throw failure }
+    store.dependencies.app.loadData.load = { _ in throw failure }
 
     store.send(.fileImport(.success(URL(string: "test")!))) {
       $0.isImportingFile = false
@@ -102,23 +99,22 @@ final class RestoreFeatureTests: XCTestCase {
     var didSaveContact: [XXModels.Contact] = []
 
     let store = TestStore(
-      initialState: RestoreState(
+      initialState: RestoreComponent.State(
         file: .init(name: "file-name", data: backupData)
       ),
-      reducer: restoreReducer,
-      environment: .unimplemented
+      reducer: RestoreComponent()
     )
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.now = { now }
-    store.environment.messenger.restoreBackup.run = { data, passphrase in
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.now = { now }
+    store.dependencies.app.messenger.restoreBackup.run = { data, passphrase in
       didRestoreWithData.append(data)
       didRestoreWithPassphrase.append(passphrase)
       udFacts = restoredFacts
       return restoreResult
     }
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.getContact.run = {
         var contact: XXClient.Contact = .unimplemented(Data())
@@ -127,12 +123,12 @@ final class RestoreFeatureTests: XCTestCase {
       }
       return e2e
     }
-    store.environment.messenger.ud.get = {
+    store.dependencies.app.messenger.ud.get = {
       var ud: UserDiscovery = .unimplemented
       ud.getFacts.run = { udFacts }
       return ud
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.fetchContacts.run = { query in
         didFetchContacts.append(query)
@@ -189,11 +185,10 @@ final class RestoreFeatureTests: XCTestCase {
 
   func testRestoreWithoutFile() {
     let store = TestStore(
-      initialState: RestoreState(
+      initialState: RestoreComponent.State(
         file: nil
       ),
-      reducer: restoreReducer,
-      environment: .unimplemented
+      reducer: RestoreComponent()
     )
 
     store.send(.restoreTapped)
@@ -206,17 +201,16 @@ final class RestoreFeatureTests: XCTestCase {
     }
 
     let store = TestStore(
-      initialState: RestoreState(
+      initialState: RestoreComponent.State(
         file: .init(name: "name", data: "data".data(using: .utf8)!)
       ),
-      reducer: restoreReducer,
-      environment: .unimplemented
+      reducer: RestoreComponent()
     )
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.restoreBackup.run = { _, _ in throw Failure.restore }
-    store.environment.messenger.destroy.run = { throw Failure.destroy }
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.restoreBackup.run = { _, _ in throw Failure.restore }
+    store.dependencies.app.messenger.destroy.run = { throw Failure.destroy }
 
     store.send(.restoreTapped) {
       $0.isRestoring = true
diff --git a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestComponentTests.swift
similarity index 78%
rename from Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift
rename to Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestComponentTests.swift
index 4c68abab06a609346b47b4fe472d5f377417c2f5..ee2b668f779afcfd1ede6c6e0c42799d3a0be8b8 100644
--- a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestComponentTests.swift
@@ -7,22 +7,21 @@ import XXMessengerClient
 import XXModels
 @testable import SendRequestFeature
 
-final class SendRequestFeatureTests: XCTestCase {
+final class SendRequestComponentTests: XCTestCase {
   func testStart() {
     let myContact = XXClient.Contact.unimplemented("my-contact-data".data(using: .utf8)!)
 
     var didGetMyContact: [MessengerMyContact.IncludeFacts?] = []
 
     let store = TestStore(
-      initialState: SendRequestState(
+      initialState: SendRequestComponent.State(
         contact: .unimplemented("contact-data".data(using: .utf8)!)
       ),
-      reducer: sendRequestReducer,
-      environment: .unimplemented
+      reducer: SendRequestComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.myContact.run = { includeFacts in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.myContact.run = { includeFacts in
       didGetMyContact.append(includeFacts)
       return myContact
     }
@@ -39,15 +38,14 @@ final class SendRequestFeatureTests: XCTestCase {
     let failure = Failure()
 
     let store = TestStore(
-      initialState: SendRequestState(
+      initialState: SendRequestComponent.State(
         contact: .unimplemented("contact-data".data(using: .utf8)!)
       ),
-      reducer: sendRequestReducer,
-      environment: .unimplemented
+      reducer: SendRequestComponent()
     )
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.myContact.run = { _ in throw failure }
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.myContact.run = { _ in throw failure }
 
     store.send(.start)
 
@@ -70,12 +68,11 @@ final class SendRequestFeatureTests: XCTestCase {
     myContact.getFactsFromContact.run = { _ in myFacts }
 
     let store = TestStore(
-      initialState: SendRequestState(
+      initialState: SendRequestComponent.State(
         contact: contact,
         myContact: myContact
       ),
-      reducer: sendRequestReducer,
-      environment: .unimplemented
+      reducer: SendRequestComponent()
     )
 
     struct DidBulkUpdateContacts: Equatable {
@@ -90,9 +87,9 @@ final class SendRequestFeatureTests: XCTestCase {
     var didBulkUpdateContacts: [DidBulkUpdateContacts] = []
     var didRequestAuthChannel: [DidRequestAuthChannel] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.db.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContacts.append(.init(query: query, assignments: assignments))
@@ -100,7 +97,7 @@ final class SendRequestFeatureTests: XCTestCase {
       }
       return db
     }
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.requestAuthenticatedChannel.run = { partner, myFacts in
         didRequestAuthChannel.append(.init(partner: partner, myFacts: myFacts))
@@ -149,25 +146,24 @@ final class SendRequestFeatureTests: XCTestCase {
     myContact.getFactsFromContact.run = { _ in myFacts }
 
     let store = TestStore(
-      initialState: SendRequestState(
+      initialState: SendRequestComponent.State(
         contact: contact,
         myContact: myContact
       ),
-      reducer: sendRequestReducer,
-      environment: .unimplemented
+      reducer: SendRequestComponent()
     )
 
     struct Failure: Error {}
     let failure = Failure()
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.db.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { _, _ in return 0 }
       return db
     }
-    store.environment.messenger.e2e.get = {
+    store.dependencies.app.messenger.e2e.get = {
       var e2e: E2E = .unimplemented
       e2e.requestAuthenticatedChannel.run = { _, _ in throw failure }
       return e2e
diff --git a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchFeatureTests.swift b/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchComponentTests.swift
similarity index 81%
rename from Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchFeatureTests.swift
rename to Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchComponentTests.swift
index 33f1edb9d61d7186503b63f90c1f73f11251185f..97ca5faef6c7fbf65470e9416df77ca25ca9bab1 100644
--- a/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/UserSearchFeatureTests/UserSearchComponentTests.swift
@@ -6,12 +6,11 @@ import XXClient
 import XXMessengerClient
 @testable import UserSearchFeature
 
-final class UserSearchFeatureTests: XCTestCase {
+final class UserSearchComponentTests: XCTestCase {
   func testSearch() {
     let store = TestStore(
-      initialState: UserSearchState(),
-      reducer: userSearchReducer,
-      environment: .unimplemented
+      initialState: UserSearchComponent.State(),
+      reducer: UserSearchComponent()
     )
 
     var didSearchWithQuery: [MessengerSearchContacts.Query] = []
@@ -43,9 +42,9 @@ final class UserSearchFeatureTests: XCTestCase {
     contact4.getFactsFromContact.run = { _ in throw GetFactsFromContactError() }
     let contacts = [contact1, contact2, contact3, contact4]
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.searchContacts.run = { query in
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.searchContacts.run = { query in
       didSearchWithQuery.append(query)
       return contacts
     }
@@ -93,17 +92,16 @@ final class UserSearchFeatureTests: XCTestCase {
 
   func testSearchFailure() {
     let store = TestStore(
-      initialState: UserSearchState(),
-      reducer: userSearchReducer,
-      environment: .unimplemented
+      initialState: UserSearchComponent.State(),
+      reducer: UserSearchComponent()
     )
 
     struct Failure: Error {}
     let failure = Failure()
 
-    store.environment.bgQueue = .immediate
-    store.environment.mainQueue = .immediate
-    store.environment.messenger.searchContacts.run = { _ in throw failure }
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.messenger.searchContacts.run = { _ in throw failure }
 
     store.send(.searchTapped) {
       $0.focusedField = nil
@@ -121,7 +119,7 @@ final class UserSearchFeatureTests: XCTestCase {
 
   func testResultTapped() {
     let store = TestStore(
-      initialState: UserSearchState(
+      initialState: UserSearchComponent.State(
         results: [
           .init(
             id: "contact-id".data(using: .utf8)!,
@@ -129,12 +127,11 @@ final class UserSearchFeatureTests: XCTestCase {
           )
         ]
       ),
-      reducer: userSearchReducer,
-      environment: .unimplemented
+      reducer: UserSearchComponent()
     )
 
     store.send(.resultTapped(id: "contact-id".data(using: .utf8)!)) {
-      $0.contact = ContactState(
+      $0.contact = ContactComponent.State(
         id: "contact-id".data(using: .utf8)!,
         xxContact: .unimplemented("contact-data".data(using: .utf8)!)
       )
@@ -143,13 +140,12 @@ final class UserSearchFeatureTests: XCTestCase {
 
   func testDismissingContact() {
     let store = TestStore(
-      initialState: UserSearchState(
-        contact: ContactState(
+      initialState: UserSearchComponent.State(
+        contact: ContactComponent.State(
           id: "contact-id".data(using: .utf8)!
         )
       ),
-      reducer: userSearchReducer,
-      environment: .unimplemented
+      reducer: UserSearchComponent()
     )
 
     store.send(.didDismissContact) {
diff --git a/Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactFeatureTests.swift b/Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactComponentTests.swift
similarity index 80%
rename from Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactFeatureTests.swift
rename to Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactComponentTests.swift
index ceaa61cc3f215234aea66381b4c7dc17965bb1a4..c979ceaa572be2bcf9e4c0326763cf7f285db150 100644
--- a/Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/VerifyContactFeatureTests/VerifyContactComponentTests.swift
@@ -5,31 +5,30 @@ import XXClient
 import XXModels
 @testable import VerifyContactFeature
 
-final class VerifyContactFeatureTests: XCTestCase {
+final class VerifyContactComponentTests: XCTestCase {
   func testVerify() {
     var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
     let contactId = "contact-id".data(using: .utf8)!
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: VerifyContactState(
+      initialState: VerifyContactComponent.State(
         contact: contact
       ),
-      reducer: verifyContactReducer,
-      environment: .unimplemented
+      reducer: VerifyContactComponent()
     )
 
     var didVerifyContact: [XXClient.Contact] = []
     var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
     var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.verifyContact.run = { contact in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.verifyContact.run = { contact in
       didVerifyContact.append(contact)
       return true
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContactsWithQuery.append(query)
@@ -66,24 +65,23 @@ final class VerifyContactFeatureTests: XCTestCase {
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: VerifyContactState(
+      initialState: VerifyContactComponent.State(
         contact: contact
       ),
-      reducer: verifyContactReducer,
-      environment: .unimplemented
+      reducer: VerifyContactComponent()
     )
 
     var didVerifyContact: [XXClient.Contact] = []
     var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
     var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.verifyContact.run = { contact in
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.verifyContact.run = { contact in
       didVerifyContact.append(contact)
       return false
     }
-    store.environment.db.run = {
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContactsWithQuery.append(query)
@@ -120,11 +118,10 @@ final class VerifyContactFeatureTests: XCTestCase {
     contact.getIdFromContact.run = { _ in contactId }
 
     let store = TestStore(
-      initialState: VerifyContactState(
+      initialState: VerifyContactComponent.State(
         contact: contact
       ),
-      reducer: verifyContactReducer,
-      environment: .unimplemented
+      reducer: VerifyContactComponent()
     )
 
     struct Failure: Error {}
@@ -133,10 +130,10 @@ final class VerifyContactFeatureTests: XCTestCase {
     var didBulkUpdateContactsWithQuery: [XXModels.Contact.Query] = []
     var didBulkUpdateContactsWithAssignments: [XXModels.Contact.Assignments] = []
 
-    store.environment.mainQueue = .immediate
-    store.environment.bgQueue = .immediate
-    store.environment.messenger.verifyContact.run = { _ in throw error }
-    store.environment.db.run = {
+    store.dependencies.app.mainQueue = .immediate
+    store.dependencies.app.bgQueue = .immediate
+    store.dependencies.app.messenger.verifyContact.run = { _ in throw error }
+    store.dependencies.app.dbManager.getDB.run = {
       var db: Database = .unimplemented
       db.bulkUpdateContacts.run = { query, assignments in
         didBulkUpdateContactsWithQuery.append(query)
diff --git a/Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeFeatureTests.swift b/Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeComponentTests.swift
similarity index 60%
rename from Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeFeatureTests.swift
rename to Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeComponentTests.swift
index c6f23b7a6c885a0b8728796f6e3fd41f3a018aa1..4d2391d38cbfba47a3cdfcda70539b3f1a1bce16 100644
--- a/Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeFeatureTests.swift
+++ b/Examples/xx-messenger/Tests/WelcomeFeatureTests/WelcomeComponentTests.swift
@@ -2,8 +2,7 @@ import ComposableArchitecture
 import XCTest
 @testable import WelcomeFeature
 
-@MainActor
-final class WelcomeFeatureTests: XCTestCase {
+final class WelcomeComponentTests: XCTestCase {
   func testNewAccountTapped() {
     let mainQueue = DispatchQueue.test
     let bgQueue = DispatchQueue.test
@@ -11,14 +10,13 @@ final class WelcomeFeatureTests: XCTestCase {
     var didCreateMessenger = 0
 
     let store = TestStore(
-      initialState: WelcomeState(),
-      reducer: welcomeReducer,
-      environment: .unimplemented
+      initialState: WelcomeComponent.State(),
+      reducer: WelcomeComponent()
     )
 
-    store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
-    store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
-    store.environment.messenger.create.run = { didCreateMessenger += 1 }
+    store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
+    store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
+    store.dependencies.app.messenger.create.run = { didCreateMessenger += 1 }
 
     store.send(.newAccountTapped) {
       $0.isCreatingAccount = true
@@ -44,14 +42,13 @@ final class WelcomeFeatureTests: XCTestCase {
     let bgQueue = DispatchQueue.test
 
     let store = TestStore(
-      initialState: WelcomeState(),
-      reducer: welcomeReducer,
-      environment: .unimplemented
+      initialState: WelcomeComponent.State(),
+      reducer: WelcomeComponent()
     )
 
-    store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
-    store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
-    store.environment.messenger.create.run = { throw failure }
+    store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
+    store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
+    store.dependencies.app.messenger.create.run = { throw failure }
 
     store.send(.newAccountTapped) {
       $0.isCreatingAccount = true
@@ -69,9 +66,8 @@ final class WelcomeFeatureTests: XCTestCase {
 
   func testRestore() {
     let store = TestStore(
-      initialState: WelcomeState(),
-      reducer: welcomeReducer,
-      environment: .unimplemented
+      initialState: WelcomeComponent.State(),
+      reducer: WelcomeComponent()
     )
 
     store.send(.restoreTapped)
diff --git a/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 482d554fe8adf685c8dc7ce64c3f7d09ccbf25e0..4626e31d8eb62969ebd67d9f70afd2f184787bd0 100644
--- a/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -23,8 +23,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/groue/GRDB.swift",
       "state" : {
-        "revision" : "0ac435744a4c67c4ec23a4a671c0d53ce1fee7c6",
-        "version" : "6.0.0"
+        "revision" : "13e1f4d7c2896a6a9293102f664e5311e017ffb2",
+        "version" : "6.1.0"
       }
     },
     {
@@ -68,8 +68,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/pointfreeco/swift-composable-architecture.git",
       "state" : {
-        "revision" : "5c476994eaa79af8e466041f6de1ab116f37c528",
-        "version" : "0.42.0"
+        "revision" : "5bd450a8ac6a802f82d485bac219cbfacffa69fb",
+        "version" : "0.43.0"
       }
     },
     {
@@ -77,8 +77,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/darrarski/swift-composable-presentation.git",
       "state" : {
-        "revision" : "bdb7df9476cf29e8379fc50aa03848dd6c8033d9",
-        "version" : "0.5.3"
+        "revision" : "f69eb0c9a82832f67dfd5dace98e6d0e8d748b0f",
+        "version" : "0.6.0"
       }
     },
     {
@@ -113,8 +113,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay.git",
       "state" : {
-        "revision" : "30314f1ece684dd60679d598a9b89107557b67d9",
-        "version" : "0.4.1"
+        "revision" : "16e6409ee82e1b81390bdffbf217b9c08ab32784",
+        "version" : "0.5.0"
       }
     }
   ],
diff --git a/Package.resolved b/Package.resolved
index eb44c7c2804b6d4b64f6ad27cdde3a8f42b6710b..d7760fa278975208f6d8594eac3dbd5c29784cbe 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -32,8 +32,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay.git",
       "state" : {
-        "revision" : "30314f1ece684dd60679d598a9b89107557b67d9",
-        "version" : "0.4.1"
+        "revision" : "16e6409ee82e1b81390bdffbf217b9c08ab32784",
+        "version" : "0.5.0"
       }
     }
   ],
diff --git a/Package.swift b/Package.swift
index cd25ca17a71b46a3f2dfe1d01374d552fb429475..b1d78d2adb57ef25fa0056fae812cc04b853cf7e 100644
--- a/Package.swift
+++ b/Package.swift
@@ -21,11 +21,11 @@ let package = Package(
   dependencies: [
     .package(
       url: "https://github.com/pointfreeco/swift-custom-dump.git",
-      .upToNextMajor(from: "0.5.2")
+      .upToNextMajor(from: "0.6.0")
     ),
     .package(
       url: "https://github.com/pointfreeco/xctest-dynamic-overlay.git",
-      .upToNextMajor(from: "0.4.1")
+      .upToNextMajor(from: "0.5.0")
     ),
     .package(
       url: "https://github.com/kishikawakatsumi/KeychainAccess.git",