Microsoft Technologies based on the .NET software framework. Miscellaneous topics that do not fit into specific categories.
Hi @Michał Śmiechowicz ,
Thanks for reaching out.
Regarding your concern, it really comes down to where the differences in your system actually live - in behavior or just in data.
If your different types (A, B, C) have different logic or behavior, then it’s better to model that at the grain level using interfaces like IGrainC : IGrainB : IGrainA. In Microsoft Orleans, grains are meant to encapsulate behavior, so this approach keeps things clean and aligned with how Orleans is designed. Each grain type can implement its own logic without needing a lot of conditional checks.
You can find more details here: https://learn.microsoft.com/en-us/dotnet/orleans/grains/
On the other hand, if the differences are mostly about data shape or stored state, and the behavior is largely the same, then using a shared grain with polymorphic state (e.g. classC : classB : classA with IPersistentState) can work well. This keeps the grain surface simpler, but you may end up adding type checks or branching logic inside the grain if behavior starts to diverge.
More on grain persistence and state: https://learn.microsoft.com/en-us/dotnet/orleans/grains/grain-persistence/
A good rule of thumb is:
- Use grain/interface inheritance when behavior differs
- Use state inheritance when only the data differs
If you’re unsure, it’s usually safer to lean toward grain-level polymorphism, because it avoids mixing multiple behaviors into a single grain and keeps things easier to maintain as your system grows.
You might also find the general design guidance helpful: https://learn.microsoft.com/en-us/dotnet/orleans/resources/best-practices/
Hope this helps! If my answer was helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.