When you are modernising a software application the architecture patterns you will use to evolve from where you are to where you need to be are just as important as your target, end-goal architecture.
Rebuilding, rearchitecting, refactoring and replacing old software isn’t new. It’s been around as long as software development has been around.
But, finding a list of architecture patterns that you can use in the context of App Modernisation is a little harder than it should be. So, this post attempts to provide a list of architecture patterns for app modernisation as well as some thoughts on each one.
The architecture patterns for app modernisation are:
- Strangler
- Facade
- API Encapsulation
- Backend for Frontend
- Eventify
- Microservices
- Micro-Front Ends
- Multi-monolith
- Parasite
In a rebuild or replacement you may look at many other architecture patterns that aren’t listed here. However, this list tries to be architecture patterns that are particularly helpful for when you have existing, legacy code to work with.
You’ll also notice that there are some similarities and overlap between each architecture pattern. This list is more to ensure you have all the options.
#1: Strangler Pattern
The Strangler Pattern, sometimes called the Strangler Fig Pattern, is an approach to incrementally isolating and replacing or rebuilding functionality slices of the legacy application.
The Strangler Pattern was first popularised by Martin Fowler after he observed an Australian tree called a strangler fig. The strangler fig wraps itself around a host tree and gradually strangles it, bit by bit, until the strangler fig replaces the host.
The Strangler Pattern essentially reduces down to a small team, making incremental changes with value to the user and business in mind. An early paper on it provides some key rules of thumb:
- Don’t reproduce legacy code
- Always ask the users what the problem is
- Refactor a legacy application by delivering business value
- Incrementally build trust – prove that you can do the hardest part of the system
- Build a small, self-selected team
- Don’t get hung up on process
- Involve the whole team with large refactorings so the team can move on as quickly as possible
- Effective teams need break points
- Treat politics as a user requirement
- A system that connects to a legacy system must be tested using live feeds
- Engage users and they not only won’t they turn it off, they will fight some of your battles for you
- Keep giving a good team motivated by giving them new hard problems – don’t waste a good team
#2: Facade Pattern
The Facade Pattern is about providing a simple interface to a complex system.
This might be creating a new layer of access on top of your existing system or it might be refactoring the existing system itself to provide simpler interfaces.
The key for this pattern is to keep the layer as thin as possible. Sometimes this isn’t possible (technology doesn’t allow it) or feasible.
Once you start building genuine functionality into the Facade you’re probably shifting into a different pattern or just doing a regular modification to the existing system.
#3: API Encapsulation Pattern
With the API Encapsulation Pattern you provide an interface to the existing system.
Generally, you’re taking an old interface and doing whatever you need to do to convert it into a modern API. This usually involves building some functionality, either to do the translation between the old interface and the new API you require or to simply create a new function that is needed.
This pattern can form part of your Strangler Fig, just remain as a sort of workaround or form the beginning of a complete rebuild.
#4: Backend for Frontend Pattern
The Back End for Front End Pattern, or just BFF Pattern, is where you build a new system that has the purpose of doing whatever is required to provide your front end applications (e.g. mobile app or web app) with simple APIs that meet their specific requirements.
You can reuse this pattern to serve any application, it doesn’t just need to be a web or mobile app.
The benefit to this approach is that you can make development of your front end apps much faster, you can improve security and provide separation between older processes and newer technology.
The challenge with this approach is that it often can only take you so far. You will inevitably bump into the limitations of the older systems sitting behind the scenes.
You need to be careful with this limitation because you can end up embarking on a modernisation without doing it consciously and then be caught with not just your original legacy system but now another messy system in the Backend for Frontend.
#5: Eventify Pattern
The Eventify Pattern is where you use events to modernise some of all of your legacy application.
There are a two patterns that are most relevant to app modernisation:
- Consume and Broadcast: This is for when you have an existing system that has become a bottleneck. Rather than have a downstream app call your legacy system, you have the legacy system produce events that are then broadcast to consumers.
- Scheduler: When a service or slice of functionality is failing or likely to fail due to temporary errors, you may want to take the function and use events to more resiliently process the event and function.
You can also transition these into a pure or primarily event driven architecture.
Where events can be slightly challenging is with modernising transactions or transaction-like operations. Particularly if the rest of the existing system isn’t in the right state.
#6: Microservices
The Microservices Pattern is where you build small independent services that communicate over well-defined APIs. These small services tend to focus on discrete slices of functionality.
In relation to app modernisation, you can start carving out small slices of functionality from an existing system. You can continue to do this incrementally until you’ve replaced the old system.
A more in-depth discussion is beyond the scope of this post.
#7: Micro Frontend
The Micro Frontend Pattern is where you build small frontend components embedded within a larger frontend. These small components are discrete, independently deployable and as loosely coupled as possible from the frontend.
Just like with microservices, you incrementally replace and improve components of an existing frontend.
A more in-depth discussion is beyond the scope of this post.
#8: Multi Monolith
The Multi Monolith Pattern is where you duplicate your monolith and run it as multiple services in parallel.
This is often not possible at all. Sometimes it’s possible but it comes with significant costs, such as extra licensing and compute.
Where it is possible, some of the aspects that make it interesting are:
- Reducing risk of impact of changes by running a small segment of users against the monolith you are refactoring rather than your whole user base.
- Having a point of comparison to see if your newly developed functionality produces the same end results as the old system.
- Treating the monoliths like microservices by only calling specific functions from each (as you would a microservice) and gradually removing unused functionality until you have a microservice.
#9: Parasite
The Parasite is where you attach whatever you can to a legacy application to extract the data and functions you need.
It’s called a Parasite because you’re likely using unorthodox methods and the ‘owner’ of the legacy application doesn’t particularly like what you’re doing.
This is often needed when a vendor won’t collaborate, another team in your organisation is too busy, or when a more considered modernisation isn’t possible.