Enabling code reuse

In today's software development landscape, code reuse and maintainability are essential for faster delivery and lower costs. A mono-repo is a software development approach that allows multiple projects to share a single codebase, enabling code reuse and improving code maintenance. This can contain multiple packages or modules, and each package can have its own dependencies, tests, and documentation with the benefit of a standardized dev environment, pipeline and linting that comes with the mono-repo.

In this post, we will explore the benefits of an internal Node package mono-repo containing a library of "adapters", "clients" and "utils".

But first

  1. Client: A client is a package or module that facilitates connections to a downstream system or API. A client contains no business logic and is used to abstract away the complexity of making requests to an external system. Clients are typically lightweight and can be easily mocked for unit testing.

  2. Adapter: An adapter is a package or module that wraps one or more clients and adds business logic to the response data. Adapters use the types and data structures defined in the client packages to ensure that the data they receive is compatible with the downstream system. Adapters can also be easily mocked for unit testing. They return specified types, making them easy to predict and safe to consume.

  3. Utils: Short for "utilities", is a package or module that contains basic, commonly used functionality such as logging, error handling, or utility functions that can be used in any Node.js-based project. Utils packages typically don't have any external dependencies and can be used in any TypeScript project.

Code Reuse

A set of Node packages allows for code reuse across multiple projects, making it easier to maintain and update code. In our case, having a mono-repo with "adapters", "clients", and "utils" enables us to reuse business logic, connection to clients, and utility functions across multiple projects.

For example, suppose we have a project that needs to connect to an HTTP API and process the response. We can use the "http-api-client" client package in the mono-repo, which wraps Axios and provides a HttpResponse type. We can also use the "utils" package to log any errors or debug information. Additionally, we can create an "adapter" package that uses the "http-api-client" and adds our business logic to process the response.

Here's an example of how we can use the "http-api-client" package in our project:

Improved Code Maintenance

A mono-repo can also improve code maintenance by making it easier to patch and update code. Instead of having to update each project separately, we can make changes to a package in the mono-repo and have it reflected in all projects that use it.

For example, suppose we discover a security vulnerability in the "http-api-client" package. We can fix the vulnerability in the mono-repo and publish a new version to our private NPM registry. Then, we can use GitHub actions to notify all projects that depend on the "http-api-client" package to update to the latest version.

Simplified Mocking

Both "clients" and "adapters" can be easily mocked, making it easier to write unit tests for our code.

For example, suppose we want to write a unit test for our "user-adapter" package that uses the "http-api-client" to fetch user data. We can mock the "http-api-client" and its response to test our business logic.

Here's how that looks in our unit test:

Downsides

While using this internal Node packages pattern can bring many benefits, there are also some downsides to consider:

  1. Increased complexity: It can require additional effort to set up the development environments, maintain package dependencies, and manage releases across multiple packages and projects.

  2. Tighter coupling between packages: Since adapters depend on specific client packages, changes to those clients can affect adapters and vice versa. This can make it more difficult to make changes to the packages without breaking existing functionality. We tend to always make sure functionality is backwards compatible.

  3. Potential for versioning issues: With multiple packages in a mono-repo, versioning can become more complex. It can be challenging to ensure that all packages are updated to the correct versions and that the correct version of each package is used across all projects.

Despite these potential downsides, our development teams have found that the benefits of using clients, adapters, and utils packages outweigh the costs. It's important to carefully evaluate your team's needs and weigh the benefits and drawbacks before making a decision.

Final thoughts

With this library of internal Node packages, we can reuse code across multiple projects, make changes in one place, and have those changes reflected in all projects that use it, see: using github actions to trigger actions across repos. Additionally, the easy mocking of "clients" and "adapters" makes it easier to write unit tests for our code.

The views expressed on this blog post are mine alone and do not necessarily reflect the views of my employer, Optus Administration Pty Ltd

Previous
Previous

Flutter @ amaysim - Journey & Wins

Next
Next

Orchestrating Multi-Domain Processes Using AWS Step Functions