Reactive Architecture

Reactive Architecture

Introduction

The rise of adoption of microservices architecture has led to emerging pattern called reactive.

It revolves around Reactive systems, Reactive Microservices & Reactive programming.

Reactive manifesto, it incorporates the idea, paradigms, methods and patterns from both Reactive Programming and Reactive systems into a set of practical principles that software architect and developers need to apply in their transformative work.

The adoption to Reactive architecture is an enabler in making the business realize the efficiencies.

This comes in handy to enable the design and implementation of a highly concurrent and distributed software that is performant, scalable and resilient, while at the same time consuming resources when deploying, operating, and maintaining it.

Therefore, with the adoption of Reactive principles allows an organization to depend on software for making our diverse and distributed civilization more robust.

What makes a system to be Reactive:

I. Stay Responsive

II. Accept Uncertainty

III. Embrace Failure

IV. Assert Autonomy

V. Tailor Consistency

VI. Decouple Time

VII. Decouple Space

VIII. Handle Dynamics


1. Stay Responsive

Ensure the architecture of the solution that is rolled out always responds in a timely manner. The responsiveness of your digital solution matters a lot in the face of your business and its quality of service, the last link in the chain, bridge to your users, and forms the cornerstone of usability and utility.

Being responsive is not just about low latency and fast response time, but also about managing changes—in data, usage patterns, context, and environment. Such changes should be represented within the application and its data model, right up to its end-user interactions; reactions to change will be communicated to the users of a component, be they humans or programs, so that responses to requests can be interpreted in the right context.

Reactive, responsive applications effectively detect and deal with problems. Reactive applications focus on providing rapid and consistent response times. In the worst-case scenario, they respond with an error message or provide a degraded but still useful level of service. This establishes mutually understood upper bounds on response latency and thereby creates the basis for delivering a consistent quality of service. Such consistent behavior in turn simplifies error handling, builds end-user confidence, and encourages further interaction.

2. Accept Uncertainty

Your architecture should build reliability, regardless of unreliable foundations.

Deploying our solutions in cloud or adopting edge technologies does not make your solution reliable. It is scary for the business when systems fail in the most spectacular and intricate ways this might lead to loss of information, reordering, and corruption, and failure detection is guessing game. The world is full of uncertainty.

Even though there are well established distributed algorithms to tame this uncertainty and produce consistent view of the world, these algorithms may introduce poor performance and scalability characteristics and imply unavailability during network partitions.

The key is to manage uncertainty directly in the application architecture. To design resilient autonomous components that publish their protocols to the world—protocols that clearly define what they can promise, what commands and events will be accepted, and, because of that, what behavior will trigger and how the data model should be used. The timeliness and assessed accuracy of underlying information should be visible to other components, where appropriate so that they — or the end-user — can judge the reliability of the current system state.

3. Embrace failure

4. Assert Autonomy

Design components that act independently and interact collaboratively.

Valuable patterns that foster autonomy include domain-driven design new tab, event-sourcing new tab, and CQRS new tab. Communicating fully self-contained facts — modeled closely after the underlying business domain — gives the recipient the power to make their own decisions without having to ask again for more information. CQRS separates concerns by making decisions about one part of the system in one location (one component), which can then readily be disseminated and acted upon elsewhere—possibly at a much later time.

5. Tailor Consistency

Separate consistency per component to balance availability and performance.

Consistency entails guaranteeing the correctness and integrity of your application and user's data.

Providing more consistency led to less value addition, therefore decreasing the availability, efficiency, and performance of your application.

When possible, design systems for eventual consistency new tab or causal consistency new tab, leveraging asynchronous processing, which tolerates delays and temporary unavailability of its participants (e.g. using an event-driven architecture new tab, certain NoSQL new tab databases, and CRDTs new tab). This allows the system to stay available, eventually converge, and, in the event of failure, automatically recover.

If strong consistency is inherently needed for the correctness of a use-case, it should be applied judiciously and selectively, with clearly defined consistency boundaries, keeping the unit of consistency as small as possible to retain a maximum of scalability and availability.

6. Decouple time

Embrace asynchronous to avaoid coordination and waiting.

It's been said that "silence is golden," and this is a true reflection of software systems in the real world. Amdahi's Law and the Universal Scalability Law show that the ceiling on scalability can be lifted by avoiding needless communication, coordination, and waiting.

With temporal decoupling, we give the caller the option to perform other work, in an asynchronous new tab, rather than be blocked while waiting for the resource to become available. This can be achieved by allowing the caller to put its request on a queue, register a callback new tab to be notified later, return immediately, and continue execution (e.g., non-blocking I/O new tab). A great way to orchestrate callbacks is to use a Finite State Machine new tab (FSM), other techniques include Futures/Promises new tab, Dataflow Variables, new tab Async/Await new tab, Coroutines new tab, and composition of asynchronous functional combinators new tab in streaming libraries.

7. Decouple Space

Create flexibility by embracing the network.

System architecture for solutions should embrace the existence of service components that can live in multiple locations, this comes in handy when parts of the underlying hardware malfunction or are inaccessible.

Spatial decoupling enables replication, which ultimately increases the resilience of the system and availability. By running multiple instances of a component, these instances can share the load. Thanks to location transparency, the rest of the system does not need to know where these instances are located but the capacity of the system can be increased transparently, on-demand. If one instance crashes or is undeployed, the other replicas continue to operate and share the load. This capability to fail-over is essential to avoiding service disruption.

8. Handle Dynamics

Continuously adapt to the varying demand and resources.

Tharindu Kavinda

Cloud Engineer @ MIT esp | Cloud Automation | AI | AWSx3 | Azurex1 | Serverless | FinOps | Terraform |BSc (Hons) EEE | AMIE(SL) | A.Eng(ECSL) | UOP

7 个月

Well written! Martin Joseph

回复
Yuriy Vashinko

Engineering Manager and Tech Lead passionate about building great projects, processes and teams | Mentor | Manager | Developer | Qt | C++ | .NET

7 个月

I think adoption today is a key value of any architecture

要查看或添加评论,请登录

社区洞察

其他会员也浏览了