Every customer today demands that the application to be build should be Cloud Ready and with no or minimum changes they can run on Cloud. Building an application to run on the cloud is becoming a common task. The prerequisites step is that the application is first built as “Service Enabled”.
The application’s functionality needs to be exposed to the outside world through services, i.e. not requiring knowledge of the application’s inner workings, not resulting in side effects, and using an interface that will change very little over time. The same holds true within the applications when exposing (sub) components to each other. Service design is the way to build applications.
Following are the important Design Principles, Standards and Best Practices for Service-Enabled applications eventually to build Cloud Ready Application.
Asynchronous / Parallel Programming
Parallel programming in which a unit of work runs separately from the main application thread and notifies the calling thread of its completion, failure or progress.
Task-based Asynchronous – Which uses a single method to represent the initiation and completion of an asynchronous operation.
All applications and services are self-contained and “loosely coupled”, Services maintain a relationship that minimizes dependencies and only requires that they maintain an awareness of each other.
Standardized I/O – All data provided to or by the (subcomponents of the) applications are transferred through standardized interfaces, both in structure and technology. These interfaces should be designed in such a way that:
- they will change very little over time
- they are backwards compatible whenever possible
The content/structure of these interfaces should be based on the information needs and required results of the application/component, and not on the data structures of data sources and/or targets.
Best Practices – Dependency Injection pattern to achieve loose couple classes from their dependencies.
Example – Microsoft Unity is one of the most popular tools to implement Dependency Injection (DI). Unity is a lightweight, extensible dependency injection container and it facilitates building loosely coupled applications.
Microsoft’s patterns and practices suggest Dependency Injection with Unity.
When selecting a Dependency Injection container it is worth considering a number of factors such as:
- Ease of use
- NuGet Support etc.
Basic Comparison with some IoC Containers are available here.
Asynchronous Messaging – Messaging supports asynchronous operations, enabling to decouple a process that consumes a service from the process that implements the service to build scalable and resilient solutions.
Design scalable services by separating them from their state data whenever possible.
Stateless – Avoid session state and if states are required then moved to external component i.e. Cache.
Ability of a service to produce the same outcome after calling it multiple times.
Idempotent – An operation is idempotent in case it has no additional effects if it is called more than once with the same parameters.
Applications are based on secure lifecycle standards and include built-in security.
Secure Data in Transit – Secure network protocols, such as SSL and HTTPS, must be used to prevent network traffic snooping or man-in-the-middle attacks.
Secure Service / API – Authentication and Authorization mechanism ensues that applications and services are only accessible to the clients that should have access to them.
Secure Data at Rest – Data at rest are encrypted.
A method or service must always behave in a predictable way, regardless of the context in which, or the parameters with which, it is called.
Note that this “predictable way” maybe an error return (with a meaningful error code or message).
Once the application is Service Enabled it will further be extended to be built as Cloud Ready.
The application need to be designed and written in such a way that they can be deployed in a cloud environment.
Following are the Cloud Service Models.
For building a Cloud Ready application the focus will on Platform as a Service (PaaS).
Following are the Steps to achieve Cloud Ready Applications:
Let’s first understand in summery the characteristics of Cloud.
- On-Demand, self-service model – Cloud offers services On-Demand using self-service model. The management console or API’s exposed be the cloud provider enables to use cloud services as need arises. This offers convenience and flexibility to the consumers.
- Broad Network Access – The cloud services are available across common networks. In this way consumers benefit because they can use the services with the broad range of computing platform and devices.
- Dynamic Shared and Virtual Infrastructure – Cloud service providers have a business model incentive to leverage their infrastructure across as many consumers as possible. Providers offering shared hardware and shared OS multitenancy use server virtualization technology to create their shared infrastructure.
- Scale Rapidly and Elastically – From a consumer point of view, a cloud provider’s ability to quickly provision and de-provision IT services creates an elastic, scalable IT resource. Consumers pay for only the IT services they use.
- Meter Consumption – Providers meter the resources (for example, compute, storage and network) that cloud applications consume. Metering enables multiple pricing or chargeback models. These may include pay-as-you go plans, subscriptions, fixed plans and even free plans.
REALIZATION – Cloud Design Principles realized by Patterns
Following are the Design Principles, Standards and Best Practices for Cloud applications.
Failure-aware (Resilient to failure)
Applications are resilient to occasional or extended failures of underlying infrastructure components or dependent services.
Data Replication – Replication refers to creating a redundant set of data so that, if one set of instances fails, another copy of data is available for other instances to work with. Mirroring critical data to another data centre managed by your cloud service provider provides a higher level of resilience and availability.
Circuit Breaker – Handle faults that may take a variable amount of time to rectify when connecting to a remote service or resource.
Retry – Enable an application to handle temporary failures when connecting to a service or network resource by transparently retrying the operation in the expectation that the failure is transient.
Latency-aware (Resilient to latency)
Applications remain operational despite variable and extended latency for services accessed across public networks and between cloud provider data centres.
Cache-aside – Load data on demand into a cache from a data store. This pattern can improve performance and also helps to maintain consistency between data held in the cache and the data in the underlying data store.
Static Content Hosting – Deploy static content to a cloud-based storage service that can deliver these directly to the client. This pattern can reduce the requirement for potentially expensive compute instances.
All data provided to, provided by and stored by the application (also as intermediate results) need to be fenced off and encrypted and/or anonymized. The application protects the information and systems, guarding against data loss or SLA violations caused by malicious or accidental events.
Secure Data at Rest – Cloud providers offer encrypted storage services that protect stored data from third-party access. In addition attention is required for protecting personally identifiable information (PII).
Federated Identity / Tokenization – Delegate authentication to an external identity provider. This pattern can simplify development, minimize the requirement for user administration, and improve the user experience of the application.
Role-Based Access Controls (RBAC) – Implementing a secure application requires role-based access controls (RBAC) to ensure that only authenticated and authorized users access application resources and that access is deliberately assigned according to their role.
Applications discover services dynamically rather than relying on hard-coded dependencies.
Service Discovery – Implementing a service discovery service that application components can query to determine the location of a given service dynamically, there should be no hard-coded configuration information (such as static IP addresses of services).
Applications are packaged, deployed and executed without manual steps or complex dependencies.
Template-Driven Deployment – All provisioning, instantiation and configuration for an application should be scripted and preferably encapsulated in an automatically unpacked and deployable template. Automating deployment also means encapsulated application dependencies for deployment as a single package.
Example – Azure Resource Manager (ARM) template is used to define the resources to deploy for a solution, and specify parameters and variables that enable to input values for different environments.
Sufficient telemetry data is available from applications to determine health and other operational and performance characteristics.
External Configuration Store – Move configuration information out of the application deployment package to a centralized location. This pattern can provide opportunities for easier management and control of configuration data, and for sharing configuration data across applications and application instances.
Example – With Application insights, an application’s telemetry data is captured, sent to the cloud for storage and processing and then rendered to the user in an interactive format via the portal.
Applications make no assumptions about the underlying infrastructure, using abstractions in relation to the operating system, file system, and so on.
Infrastructure Constrained – Applications should not be constrained to specific infrastructure also there should not any direct interface with hardware.
No Local Storage – Applications to be designed considering there is no local storage on the hosted infrastructure. Cloud provided storage to be used to store files.
Applications can be adapted to different algorithms, data structures and system architecture in response to resource cost changes.
Data Compression – Consider data compression, but balance against CPU costs for encoding and decoding.
Replace or avoid “chatty” protocols – Avoid make multiple requests over time to accumulate the necessary data.
Each application should have a deliberate, defined single tenancy or multitenancy model.
Sharding – Divide a data store into a set of horizontal partitions shards. This pattern can improve scalability when storing and accessing large volumes of data.
Cloud related all Design Patterns, Practices and Guidance are available here.
Finally, Cloud Ready to Cloud Native application is achieved by implementing Services exposed by Cloud Provider and hosting the application / services on Cloud.
 The term ‘service’ refers to the concept. The principles are valid regardless of the actual physical implementation (RPC, web service, etc.)
About Ashok Bhandari
Ashok is a Senior Software Architect for MS Stack in Sogeti Netherlands. Ashok has 15+ years of IT experience and plays a role of Architect / Lead developer for designing & developing solutions on cloud for Microsoft Azure and Asp.Net MVC projects. Ashok has extensively worked on projects for Banking, Foreign Exchange Services, Insurance, Utilities and Energy domain. He has worked on award winning projects i.e. SlimLaden App and OneShare. Ashok as an architect / lead developer has guided teams and organization with their road map to Cloud. He has extensively worked on MS stack including MS Azure, CI / CD Azure Pipelines, ASP.Net MVC, C#, Web API, Entity Framework, .Net Framework, SQL Server etc. Ashok is Microsoft® Certified Solutions Associate on Cloud Platform and Microsoft Certified: DevOps Engineer Expert.
More on Ashok Bhandari.