As identigral has mentioned in the comments, "There's no One True Way to implement microservices".
I'm pretty new to the microservices architecture myself but here's some advice I can offer.
Avoid synchronous service-to-service calls when possible
One of the biggest benefits of the microservice architecture is that your application is composed of several small-ish loosely coupled services. This is a benefit because it makes maintenance easier. You can work on a microservice in relative isolation knowing that it is very unlikely you'll affect the larger ecosystem (as long as you don't change that microservice's public contract).
When you make direct service-to-service calls you tighten the coupling between those services and loose this benefit.
Know which services own which entities
You mentioned populating
User data when
User are actually owned by two different microservices.
Rather than duplicating the
User information in two services, it might make more sense for the
Product service to only store the subset of
User information it needs for its own purposes.
For this to work, the
Product service needs to understand that the
User service is the owner of that
User data. This means that changes to the
User information should always come from the
User service. The
Product service, if it is interested in those changes, should subscribe to events from the
User service and update its local copies as needed. I believe this approach is called "Eventually Consistent" and is a decent compromise to avoid forcing two microservices to be coupled by synchronous API calls and to avoid sharing ownership of data.
Be careful about your service boundaries
I think this is arguably the most difficult aspect of microservices. If your microservices are too large they can become mini-monoliths that are responsible for too many things and become difficult to maintain over time.
Conversely, if your microservices are too small, they just become thin views over data and defining behaviour over that data ends up being distributed awkwardly over multiple services.
Looking at your examples, I worry that you've gone down the route of making your services too small.
What I've found helpful in dealing with this is Domain Driven Design's concept of Bounded Context. In DDD, when modelling your application and its domain, you're encouraged to draw a map of all the main concepts and try to identify relatively isolated regions of data and behaviour. These relatively isolated regions are Bounded Contexts (BC) and tend to map really well to microservices.
Additionally, DDD talks about how multiple BC can have different versions of the same concept (eg,
User) without actually being duplicated data.
I'm still learning DDD but it's been a very helpful tool in navigating my first attempt at the microservice architecture. I'd recommend reading up on it.