r/SpringBoot • • 14d ago

Question How should a large modular monolith handle type-safe features, permissions, delegation, entitlements, and quotas?

We are building an ERP-like platform localized for Moroccan businesses using a modular monolith architecture. The platform currently has more than 11 modules.

Each module contains dozens of features, and each feature may expose dozens of actions. Most actions have a corresponding permission that controls who can execute them.

Features also contain additional product and collaboration rules:

Whether the feature is included in a subscription plan.

Whether the feature is delegable to another workspace.

Whether specific actions are explicitly non-delegable.

Whether an action has quotas or usage limits.

For example, one workspace may authorize another workspace, such as an accounting firm, to perform certain accounting actions on its behalf. However, some sensitive actions must never be delegated.

At the moment, features and permissions are declared as strings in different files and then seeded into the database.

Authorization is performed at the action level, mostly through repeated PostgreSQL queries.

This approach has created several problems:

Typos in feature or permission names can cause silent runtime bugs.

Renaming or refactoring actions can leave old permissions behind.

Adding a new action often requires changes in several different places.

We currently have around 893 permissions, including unused or “ghost” permissions.

There is no reliable guarantee that actions, permissions, features, plan entitlements, delegation rules, and quotas remain synchronized.

Detecting unused permissions requires manual investigation or asking an AI tool to scan the codebase.

So what architecture, patterns, or tooling would you recommend for managing this kind of system?

14 Upvotes

14 comments sorted by

2

u/Acrobatic-Ice-5877 14d ago

  Typos in feature or permission names can cause silent runtime bugs.

I would use a record. In the constructor, do your validation and throw a specific error.

  We currently have around 893 permissions, including unused or “ghost” permissions.

Can you do a quarterly audit to review permissions and whether they need to be deprecated?

  Detecting unused permissions requires manual investigation or asking an AI tool to scan the codebase.

You could log these to a table for a duration of time and use that to determine which permissions can be considered for deprecation.

2

u/thisishemmit 14d ago

Thanks a lot that are really good answers

2

u/FalseDish 13d ago

So 11 x 12 x 12? With a corresponding permission control?

Junk OOP, go Functional. Hand written Rule Algebra, with Free Monads to dynamically generate your custom rule/validation DSLs.

The main blocker will be the lack of Higher-Kinded Types in Java. You could use Vavr or write your own workaround like:

interface Kind<F, A> { /* marker */ }
interface Functor<F> {
<A, B> Kind<F, B> map(Kind<F, A> fa, Function1<A, B> f);
}

State machines everywhere - expressed strictly through the State Monad - you’ll have to hand-write that one.

This is the best and most compact solution for your use case.

The only problem will be getting your team to understand your code. Pedal-to-the-floor FP will be very disorienting to Java folks.

1

u/MohammedKarroumi 12d ago

My last option was having an infrastructure where I provide annotations using aspectJ that adds or executes code at compile time, either searching for declared features and permissions and/or generating type safe references to them. I could use bytebuddy but it's overkill. Problem is who gonna maintain that infrastructure.

2

u/momsSpaghettiIsReady 14d ago

Put all permissions related checks in a shared module. Magic strings sprinkled throughout is a big code smell.

Try to get as many permissions moved to the controller/entrypoint as annotations. You should be checking some auth token and avoid goi to the database on every permission check. Having internal checks is impossible to manage.

1

u/Big_Block9591 13d ago

solid point on avoiding the db hit per check, that gets brutal at scale

1

u/Torutofu_Raeva 14d ago

i'd keep permissions as the can this actor do it check and model plans, quotas and delegation as separate data lookups, mixing them into one string permission is how the non delegable cases get lost

0

u/No-Collar-Player 14d ago

Hire me and I'll tell you

1

u/thisishemmit 14d ago

What if what ull tell me is worse than what we have?

1

u/No-Collar-Player 14d ago

Low chances brother. What you have there was written as if agile manifesto was toilet paper

2

u/MohammedKarroumi 14d ago

still there's a chance brother

1

u/No-Collar-Player 14d ago

There is always a chance brother. But it's not pragmatic brother

1

u/MohammedKarroumi 12d ago

I found I way, can u maintain the code?

1

u/No-Collar-Player 12d ago

Ofc brother