Layered architecture organizes a system into logical levels, each defined by a shared architectural purpose or level of abstraction. A layer is not a deployable unit: one component may span several layers, and one layer may contain several components. The point is to make responsibilities, boundaries, and allowed dependencies visible before implementation details take over. section 2.2.1 section 3.2
Typical responsibilities include application startup and configuration, service interfaces, persistence abstractions, presentation and input handling, domain modeling, and interoperability. In a functional design, the domain model is kept pure, while effects such as storage, networking, runtime setup, and user interfaces stay at explicit boundaries. section 2.2.1
Use layering to constrain how parts collaborate:
- Give each subsystem a clear, minimal interface; external code should use that interface rather than bypass it. section 4.3
- Examine dependencies between layers early, then rework dependencies that violate the intended boundaries. section 4.3
- Split broad layers into sublayers when doing so improves cohesion and lowers coupling without changing the whole architecture. section 4.2.1
- Reserve an application-level composition point for wiring layers together; boundaries do not mean that layers operate independently forever. section 7.1.1
A useful functional arrangement separates business logic, expressed against declarative interfaces, from implementations that perform operational work. The same design principles apply inside a large implementation layer: use single responsibility, interface segregation, low coupling, and high cohesion to divide hardware, database, or other service code into focused sublayers. section 4.2.1 section 4.3
A stateful sandwich application
A sandwich recipe forms the business-logic layer: it describes starting, adding ingredients, and finishing without owning mutable state. An interpreter implements those recipe operations and holds the ingredients reference. The main function creates that state, runs the interpreter, and prints the result, acting as the application layer that ties the other layers together. section 7.1.1
Keeping web logic out of the HTTP handler
Putting domain-aware database work directly in a Servant Handler mixes business logic with server code, making it imperative, easier to break, and harder to test. Moving the operation into AppL keeps it separate from the HTTP runtime; a ReaderT-based handler layer supplies Runtime and runs the AppL scenario at the boundary. section 14.2.2
Layers can also add safety rather than merely separate effects. A typed key-value interface can sit above a raw ByteString database interface, giving business logic typed data while leaving the existing underlying framework and interpreters unchanged. This makes layering an incremental tool: introduce a focused abstraction where an untyped boundary leaks too much risk. section 11.2
Figure 7.2 A layered free monadic architecture
This diagram visualizes the application layer composing a free-monadic business script with its interpreter and operational state. section 7.1.1






