Most Power Platform Code Apps I've reviewed eventually accumulate two, three,
sometimes five different places that each answer "what can this user do" -
slightly differently every time. One hook checks a role field. A screen
re-checks it with a different condition. A button disables itself based on a
third rule that nobody remembers writing.
By the time someone asks "can a Regional Manager delete a validated record,"
nobody can answer with certainty - because the answer isn't in one place.
The invariant
One hook answers "who am I, and what am I allowed to do." Everything else
derives from it.
function usePermissions(): { role: Role; can: (action: Action) => boolean } {
// the ONLY place that resolves identity -> role -> capability
}
Every screen, every button, every guard calls this hook - never its own
re-implementation of the rule.
Adding a dimension without breaking the invariant
The moment you need a second dimension - say, permissions that also depend on
which business entity the user is acting in - the instinct is to wire the new
logic into the existing hook. Don't. Compose a second hook on top of the
first one.
function usePermissionsForEntity(entityId: string) {
const base = usePermissions();
// combines base.role with entity-specific rules
// never calls back into itself - no cycle
}
Wiring the first hook to depend on the second (instead of composing on top)
is how you get a circular dependency that only shows up once the app is big
enough to hurt.
Why this is worth protecting
- One audit point. "What can a Regional Manager do" has exactly one place to read, not five to reconcile.
- Safe to extend. A new dimension (entity, project, region - whatever your domain needs next) is a new layer on top, never a rewrite of the foundation.
- Impossible to accidentally diverge. A button and a screen can't disagree about what's allowed if they both call the same function.
I work on Power Platform / Dataverse architecture (Code Apps, permission
models, approval workflows). Happy to discuss your project's architecture if
you're scoping something similar.
Top comments (0)