Why architecture matters here
Akka HTTP matters because its streaming-first, actors-and-streams foundation gives it distinctive strengths for reactive, streaming HTTP -- and it's the natural HTTP choice for Akka-based systems. For systems already built on Akka (actors for concurrency, Akka Streams for data processing), Akka HTTP is the natural HTTP layer (same foundation, integrated) -- and its streaming-first nature (bodies as Akka Streams, backpressured) makes it strong for streaming workloads (large uploads/downloads, server-sent events, streaming APIs -- naturally backpressured). The routing DSL (composable directives) is productive for building HTTP APIs. For Akka-based reactive systems and streaming-heavy HTTP workloads, Akka HTTP is a strong choice, and understanding it (its streaming foundation, routing DSL, actor integration) is understanding the Akka ecosystem's HTTP layer. (Note: the Akka ecosystem's licensing changed, and Apache Pekko is the open-source fork -- Pekko HTTP being the equivalent; the architecture is the same.)
The streaming-first foundation is the architectural distinctive, and it's what makes Akka HTTP strong for streaming. Because Akka HTTP is built on Akka Streams, HTTP entities (request and response bodies) are Akka Streams (a stream of bytes) -- not materialized in memory by default, but streamed. This has powerful consequences. Streaming is natural: streaming a large response (a big download, a database export, server-sent events) is just returning a stream (the body as an Akka Stream) -- the framework streams it (not buffering it in memory) -- so large or unbounded responses are handled in constant memory (streamed, not materialized). Similarly, streaming request handling (processing a large upload as a stream) is natural. Backpressure is built in: because the bodies are Akka Streams (which have backpressure), the streaming is backpressured end to end -- a slow consumer (a slow client reading a response) backpressures the producer (the server slows its production to match) -- so a slow client doesn't cause the server to buffer unboundedly (the backpressure throttles the server to the client's pace). This streaming-first, backpressured foundation is Akka HTTP's distinctive strength (streaming large/unbounded data naturally and backpressured), and understanding that entities are Akka Streams (streamed, backpressured) is understanding Akka HTTP's streaming character and its strength for streaming workloads.
And the routing-DSL-plus-actor-integration design is what makes Akka HTTP productive and integrated. The routing DSL is composable: directives (small building blocks -- extract a path segment, require a method, unmarshal an entity, complete a response) compose into a route (a tree of directives handling the HTTP surface) -- productive (building complex routing from small, reusable directives) and readable (the route structure mirrors the HTTP structure). Actor integration: Akka HTTP integrates with actors (the foundation of Akka) -- a request can be handled by asking an actor (the ask pattern -- the route asks an actor to process the request and completes with the actor's response), leveraging the actor model (concurrency, supervision, state) for request handling. This -- the productive composable routing DSL, integrated with the actor model -- makes Akka HTTP both productive (the DSL) and integrated with the Akka ecosystem (actors) -- so an Akka-based system can build HTTP APIs productively (the DSL) that integrate with its actors (the ask pattern). Understanding the routing DSL (composable directives) and actor integration (the ask pattern) is understanding how Akka HTTP is productive and integrated with Akka.
The architecture: every piece explained
Top row: the routing and conversion. The routing DSL: a composable tree of directives handling the HTTP surface (building routes from small building blocks). Directives: the building blocks -- extraction (path segments, query params, headers, entities -- pulling values from the request), filtering (method, content type -- requiring conditions), and completion (producing responses) -- composed into routes. Marshalling: converting between Scala types and HTTP entities -- unmarshalling (request entity to a Scala type -- e.g., JSON to a case class) and marshalling (a Scala type to a response entity -- case class to JSON) -- via marshallers (e.g., spray-json, circe integration). Streams foundation: HTTP entities are Akka Streams (bodies as streams of bytes) -- the streaming-first foundation (entities streamed, not materialized by default).
Middle row: streaming, clients, protocols. Backpressure: the streaming request/response is backpressured (a slow consumer backpressures the producer -- via the Akka Streams foundation) -- so streaming is backpressured end to end (slow clients throttle the server, no unbounded buffering). Connection pools: the HTTP client uses connection pools (reusing connections for outgoing requests -- for calling other services) -- the client-side connection management. WebSockets and SSE: WebSockets (handled as Flows -- a Flow of messages, bidirectional) and server-sent events (a streamed response of events) -- streaming protocols, natural on the streams foundation. Low-level vs high-level: two APIs -- the high-level routing DSL (productive, for most cases) and the low-level API (direct request/response handling -- for control or unusual needs) -- the high-level for productivity, the low-level for control.
Bottom rows: integration and comparison. Actor integration: Akka HTTP integrates with actors -- the ask pattern (a route asks an actor to handle a request, completing with the actor's response -- leveraging actors for request handling), supervision (actor supervision for resilience) -- integrating HTTP with the actor model. vs http4s / Play: Akka HTTP (streaming-first, actor-integrated -- strong for streaming and Akka-based systems) vs http4s (pure-functional, effect-based -- strong for the Typelevel ecosystem) vs Play (full-stack framework -- more batteries-included) -- different strengths, chosen for the ecosystem and needs. The ops strip: blocking discipline (like all Akka Streams/actor code -- never block the dispatcher, offload blocking work -- the load-bearing discipline; blocking the dispatcher starves the reactive system), streaming (leveraging the streaming-first nature -- streaming large/unbounded data, backpressured -- and handling it correctly), and tuning (connection pools, dispatcher configuration, streaming buffer sizes -- the reactive-system tuning).
End-to-end flow
Trace Akka HTTP handling a streaming request. A route handles a large file upload: the request entity (the uploaded file) is an Akka Stream (a stream of bytes -- not materialized in memory). The route processes it as a stream (e.g., streaming the bytes to storage -- via an Akka Streams flow) -- so a large file is handled in constant memory (streamed, not buffered) -- the streaming-first foundation making the large upload natural. And it's backpressured: if the storage (the consumer of the stream) is slow, it backpressures the upload (the server reads the upload at the storage's pace -- not buffering the whole file waiting) -- so a large upload with slow storage doesn't cause unbounded buffering (the backpressure throttles the read to the storage's pace). Similarly, a route streaming a large response (a big download or SSE) returns the body as an Akka Stream -- streamed to the client, backpressured (a slow client throttles the server's production). The streaming (large upload/download, backpressured) is natural on Akka HTTP's streaming foundation -- its distinctive strength.
The routing-DSL and actor-integration vignettes show the productivity and integration. A routing case: a route is built from composable directives -- e.g., path('orders' / LongNumber) { id => get { complete(getOrder(id)) } } -- extracting the path (the order ID), requiring GET, and completing with the order (marshalled to JSON) -- the composable directives building the route productively (small building blocks composed). An actor-integration case: a route handles a request by asking an actor -- onSuccess(orderActor ? GetOrder(id)) { order => complete(order) } -- the route asks the order actor (the ask pattern), and completes with the actor's response -- integrating the HTTP request handling with the actor model (the actor processing the request, with the actor's concurrency, state, supervision) -- the actor integration leveraging Akka's foundation for request handling.
The blocking-discipline and comparison vignettes complete it. A blocking case: a route handler makes a blocking call (a synchronous database call) directly on the dispatcher -- under load, this blocks the dispatcher threads (starving the reactive system -- the same blocking problem as all Akka Streams/actor code); the fix is offloading the blocking work (running it on a dedicated dispatcher/executor, not the main dispatcher) -- keeping the main dispatcher non-blocking (the load-bearing blocking discipline). A comparison case: the team chose Akka HTTP (for their Akka-based system -- integrated with their actors, and their streaming-heavy workload -- the streaming-first strength); a different team on the Typelevel stack might choose http4s (pure-functional, effect-based), and a team wanting a full-stack framework might choose Play -- matching the HTTP toolkit to the ecosystem and needs. The consolidated discipline the team documents: leverage Akka HTTP's streaming-first foundation (entities as Akka Streams -- streaming large/unbounded data naturally, backpressured), build routes with the composable directive DSL (productive), use marshalling (to/from JSON/entities), integrate with actors (the ask pattern -- leveraging the actor model for request handling), support WebSockets/SSE (Flow-based, on the streams foundation), never block the dispatcher (the load-bearing discipline -- offload blocking work), tune the reactive system (connection pools, dispatchers, buffers), and choose Akka HTTP for Akka-based systems and streaming workloads (versus http4s/Play for other ecosystems/needs) -- because Akka HTTP is the streaming-first, actor-integrated HTTP toolkit of the Akka ecosystem, strong for reactive streaming HTTP and natural for Akka-based systems, with the blocking discipline of all Akka code.