We are implementing (and now maintaining) an online shopping web application in a microservice architecture.
One of the requirements is that the business must be able to apply rules on what our customers add to their cart, in order to customize their experience and the eventual order. Quite obviously, a business rules engine had to be put in place, and we implemented a specific "microservice" for this (if we could still call it so).
Over the course of a year, this rules engine has become more and more complex, requiring more and more data (e.g. content of the cart but also user information, his role, his existing services, some billing information etc.) to be able to compute those rules.
For the moment, our
shopping-cart microservice is gathering all this data from other microservices. Even though part of this data is used by
shopping-cart, most of the time it is mainly used to feed the rules engine.
Now arrives the need for other applications/microservices to reuse the rules engine for similar requirements. In the current situation, they would thus have to transmit the same kind of data, call the same microservices and build (almost) the same resources to be able to call the rules engine.
Continuing as is, we will face several issues:
- everyone (calling the rules engine) has to reimplement the fetching of the data, even if they don't need it for themselves;
- the requests to the rules engine are complex;
- continuing in this direction, we will have to transport this data all around the network for many requests (think of μs A calling μs B calling the rules engine, but A already has some of the data the rules engine needs);
shopping-carthas become huge due to all the data fetching;
- I probably forget many…
What can we do to avoid these troubles?
Ideally, we would avoid adding more complexity to the rules engine. We must also make sure that it does not become a bottleneck – for example some data is rather slow to fetch (10s or even more) so we implemented pre-fetching in
shopping-cart such that the data is more likely to be there before we call the rules engine, and keep an acceptable user experience.
- Let the rules engine fetch the data it needs. This would add even more complexity to it, violating the single responsibility principle (even more…);
- Implement a proxy μs before the rules engine to fetch the data;
- Implement a "data fetcher" μs that the rules engine calls to fetch all the data it needs at once (composite inquiry).