This article is based on a real story that happened during an integration project I worked on a few months ago.

During our daily work as Software or Solutions Architects, the moment we design a solution, we worry about non-functional requirements such as security, scalability, and performance. But too often, this thought process only occurs at the beginning of a project or in the initial weeks of the development process.

As usual, we would like to stay vigilant to prevent possible issues or bottlenecks during our deliveries. It’s important to stress the scope and avoid unpleasant surprises during production deployment. Even when deploying a small MVP, architects want to deliver something that does not break after the first run.

Architects work closely with developer teams during the implementation, deployment, and initial operation phases. But, often, we must follow many projects simultaneously. Thus, we may lose attention to a specific solution in the weeks following its initial deployment.

The Scenario

And then comes the moment that your customer calls you for a meeting and says, “Hey, let’s think about a Phase 2?”. Here is where my article touches on the point that I’d like to share with you all. When this situation happens, as a Software or Solution Architect, do you even think that your initial design supports Phase 2, or do you review your solution?

For example, let’s imagine this scenario:

“Our project is a microservice component developed in Java. It integrates with a Message Broker and a File Server. This component reads a message from the Broker and generates a physical file to be sent and stored on the File Server.”

Initially, this scope is not that complex — it’s a small Java application deployed on a cloud environment that reads a queue and delivers a generated file to an external server. This process is a synchronous solution, even if it delivers what the customer desires.

Expanding Scope With A Design Review

Now, let’s expand our scope to consider the customer’s desires for Phase 2. At this point, the customer wanted to schedule the routine that delivers the physical file to the File Server and send these files in a batch, not one by one as before.

So, do I implement this Phase 2 without thinking much, or do I review my entire design at this point?

Most Architects are not allocated to only one project — we are most often supporting many other projects, even if they are new MVPs or just an enhancement of pre-existing production software.

As this was my situation during this case, I had to pause all other projects and review this scope once again. Once doing so, I realized that my initial synchronous solution was not enough to implement this change. If I had just told the developer team to implement what the customer desired, we would have generated more coupling in the solution and compromised all other non-functional requirements.

So, I proposed to “split” our solution into two components. We separated the functionality that reads the queue and generates the physical files into one microservice. The functionality that gets the files (or the list of the files) and sends it to the File Server was allocated to another microservice.

With this strategy, we were able to deal with possible issues and treat failures separately, thus decreasing the coupling of our overall solution. And the most important thing — we once again delivered our customer needs without impacting any side of the integration and the performance.

Preparing For Phase 3

After our second delivery, the customer then proposed a Phase 3 for the project. They wanted to be able to read the message information. Now, all the messages from the queue had to be sent to another component to store it for future development (a Dashboard, for example).

Just as we did before, we reviewed the solution. We concluded that since we split our solution during Phase 2, this new implementation would be less complex, since we decreased coupling by no longer working with a synchronous solution.

So, we added a third microservice to read the information and store it in a database. It could also send the message information to another message broker or deliver it to a third-party partner.

Again, after the first design review, we continued to deliver value to our customer without impacting the operation of something already in the production environment and running well.

Conclusion

The main goal of this article is not to discuss the specific solution (see that at Phase 3, we have the opportunity to improve the process with better solutions). Instead, I want you to pay attention to the architecture and design improvement.

As I said at the beginning of this article, as Architects, we are always trying to stay a step ahead to prevent software issues. But, our role is also to stay close to new functionalities. Part of this is keeping in mind that our original designs are not written in stone and must be improved upon as time goes by.

Loose coupling is a subject most related to microservices implementations, where we build around domain boundaries (DDD aka Domain Driven Design) or apply other techniques to slice our legacy system.

If we consider the Single Responsibility Principle from SOLID, we’ll have another powerful tool to amplify our vision about the architecture. This can help avoid issues with non-functional requirements during the implementation of new projects or even when evolving solutions that are already in production.

Software architecture must be treated just like any other process using a lifecycle strategy. Software Architects also must measure, monitor, and improve the architecture as needed to keep delivering value to customers without impacting what is already running and in good shape.