By: Levi Masonde | Updated: 2024-04-23 | Comments | Related: > Cloud Strategy
Problem
You run multiple web applications hosted in-house or on virtual machines (VM) in the cloud. Since your architecture is monolithic, every time it encounters a technical bug or hardware issue, the entire application stops working, i.e., it goes offline. This means your web application will remain offline until you fix the problem.
Imagine your entire web application comes to a halt because a dependency you used to create a dynamic success page got upgraded. This is just an example, but as a developer, you know that errors arise from all sorts of issues in the lifespan of your development. However, these issues should not result in the whole application stopping.
Solution
Serverless computing ensures that your hardware components are not an issue. It takes care of your application's infrastructure and lets you focus on the software. This solution also enables you to automatically scale your applications as they grow and require more computing resources.
Microservice architecture is when you break an application into smaller independent pieces of code that you can manage separately, but ultimately work together to form one flexible application. This architecture ensures that no one service can disturb the others since they are not closely coupled. The application will keep running even if one of the services is down. You can also isolate the services for upgrades, debugging, and making changes.
Although the microservice architecture solves many issues, it also introduces some problems. The main issue with this approach is maintaining clear communication between all your services. The more services you have, the trickier it becomes to maintain communication between them. To avoid this, you need a well-designed architecture, starting with mapping out a bird's-eye overview of the entire application.
In this tutorial, you will learn how to design a microservice architecture following the Assemblage process.
What is Serverless Computing?
Serverless computing doesn't necessarily mean there is no backend to the applications you build. Rather, it refers to building applications or containers without having to manage and maintain the infrastructure that hosts them. Serverless architecture enables your applications to scale dynamically.
In Microsoft's ecosystem, you can create serverless microsystems using Azure Service Fabric (ASF), Azure Kubernetes Services (AKS), or Azure functions. ASF is Microsoft's microservices framework, which includes a Container Orchestrator; you can run your microservices with or without a container. AKS is a Container Orchestrator and depends on a Containerization framework like Docker. Both ASF and AKS require a minimum of three VMs to run, while Azure functions is a Serverless offering in Azure. It does not require any fixed number of VMs in the consumption plan.
This makes Azure functions an ideal tool for creating serverless microservices using Azure, but I plan to show examples of all the options in later articles.
What are Microservices?
Microservices are a form of software architecture where loosely coupled independent services are joined together via well-designed APIs to create a flexible, auto scaling, and reliable application. You can run, maintain, upgrade, and deploy services independently. As much as this is great for separating components of your application, it can quickly become complex to maintain the entire application with multiple services. So, it is crucial to design the application correctly and define your APIs in a simple, straightforward manner. Most large software companies like Netflix and Twitter have switched to using the microservice architecture.
What is Assemblage?
Designing microservices is just one step in Chris Richardson's Assemblage Approach. Assemblage is a microservice architecture definition process. As the name suggests, it is a template process for creating cohesive microservice architectures.
System Operations
The first step in the Assemblage process is to discover system operations. System operations in this context refer to the actions you expect your application to handle. These actions will be invoked by user actions or external events.
The following are examples of system operations for an ecommerce application:
- createCustomer(): Operation to create a new customer in the system.
- createOrder(): Operation to create a new order for a customer.
- cancelOrder(): Operation to cancel an existing order.
- findOrderHistory(): Operation to retrieve the order history for a customer.
A system operation reads and/or writes to one or more business entities (domain-driven design (DDD) aggregates), such as Customer and Order.
Subsystems
The next step is to define subsystems. Using the domain-driven design, you must identify the Domain, Entities, Values Objects, Aggregates, Repositories, Services, and Bound Context of our project.
Domain
The domain refers to the main subject or target you are developing an application/program for. If you are developing an application for company X, then company X is referred to as the Domain of the project. The domain will dictate the knowledge and activities to need to consider strongly to develop the core business logic of your application. Therefore, it is important to understand the everyday business of the domain you plan to develop an application for.
The domain creates a common area of understanding for business experts, users and developers by allowing developers to follow a set of predictable classes which mirror the processes followed by the business experts and users. For example, an ecommerce domain will have classes like "order", "customer" and methods that go with it like "place order", "order delivered".
Entities
Entities are objects that must remain unique throughout their lifespan on the application and can change their state while retaining the same identity. Entities are based on their identity not on their attributes, just like how people can have the same name, height and age but ultimately, they can be separated by another form of identity like an ID number. These usually refer to objects you would like to track in the real world. For a simple ecommerce application, these would be objects like:
- Order
- Product
- Customer
Value Objects
After the Entities, you must identify the Value objects. These are objects that are immutable attributes and do not have a concept of identity. The values of a Value Object cannot be changed once the Value Object is created. Working with Python classes without value objects might look like this:
Class order: Streetname: str City: str Province: str Country: str StartDate endDate
Using value objects in Python, you can first declare the values for an immutable object:
from dataclasses import dataclass @dataclass(frozen=True) class Address: street: str city: str Province: str
This value object can then be used on the application as shown below:
from dataclasses import dataclass @dataclass(frozen=True) class Order: address = Address (street='', city='', province='')
Aggregates
Aggregates are classes that group different Entities or Value Objects, and they are the basic element of transfer of data storage. Aggregates help you to not have a stiff view on how project entities interact and helps against the tendencies for developers to treat everything on a project like data objects which are all directly linked.
Aggregates are clusters of related objects, but these objects must in a way reflect how they behave in real life to an extent.
Repositories
Repositories are responsible for interfaces that house/host objects and make it easier to aggregate data. Repositories can act like a wrapper layer that sits between the database and your mutable objects. You can store your mutable objects on a repository which is separate from your business logic for the application.
Repositories are designed to control data access, for example: if you were to walk in a large supermarket for the first time to do your shopping, instead of you as the shopper having to walk all the isles to get products, you can have an experienced personal shopper who knows where all the products are and you can give the personal shopper a list of products you want and they would bring it to you then you go pay. In this instance, the supermarket in the database, while the personal shopper is the repository and the list you provide is a query.
Domain Services
Domain services cover most of the application logic that cannot be fully encapsulated using Entities or Value Objects. These application logics are usually stateless and are called by other classes in the domain. They usually work by combining multiple entities to achieve cross-cutting concerns like validation.
The main aim of domain services is to develop action driven software that do not belong to any Entity. Continuing with the example from the above section, assume you do not even go to the store but place the order online or via a phone call, there must be logic to calculate how much you should be charged for shipping the items to you. This calculation will involve things like your distance from the store, discounts, the weight and dimensions of the item, how fast you want the item, who are the available drivers and more, so a domain service should then be able to span across all these concerns and have a service called 'shippingCostService' for example which will encapsulate all the concerns.
Bounded Context
DDD design focuses on having explicit separation between different aspects of a project or Domain, this means every team working on the project can be put into a bounded context which has its objectives. This mean that the application will not have a unified model, but if will have different models that cater to different teams and this a one of the major reasons behind the DDD design, the ability to involve different departments in the application in a way they want to be represented instead of designing everything from the developer's point of view.
Now that we have roughly covered the first two steps of the Assemblage process, we can start with the third step, designing Microservices.
Designing the Application Microservices
The general design of a microservice architecture looks something like this:
To start your design, you must list all your system operations by descending importance and tackle them individually. First, the subdomains that implement the system operation are grouped to form services. Each service consists of one or more subdomains. Each subdomain is in one and only one service. For our ecommerce web application, using the Entities and Value Objects shown earlier, this is how the design should look:
Note: To capitalize on the advantages of using microservices, you must also structure your organization accordingly. Ideally, small teams should operate on different microservices while adhering to similar API conventions, which is one more reason to ensure that your microservice design is clear to all parties involved.
Each service is given a name and a set of responsibilities, and each service
has an API to handle system operations and publish events. For example, the Order
Service would be responsible for system operations like
createOrder()
and publish
events like Order Created.
Each system operation can run locally when all the subdomains of a system are in one service; otherwise, they are distributed, which is when two or more subdomains (classes/functions) required to execute the operation are in different services. There are different ways to design service collaboration patterns for a distributed operation, our examples use choreography-based saga, which is a sequence of local transactions that can be implemented across different services, and it does this by ensuring that each local transaction updates the database and sends a message to an event store which will trigger the next transaction in the saga and so on.
Once a user clicks the button to order, the product service will have all the
information about the product being ordered and will pass this to the
createOrder()
operation.
This is what a simplified createOrder()
distributed operation would look like:
Now, the createOrder()
operation realization should be updated as follows:
- Updates the API Gateway - routes createOrder() request to the Product Service
- Add an event handler for the product ordered event that creates an order for the product and attaches a customer and order Value objects like address, price, and quantity.
Repeat this for all other system operations. In the following steps of the Assemblage process, you will need to evaluate the microservice architecture and try to improve its quality as much as possible.
This approach to developing software has received backlash among experienced developers who claim that this approach is the same as trying to develop a transactional database for every project you work on and can easily lead to data corruption since there might be so many pieces of moving components updating each other. To address this, methods like the saga which uses events to keep track of transaction history of the application are being developed to maintain data consistency across the application. To create microservices, you must prioritize maintaining data consistency and avoiding data corruption.
Next Steps
- Learn how to Start, Stop, Delete and Monitor Azure Container Instances for SQL Server
- Going Serverless with Azure SQL Database
- Create Azure Container Instances for SQL Server
- Study Materials Exam 70-533 Implement Microsoft Azure Infrastructure
- Auto Scale Azure SQL DB using Azure Logic Apps
About the author
This author pledges the content of this article is based on professional experience and not AI generated.
View all my tips
Article Last Updated: 2024-04-23