We begin building the Composable Architecture by settling on the core types that make up the architecture. We want to use value types as much as possible, because they are inherently simple, and we want the types to be composable so that we can break large problems down into small ones. We achieve both of these goals with reducers, while delegating the messy runtime of our application to a unit known as a “store.”
When exploring the kinds of composition reducers supported we were inevitably led to an unintuitive concept known as “contravariance.” This form of composition goes in the opposite direction that you would typically expect. We first explored contravariance early on in Point-Free where it can be seen to be quite natural if you look at things the right way.
the Composable Architecture isn’t the first time we’ve distilled the essence of some functionality into a type, and then explored its compositional properties. We did the same when we explored parsing and randomness, and we were able to cook up some impressive examples of breaking large, complex problems into very simple units.
In this section we showed how to “pullback” reducers along key paths of state and actions. However, the pullback for actions didn’t seem quite right, primarily because it required code generation to get right. It turns out that our mistake was using key paths for actions, when there is a more appropriate tool to use that we call a “case path.” The following episodes introduce case paths from first principles, and then show how to refactor the Composable Architecture to take advantage of them.
Although we have shown that reducers and stores are simple and composable, we haven’t seen what that unlocks for us in our architecture. We will begin by showing that the Composable Architecture is super modular. We can break out each screen of our application into its own module, which means each screen can be built and run in complete isolation, without building any other part of the application.