2

Let's imagine I have a single web app containing features which are topically related, but have highly differing implementation requirements.

Let's imagine that this web app is about fruit, and contains the following features:

  • A fruit encyclopedia giving details about various kinds of fruit, including full 3D models that can be inspected.
  • A game where you can throw fruit at a wall and watch it smush, again using 3D models but also full physics.
  • A marketplace for purchasing different kinds of fruit.

This seems like a prime opportunity for a microservices-based architecture. As microservices are meant to be separated into bounded contexts each of which implements a cohesive set of functionality, it makes sense to have one microservice for each of the three above features, providing its backend. A fourth microservice is then added to provide the actual web app UI frontend, to be filled in with data from the other three services. Finally, we want some sort of API gateway / load balancer which handles all client requests and redirects them to the appropriate service.

This will look something like the following: Four services plus gateway diagram

However, these services aren't as easy to separate as it first appears. I see two main issues:

Issue 1: code reuse

Both the encyclopedia and the game require 3D models of fruit, although the encyclopedia wants to add wider information, and the game wants to add physics simulation. According to traditional DRY, one would factor out the duplicated functionality as a shared library. However, in microservices this can lead to tight coupling as changing the shared library affects both services.

I can think of three solutions:

  1. Ignore DRY and just implement the same functionality in both. This keeps them nicely separated, but causes duplication of work if e.g. a bugfix in the common functionality needs to be applied to both.
  2. Embrace DRY and use a shared library. If a change is needed, upgrade the version used by each of the services separately as necessary. Accept that the services may end up running different versions of the same library, or you'll often be making changes to both together.
  3. Embrace microservices and create yet another one to serve 3D models of fruit. If the implementation is hidden behind a generic API, then implementation changes and bugfixes shouldn't affect either service using it, as the API contract is still fulfilled. However, depending on the technologies in use, generalising the 3D models in this way may not be feasible or performant, leading to tight coupling and effectively devolving into a slower and less flexible option (2).

Under what situations would each of these methods be appropriate? Is there another method I have not thought of?

Issue 2: containers

Containers are an obvious and powerful tool for implementing microservices. However, they are not synonymous with microservices, and as far as I've been able to determine, the relationship between the two is hazy at best. From my research, best practices state both that one container should implement one microservice, but also that one container should only house a single process or responsibility.

A single microservice likely still contains several components; for example, the encyclopedia and marketplace likely both want some sort of database as well as their business logic.

If the logic and the database are placed in separate containers, then there is no longer a 1:1 mapping between containers and microservices. Proliferation of containers also leads to lots of inter-container communication, which slows things down. Containers cannot necessarily be guaranteed to be co-located, so requests between must be encrypted in case they pass over the internet (I think, correct me if I'm wrong). The formation of requests, translation between different APIs, encrpytion, and the transmission time itself all add overhead.

If the logic and database are placed in the same container, then there is no longer a 1:1 mapping between containers and processes. This makes it harder to scale them independently, in case the logic is very simple but requires enormous data storage, or vice versa. One must also build and deploy them together.

How should microservices be divided into containers under this scenario? Are there genuinely good alternatives to containers?

  • "changing the shared library affects both services" ... I'm not sure it would. Just use semantic versioning to add new features or changes to the shared lib. Then each project is free to upgrade when needed. – Dogweather Jul 29 at 21:51
  • That is solution (2) above. – Christopher Riches Jul 31 at 6:51
  • I'm not even convinced that two separate services using the same shared library is a concerning form of coupling. In fact, I've never even heard of this being called coupling. If anything, both services are coupled to the shared library, but they are not in any way coupled to each other. Changing service A does not require changing service B. Think about it: If classes A and B both depend on C, are A and B somehow tightly coupled in a concerning manner? – Nerdizzle Aug 9 at 20:00
  • Perhaps you're right. I was coming from the idea of "if you make a single change that affects multiple microservices, then they are not properly separated", but may have taken it too literally. – Christopher Riches Aug 10 at 12:05
0

microservices are meant to be separated into bounded contexts each of which implements a cohesive set of functionality

Has it occurred to you that "bounded contexts each of which implements a cohesive set of functionality" is a phrase that describes a lot of things? You could use the exact same term to describe when functionality should be separated into classes, or into assemblies, or into applications.

Microservice is a physical, not logical, concern. An architect will separate things into microservices so that they can deploy them independently, meaning that they can version independently and scale independently. The sort of deployment agility improves uptime and performance and reduces deployment risk.

You seem to be conflating the purpose of microservices with logical architecture, i.e. object model. I would avoid that. You can just as easily achieve logical separation by separating features into different source code folders and deploy them as a single service.

I suggest you spend some time to analyze what deployment concerns may actually arise, and design your microservice boundaries around those.

  • Ok. Let's imagine that the encyclopedia is slow-changing and lightweight, the game is slow-changing and heavyweight, and the marketplace is fast-changing and medium-weight. Thus, they have different scaling and deployment requirements, and microservices are applicable. Given that, how would you answer the questions I have raised? – Christopher Riches Jul 18 at 15:35
0

After wider research, thinking, and taking the other answer and the comments into consideration, I have arrived at the following conclusion:

For issue 1, using a shared library is perfectly valid and the ideal solution here, as it avoids code duplication without actually coupling the containers, as long as proper encapsulation and semantic versioning practices are followed.

For issue 2, one container should only ever hold one process. This is because otherwise it is hard to scale or deploy the components independently, and their lifecycles are no longer bound to that of the container - one process could crash, and the container might still look healthy from the outside. In addition, it will likely be difficult to communicate with any process not running in the foreground of the container due to the way signals and stdout are expected to function.

If multiple containers/processes need to work together, this should be achieved by some kind of orchestration system, and any dependencies should be configured at runtime, e.g. by using an environment variable for the URL of the database. If one is going full cloud-native though, it may be worth using managed services instead of hosting your own databases, especially due to the inherent statelessness of containers and statefulness of databases.

In short, a "microservice" is a higher-level abstraction than a container or single process, and is defined more by what it does than how it does it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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