In most cases we try to follow DRY approach - Don't Repeat Yourself. This simple yet powerful statement, which - if executed correctly - has the power of changing meh projects to good ones, doesn't work well in all scenarios and use cases. As one once said "One size doesn't fit all", you shouldn't always follow each pattern or approach blindly. You can easily get burnt.
We have following concepts in the application: Company, Group and User. User can have access to multiple Companies(from which one is his main). Group is a logical thing, which exists within a Company.
The question is - how to store data in Table Storage, so we can easily query both all Groups within a Company and a single Group.
Since GroupEntity's PK is equal to Company's identifier, we'd have to make one query per each company a user has access to. Not very smart, not very clean. What to do? Change PK in GroupEntity to a generic one? We'll lost the possibility to make fast queries for all groups within a company. Make a combined identifier and user it as a PK? We still have to perform multiple queries. Go for SQL and perform proper JOINs? This is definitely a possibility - but we don't need other features of a relative database. Is it a dead end?
One thing in Azure Storage is really cheap - it's the storage itself. How can we remodel our tables so we can improve both performance and lower transactions amount? Well, we can store our data twice!
Here we're storing a row twice but with slight changes. The first insert stores data with no changes to the previous version. The second one is the crucial one - it performs one change, which is changing PK to a generic one named "group". Thanks to this solution we can have two almost the same rows, one for being displayed as a part of a list, one for storing all info regarding a row.
Now you may ask - how it secures a row from being displayed if a User doesn't have access to a given Company? That's why we're storing company identifier along with a row in CompanyId column. This is much quickier and cleaner solution than performing several requests to Table Storage - we can cache the data locally and just check whether identifiers match.
Modeling Table Storage is both challenging and rewarding - you can easily hit performance problems if tables are not designed carefully, on the other hand wise design allow you to really push the limit. Those redesigns are important also because of one more thing - they save time. And in a cloud time = money. Make sure you pay only as much as needed.