1

I am developing a web app along with a backend API. The backend however must talk to other services, some of which are internal and others are not.

In an ideal scenario we would spend time and have a local copy of all services running, but this is not possible due to; some services being 3rd party and other internal services are not accessible for all team members.

I would therefore like to know how best to handle this when we cannot have a copy running locally, and interacting with the actual hosted services is not a good idea due to data sync issues.

I would really like to avoid peppering the codebase with if(local) executeMockCall(); but I am not clear what other options there are.

2 Answers 2

1

Invert your dependencies: make the external service into a plugin to your application. Then, attach different implementations depending on whether you're running in a production environment or a development environment.

First, define an interface through which your application communicates with the third party service. With interface I mean anything from a bunch of functions in your code to a REST API, the important point is just that you can select an implementation of this interface at run time. For example, you could provide an environment variable that contains an URL that points to a microservice that provides the expected interface. Or, you could have an if/else at the beginning of your program that selects one of two classes.

Then, create two implementations of your interface:

  • The real implementation that translates your operations to the third party service.

  • The testing implementation that produces dummy results.

The point is that instead of choosing the real or mock implementation throughout your code, that your main code remains agnostic to which implementation it is talking with. This is more testable, and also much less error-prone. The decision to use a particular implementation is made once, and preferably outside of the app. Instead, the choice should be made by the environment in which the app is being run.

Related concepts:

  • Configuration and backing services in the 12-Factor App, a guide for developing SaaS style software.
  • The idea to avoid dependencies from your business logic to external components is also part of the idea of the Onion Architecture: “Inner layers define interfaces. Outer layers implement interfaces”. This style of architecture has substantial overlap or is equivalent with hexagonal architecture (ports and adapters) and clean architecture.
  • On a lower level, replacing repeated if/else with multiple classes is known as the replace conditional with polymorphism refactoring.
1

Depending on what purpose the external services serve in your application, you may enable/disable them via some configuration.

For optional services this may even prove useful in production environment when one of the services isn't available for some reason.

If a service is really essential but must be mocked in dev environments (for example, an authentication subsystem) this might still work (the configuration option wouldn't express "enable/disable this service" but "use mock instead of real thing").

If misconfiguration might cause data corruption in the external services, the configuration needs to be protected properly, for example it might be read from a read-only configuration store instead of local config files that could be edited by the user, so you would still need to distinguish between dev and prod situations, but that could be done within the configuration module and does not need to be spread all over the code.

3
  • Thanks, but I am not sure this gives me enough information. Configuration makes sense, but what happens in the code? You still have if (GLOBAL_CONFIG_FLAG) doMockStuff(); in your code. Right?
    – dendog
    Commented Jan 6, 2022 at 14:53
  • 1
    That depends on your architecture. When you have configurable options, it often makes more sense to use dynamic techniques. Think of plugins which hook into specific events to perform their tasks - the main application only needs to provide the events to hook into, but does not need to know whether there are zero, one, or several plugins handling these events. No flag checks, but of course you need to have these hooks instead. Commented Jan 6, 2022 at 15:00
  • Thanks Hans, would you be able to provide some basic example or maybe a reference to this approach? I understand from a high level but would love to see an example.
    – dendog
    Commented Jan 10, 2022 at 16:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.