How to make use of the render placeholder system for cacheablejsonresponse? I have custom controllers which provide CacheableJsonResponse responses. Those responses are quite heavy to build (couple of seconds), so I want to build them better. I build those responses by getting the entity object(s), normalizing its content—referenced entities included—and putting the data directly into the response object.

My JSON responses varies by user, checks the role access, and includes some user related content (e.g. flags). I have tried to put a cache context on user (and url) on the CacheableJsonResponse, but this is not a viable way to handle cache, from my perspective; it would create a cache entry for each user and url request and would be ineffective (the cache will HIT only when the same request is asked twice or more by the same user).

Standard Drupal renderer seems to include a placeholder system which makes the majority of the page cached with proper cache context+tags, and the little parts with high-cardinality/invalidation rate rendered last out of the cache system (from my understanding) and then would be useful for my needs.

How can I easily reproduce the same system for my controllers? Is there any standard mechanism/pattern out there?

Reference: https://www.drupal.org/docs/8/api/render-api/auto-placeholdering
The only trade-off I have found is to implement custom tags (per user) and put the response data array in cache so I have control over the conditions of cacheability at the controller level. I build the cacheability metadata (context + tags) with the help of a service which registers the metadata of the content which is currently normalized. And when the normalization ends, I merge the metadata onto the response and put the response in a cache backend.

And when the controller is called again, I try to retrieve the cached response and returns it if not invalidated.

Internal and dynamic page cache still works for the other controllers (i.e. standard rendered pages) without removing the high invalidation tags from the services.yml file.

Feel free to add better answers than this one.

Tested on Drupal 8.6.7.

Side note

Because I was using flag, I have had to implement cache tags pairs: userid+flaggingid. This way, we will not invalidate the caches of all users whenever a single user flags or unflags a content. This does not answer this question, but I thought it could be useful to include in your reflections for your own cache system.