Managing dependency on external modules in a seamless way

Introduction

Have you ever worked with a project that depends on external dependency packages throughout your application code base? Managing dependency on external modules in a seamless way is crucial, as external packages can change at any time, which can cause issues and make refactoring difficult. To avoid this problem, we can use the Adapter Design Pattern to wrap our external dependency packages with a wrapper component that maps their interfaces back to our internal ones.

In this article, we will talk about the Adapter Pattern, and how it allows us to encapsulate external dependencies and their interfaces, shielding our application code from changes in the outside world. [1]

What is Adapter Design Pattern

Adapter Design Pattern is a structural design pattern that allows you to encapsulate an existing class and adapt it to a new interface. The adapter design pattern is widely used in the computer programming and software development industries. An adapter holds the component with the additional functionality not available in the original component.

To illustrate, let’s say we have a class called Car, which stores information about cars that can run on the road. We would like to add a new feature that allows us to run the car on a railway track. For a visual representation, it should work something like this.

Source: refactoring.guru [2]

For this feature to work correctly, we need our Car class to support this functionality. Meanwhile, no methods in our Car class can handle this feature. So how do we implement this feature? The answer is by creating an adapter class that will take all of these duties for us. Let’s see a short code snippet to understand what we are talking about.

Adapter Pattern for External Dependency Changes

Let’s see a high-level overview of the approach we are talking about. In brief, assume you have a NodeJS project and it relies heavily on an external package named `log4js` to log various events. For instance, the handleAssetPublishService is an example method where log4j is being used directly.

Similarly to the above implementation, let’s say your application has several different methods different from this, which use log4js directly and all of them are distributed into different AWS lambdas for logging.

Refactoring this implementation and wrapping the log4js package inside an Adapter of our own.

We have created a LogAdapter class that serves as a wrapper for the log4js packages. As you can see the LogAdapter class implements the ILogger interface. In the constructor, we first initiate the behavior of the log4js logger object and later implementing the method bodies. With this, the modified method will look something like the one below.

Benefits of using this Approach

You might wonder why we should go to such lengths to implement the same functionality inside an adapter class. Does it not just create extra boilerplate code inside our application?

Well, think of the approach in this way, assume as we have talked about, your application may have several lambda functions which rely upon the log4js. If there is a need to change the log4js to a different logger package or update its version, the whole implementation needs to be refactored accordingly.

Without an adapter class, we will have to manually refactor our whole application where it is being used. On the other hand, with the adapter class implementation, we now just need to make changes to our adapter class and our codebase remains unaffected by the overall changes.

One of the real-life examples is the log4j vulnerability incident [3][4]. For instance, suddenly one day we hear that the log4js package has a significant security vulnerability that lets an attacker see all the log messages inside an active system. Overnight, they released a new stable version of the log4js package for the security fix. They had changed all the previous method signatures and taking new params now, making it incompatible with the current application. Without an adapter, we have to immediately change our entire application, removing the log4js implementation from every lambda, which could be a nightmare for your developer team.

But, if we have an adapter class implementation we can just refactor it inside the adapter class and our whole application will be safe from this security risk. Suppose, this scenario happens and your development team decided to change the log4js interface with another package named `pino` and `pino-pretty`, just to ensure security. All we need to do is now, change our LogAdapter class’s implementation to serve the logging functionality throughout our application.

Conclusion

As we draw to a close, putting this technique into practice within our application framework allows us to write more reusable and resilient code that is clear and manageable. Our code is isolated from external dependencies, reducing the possibility of uncontrollably disruptive changes. We can improve internal implementations thanks to this isolation, which keeps the larger application stable and scalable over time.

In our example, the Adapter pattern shows how structural design patterns can be used to solve particular use cases by allowing incompatible interfaces to work together seamlessly. However, the particular requirements and limitations of the given problem will determine which design pattern—Adapter, Proxy, Decorator, or another—is used. Every pattern minimizes system-wide impact and maintains compatibility while providing distinct benefits in terms of behavior modification, functionality extension, or interface adaptation.

Developers can future-proof their applications against changing requirements and technological advancements by carefully utilizing these design patterns. This methodology not only promotes a modular architecture that facilitates iterative improvements and raises the overall quality of software, but it also increases code flexibility.

We appreciate your time in discussing these ideas with us. We hope that this conversation deepens your understanding and provides you with insightful knowledge that will help you create software that is reliable and flexible.

Reference

Reference 1: https://betterprogramming.pub/using-the-adapter-pattern-to-secure-apps-from-external-dependency-changes-72d0f4c9d961

Reference 2: https://refactoring.guru/design-patterns/adapter

Reference 3: https://www.spiceworks.com/it-security/vulnerability-management/articles/log4j-apache-vulnerability-everything-you-need-to-know/

Reference 4: https://www.cisecurity.org/log4j-zero-day-vulnerability-response

Picture of Abdus Sayef Reyadh

Abdus Sayef Reyadh

Software Engineer-II

Hire Exceptional Developers Quickly

Share this blog on

Hire Your Software Development Team

Let us help you pull out your hassle recruiting potential software engineers and get the ultimate result exceeding your needs.

Contact Us Directly

Address:

Plot # 272, Lane # 3 (Eastern Road) DOHS Baridhara, Dhaka 1206

Talk to Us
Scroll to Top