@@ -69,6 +69,7 @@ It keeps surrounding helper layers intentionally small, so dependencies and feat
6969- [ Middleware] ( #middleware )
7070 - [ Logging] ( #logging )
7171 - [ Message] ( #message )
72+ - [ Project-specific AppStore Wrapper] ( #project-specific-appstore-wrapper )
7273- [ Testing Store] ( #testing-store )
7374
7475## Installation
@@ -207,7 +208,7 @@ class CounterStore(
207208 counterRepository : CounterRepository ,
208209): Store<CounterState, CounterAction, CounterEvent> by Store(
209210 initialState = CounterState (count = 0),
210- configure = {
211+ setup = {
211212 state<CounterState > {
212213 // ...
213214 }
@@ -886,6 +887,78 @@ val mainStore: Store<MainState, MainAction, MainEvent> = Store {
886887```
887888</details>
888889
890+ ## Project-specific AppStore Wrapper
891+
892+ In larger projects, it can be useful to wrap `Store(...)` in a project-specific `AppStore(...)`.
893+
894+ This allows you to centralize shared behavior such as common middleware, exception handling, and state persistence.
895+ It also gives you a place to prepare an extra setup hook for testing and debugging.
896+
897+ ```kt
898+ fun <S : State, A : Action, E : Event> AppStore(
899+ initialState: S,
900+ extraSetup: Setup<S, A, E> = {},
901+ setup: Setup<S, A, E>,
902+ ): Store<S, A, E> = Store(initialState) {
903+ middleware(AppLoggingMiddleware())
904+ exceptionHandler(AppExceptionHandler)
905+
906+ setup()
907+ extraSetup()
908+ }
909+ ```
910+
911+ A feature Store can then focus on its own state transitions and actions:
912+
913+ ```kt
914+ fun CounterStore(
915+ counterRepository: CounterRepository,
916+ extraSetup: Setup<CounterState, CounterAction, CounterEvent> = {},
917+ ): Store<CounterState, CounterAction, CounterEvent> = AppStore(
918+ initialState = CounterState(count = 0),
919+ extraSetup = extraSetup,
920+ ) {
921+ state<CounterState> {
922+ action<CounterAction.Increment> {
923+ val count = state.count + 1
924+ counterRepository.set(count)
925+ nextState(state.copy(count = count))
926+ }
927+ }
928+ }
929+ ```
930+
931+ For tests or debug builds, you can inject only the additional behavior you need:
932+
933+ ```kt
934+ val recordedEvents = mutableListOf<CounterEvent>()
935+
936+ val store = CounterStore(
937+ counterRepository = repository,
938+ extraSetup = {
939+ coroutineContext(testDispatcher)
940+ middleware(
941+ Middleware(
942+ afterEventEmit = { _, event ->
943+ recordedEvents += event
944+ },
945+ ),
946+ )
947+ },
948+ )
949+ ```
950+
951+ This pattern is useful for:
952+
953+ - applying project-wide middleware and exception handling
954+ - injecting test- or debug-only middleware
955+ - overriding `coroutineContext` in tests
956+ - keeping feature Store definitions focused on business logic
957+
958+ Avoid using `extraSetup` to redefine `state {}` or `anyState {}` handlers, because handler selection depends on registration order.
959+
960+ Also note that middleware execution order should not be relied on.
961+
889962## Testing Store
890963
891964Tart' s architecture makes writing unit tests for your * Store * straightforward.
0 commit comments