Current situation
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.
New requirements
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-cart
has 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.
Some ideas
- 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).
shopping-cart
, but we could quite easily adapt it for the needs of the other microservices (they are still related to users, products & ordering). As we see it, they will need the same input data, especially as the business is able to choose the predicates to apply. All data is provided by other microservices except the cart content itself. Fetching the data is not complex per se but it becomes complex when you have to call ~10 other microservices and maintain the structure expected by the rules engine.