Our legacy API (Hackathon registration service)
Figure 1 represents the architecture of our original Hackathon Registration service.
It includes the following components:
Over time, additional functionality and business logic have been added to the legacy API. Some examples are:
And more requirements mean more code!
Our technical debt and existing design patterns were making it challenging to adapt to the new requirements.
While the core functionality of collecting and storing user registration information remains constant, each hackathon event usually has its own requirements around other capabilities. Some events want to use an alternate mailing list provider. Others want to configure a unique hierarchy for teams and channels. Our API codebase quickly started filling up with conditional statements and logical branches making it nearly impossible to test and difficult to maintain.
While the American divorce rate has dropped, we knew the answer to our problem was to decouple. We want to decouple what we currently have into separate components. Each component takes care of an atomic piece of business logic and should be easily modifiable without disrupting other components. This removes the need to bake unnecessary code into our base business logic. We decided to take a spike and design out how we can accomplish that using out of the box Azure services.
Though this API is a small example we believe componentizing your architecture while it’s still small is a big step towards modernizing your architecture.
As developers, we believe in Clean Code. We have to think software architecture, similarly. Each small component is doing only what it is supposed to and then passes control to another component. This helps us to architect cleaner and sensible solutions in the cloud.
Whether you are already on the cloud or just thinking about modernizing your current architecture for the cloud, this article will hopefully give you a decent grasp of how you can go about approaching it the right way — or at least a better way. There’s no one design pattern to address all solutions of course.
Options Options Options
One of the quickest way to modernize your software is using Azure App Service. This way you allow Azure to handle all the networking and security for you. Here are some great ways to secure your App Services!
(VM is of course another route but going back to our reasoning we want to make sure things are small and manageable.)
You can either host all your messy giant application in the App Service, OR you can refactor like mentioned above and put them into separate App Services and this gets into the territory of Microservices.
Did we mention the cost can be significantly cheaper?
One step further than an app service route is the Serverless Azure Functions route. This is where Azure dynamically manages the allocation and provisioning of servers. All the benefit of the App Service but with added bonus of being only charged when it’s invoked. Scott Guthrie calls it the ‘invocation model’ where you are only responsible for chargers when the resource is called.
Now… the fun(?) part!
As you manage multiple components, event and messaging architecture becomes very important. We want our services to communicate and respond to each other. However, if we make direct calls between them, we are re-introducing strict dependencies. Ideally, these services should be able to communicate without needing any specific knowledge about what those services are, or where they are located. To accomplish this, we introduce an eventing system.
Should I use events or messages?
Whichever direction you go you need a Broker.
The broker we chose for our example is Azure Event Grid.
> Checkout the doc which explains different event-driven services in Azure
(Figure 2) represents the architecture of our refactored solution.
The core functionality of the API is still there. But we have moved all the customizable supporting features out to their own services.
In order to decouple them from the original API, and orchestrate the business logic, an Event Grid Topic has been implemented. This service receives event notifications from the API when operations are performed on the registration data.
Our supporting feature services can subscribe to these notifications to take the appropriate action when changes in the system occur.
The immediate impact of this redesign is tremendous.
You might be saying, “But now there are so many more ‘things’ to deal with!”. Yes, that is true. But the benefits FAR out-weigh the downsides here. Cloud platforms (such as Azure) provide a wealth of governance and management tools out of the box that make it easy to provision, manage, and monitor ‘all the things’.
In hindsight I guess we got to this point because we didn’t have a good design discussion when the API was being created.
But I’m sure lot of development shops do similar things. In order to push code out, the design you think is going to last doesn’t really do you any good, OR you just write code without designing at all! IT’S OK! The point is to LEARN from what you have built and try to continuously improve.
The cloud offers flexibility. A light-weight, adaptable design for the cloud is more resilient to changes in feature requirements. As your needs change and grow, you are not locked in to the server or license you bought last year, you can scale up, down, out and in whenever you need to and the cost is only for what you use.
Migrating legacy application architectures to the cloud sometimes requires a shift in perspective and thought-process. These cloud-native patterns can seem foreign and complicated at first. But with a little time and experience you will start to see the flexibility, scalability, and cost benefits of these designs.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.