Unveiling the Future of Application Networking: Trends and Impacts
Where are the application networking features heading, and how might this affect the way we design and approach distributed applications in the future? The revelations might surprise you. Let’s explore the shifting sands of application networking, focusing on the movement of networking concerns with the rise of the application cloud. Unraveling the intricacies of transparent, synchronous and asynchronous networking, let’s examine the migration and transformation of these aspects within the context of modern distributed architectures.
Transparent Networking Descends to the Platform
Distributed applications are composed of multiple components interacting with each other using networking. These interactions can be controlled at runtime transparently to the applications through service mesh and other similar technologies, or from within the application through explicitly implemented patterns such as point-to-point integration, event-driven or orchestration-based interactions.
Here I define transparent networking as control and monitoring mechanisms that can be added to the behavior of the applications interactions with each other without making developers and application implementation aware. Think of service discovery or load balancing that is performed by Kubernetes without the application being aware of how it is done. Think of traffic shifting, and resiliency features such as retry and circuit breaking performed by a Istio’s sidecars. Think of mTLS, authentication and authorization, or network tracing and observability that you can get from the Linux kernel through Cillium’s eBPF-based implementation.
All of these features can be added to a distributed application at runtime without changing the application code and without developers implementing a single line of code within the application.
These concerns used to be addressed by developers in the application layer through language-specific libraries such as Apache Camel or Spring Cloud Netflix in the Java ecosystem, but today they are increasingly delegated to polyglot runtimes such as Dapr, or delegated to the platform layer through transparent sidecars such as Envoy, and even get deeply embedded with the compute platform in the case of eBPF technology and closed source networking cloud services.
Synchronous Networking in Transit Away from Applications
Synchronous interactions between applications are ones that don’t require any kind of intermediating persistent state store such as a message broker to offload a request into an in-between-applications medium. As a result, the synchronous interactions I describe here are typically blocking interactions initiated by client applications and reaching the target service in the same invocation. The kind of application responsibilities considered here are connectors to various external APIs, calls between services within a solution and protocol conversions. This also includes content-based routing, filterings and light transformations of requests, aggregation of multiple messages into one or splitting a large message into multiple. The last group can be done with a persistent statestore, but here I consider when it is done on the fly, without persistence. Broadly, these application networking concerns consist of message routing and message transformation patterns as listed in the book “Enterprise Integration Patterns.”
While these concerns traditionally were implemented from within the application and popular mainly in the Java ecosystem, for example with projects such as Apache Camel and Spring Integration, today we can see these features moving out into purpose-built plug-and-play runtimes that can be used with many polyglot applications. Examples of these are Dapr sidecar, Apache Kafka Connect, Knative Event Sources, NATS and various managed cloud-based connectors and traffic routing services such as AWS API Gateway for routing traffic or AWS EventBridge for routing events, etc.
In all of these examples, the application passes a message to a separate runtime where message routing and transformation logic is performed, and the result is passed back to the application or forwarded to another application. The routing, filtering and transformation logic applied affects the shape of the data and the target it is flowing into.
In contrast to the transparent features that can be applied by the operations teams after an app has been implemented, synchronous networking capabilities are used by developers, and the application has to be designed and implemented with that in mind.
As a result, we can see synchronous networking features not sinking down into the platform transparently, but transforming from libraries into purpose-built reusable runtimes and cloud services that can be plugged into any applications and swapped when needed without affecting the application implementation. The latter is possible by designing the application with principles of hexagonal architecture and decoupling it from external dependencies through well-adopted open standards.
Today there is no single universally adopted standard or implementation in this space, but there are a few commonly used messaging patterns (such as filter, content-based router, wiretap, aggregator and splitter) that serve as a commonly understood language. These patterns are typically implemented through domain-specific languages or using the Common Expression Language spec, and act on data that is JSON or ProtoBuf formatted in a CloudEvents wrapper traveling on HTTP or gRPC protocols.
The Ascent of Asynchronous Networking Toward the Cloud
Asynchronous networking allows applications to store state into an external system for its own use or as a temporary store before exchanging data with another service. For example, a developer may use an external state store such as Redis for key/value access or an object store such as AWS S3 to store state and make the service stateless. An application may use a message broker such as Apache Kafka to publish an event that another service might be interested in. An application can start a business process stored in a persistent workflow engine such as Conductor, which needs to orchestrate interactions with other services. When we look at the end-to-end interaction between the source and target services, a state is persisted in the intermediate system before exchanging with other services. These asynchronous network interaction styles distribute state among participants in a predictable and reliable manner using a few well-known methods such as pub/sub, key/value access, orchestration, cron job, distributed lock, etc.
Each of the asynchronous networking patterns offers a unique interaction style on top of state. Key/value and object stores offload state that is typically accessed from the same application. A message broker is used to asynchronously communicate between a publisher service and one or more recipient services. And workflows are used to coordinate complex stateful interactions between multiple applications or to trigger a service endpoint on time-based intervals.
There are other specialized examples of stateful application infrastructure too: for example, distributing application configurations from a central configuration store, distributing secrets, mutually exclusive access to a resource using a distributed lock, etc. These interactions are explicit to the application and the developers need to develop the application in a way to interact with these specialized systems. There are a number of APIs that are becoming widely adopted standards in each respective domain. For example Redis, MongoDB and Amazon Web Services (AWS) S3 are examples of popular APIs for key/value and document access. Apache Kafka, AMQP, NATS, are examples of asynchronous interaction protocols. Camunda, Conductor and Cadence are examples of stateful orchestration engines.
While these projects focus on a single type of stateful interaction and provide the implementation and the API, the Dapr project is focused on providing unified APIs for different interaction styles and plugging them into existing backend implementations. For example, the Dapr statestore API can be used with Redis, MongoDB, PostgreSQL and others. Dapr pubsub API can be used with Kafka, AWS SQS, GCP Pub/Sub, Azure EventHub and others. Its configuration, secret and distributed lock APIs also plug into existing infrastructure systems and offer a unified polyglot higher-level HTTP and gRPC-based protocols to abstract these backends.
In response to the inherent complexities of managing state, the industry is witnessing a remarkable shift where asynchronous networking capabilities are increasingly being offered as SaaS solutions. This transition streamlines the adoption process, simplifies scalability and enhances the manageability of these services.
Apache Kafka, a widely used message broker, is now accessible as Confluent Cloud and AWS Managed Streaming for Apache Kafka (MSK). Similarly, key/value stores like Redis and MongoDB, traditionally managed in-house, have evolved into cloud services. Redis Labs’ fully managed cloud service and MongoDB Atlas’ globally available service with integrated resource and workload optimization are testaments to this shift.
Stateful workflow systems, too, have entered the SaaS realm, simplifying developers’ task of orchestrating intricate stateful interactions between applications. AWS Step Functions, Temporal Cloud, Orkes and Diagrid Cloud are leading this evolution. This SaaS transformation of stateful networking projects is driven by the desire to abstract state management complexities. It enables developers to focus on business logic rather than intricate asynchronous interactions.
The Divergent Path for Application Networking
Distributed applications are composed of multiple components distributed across numerous processes that interact with each other over the network. The primary advantages of distributed applications such as faster release cycles and scalability lie in how different networking patterns facilitate isolation of dependencies and state distribution among participants in a predictable manner. However, networking introduces new challenges in terms of the distributed systems programming model, reliability, security and observability. Similarly to how container adoption shifted significant application responsibilities from developers to operations teams, we can observe a shift in different types of networking concerns too.
Transparent networking features, while limited in their capabilities, are becoming more prevalent as they are integrated into platform offerings. With the right platform capabilities available, developers no longer need to bother with network security, observability and traffic management.
Stateless interactions combine networking with knowledge of data formats and message transformation logic. Such interactions are increasingly made reusable via standard connectors and enterprise integration patterns implemented as purpose-built distributed system middleware. Developers don’t have to continually reinvent the wheel in every language and application stack, but plug such capabilities into their applications at runtime. Given enough time, these networking patterns become reusable libraries, purpose-built frameworks and sidecars, and eventually transition into cloud-based APIs.
Asynchronous interactions present a higher degree of complexity as they necessitate behind-the-scenes state management. These networking interactions are often provided as specialized standalone software or as managed services, ideally fronted by widely adopted APIs. Unlike transparent APIs that sink into the compute layer and are primarily used by operations teams, asynchronous networking interactions arise in cloud offerings, created for application developers.
This progression of networking responsibilities is anticipated to drive transparent runtime and networking features further into the compute platform. Meanwhile, explicit features will continue to consolidate, forming common APIs and elevating into the cloud as ubiquitous serverless capabilities. Appropriately delegating responsibilities across different layers and choosing the appropriate standardized APIs for diverse networking tasks is becoming increasingly essential. Consequently, this megatrend will empower developers to focus on implementing business logic, integrating other capabilities either transparently or via universally recognized and portable APIs.