Abstract
I am modelling a generic authorization subdomain for my application. The requirements are quite complicated as it needs to cope with multi tenants, hierarchical organisation structure, resource groups, user groups, permissions, user-editable permissions and so on. It's a mixture of RBAC (users assigned to roles, roles having permissions, permissions can execute commands) with claims-based auth.
Problem
When checking for business rule invariants, I have to traverse the permission "graph" to find a permission for a user to execute a command on a resource in an environment. The traversal depth is arbitrary, on multiple dimensions.
I could model this using code, but it would be best represented using a graph database as queries/updates on this aggregate would be faster. Also, it would reduce the complexity of the code itself. But this would require the graph database to be immediately consistent.
Still, I need to use CQRS/ES, and enable a distributed architecture.
So the graph database needs to be
- Immediately consistent
And this introduces some drawbacks
- When loading events from event-store, we have to reconstruct the graph database each time
- Or, we have to introduce some kind of graph database snapshotting
- Overhead when communicating with the graph database
But it has advantages
- Reduced complexity of performing complex queries
- Complex queries are resolved faster than with code
- The graph database is perfect for this job
Why this question?
In other aggregates I modelled, I often have a EntityList
instance or EntityHierarchy
instance. They basically are ordered/hierarchical collection of sub-entities. Their implementation is arbitrary. They can support anything from indexing, key-value pairs, dynamic arrays, etc. As long as they implement the interfaces I declared for them. I often even have methods like findById()
or findByName()
on those entities (lists). Those methods are similar to methods that could be executed on a database, but they're executed in-memory.
Thus, why not have an implementation of such a list that could be bound to a database? For example, instead of having a TMemoryEntityList
, I would have a TMySQLEntityList
. In the case at hand, perhaps having an implementation of a TGraphAuthorizationScheme
that would live inside a TOrgAuthPolicy
aggregate would be desirable. As long as it behaves like a collection and that it's iterable and support the defined interfaces.
I'm building my application with JavaScript on Node.js. There is an in-memory implementation of this called LevelGraph. Maybe I could use that as well. But let's continue.
Proposal
I know that in DDD terms the infrastructure should not leak into the domain. That's what I'm trying to prevent. That's also one of the reasons I asked this question, is that it's the first time I encounter such a technical need, and I am asking people who are used to cope with this kind of problem for some advice.
The interface for the collection is IAuthorizationScheme
. The implementation has to support deep traversal, authorization finding, etc. This is the interface I am thinking about implementing by supporting it with a graph database.
Sequence :
1 When a user asks to execute a command I first authenticate him. I find his organisation, and ask the OrgAuthPolicyRepository
to load up his organisation's corresponding OrgAuthPolicy
.
The
OrgAuthPolicyRepository
loads the events from theEventStore
.The
OrgAuthPolicyRepository
creates a newOrgAuthPolicy
, with a dependency-injectedTGraphAuthorizationScheme
instance.The
OrgAuthPolicyRepository
applies all previous events to theOrgAuthPolicy
, which in turns call queries on the graph database to sync states of theGraphDatabase
with the aggregate.The command handler executes the business rule validation checks. Some of them might include checks with the aggregate's
IAuthorizationScheme
.The business rules have been validated, and a domain event is dispatched.
The aggregate handles this event, and applies it to itself. This might include changes to the
IAuthorizationScheme
.The eventBus dispatched the event to all listening eventHandlers on the read-side.
Example :
In resume
Is it conceivable/desirable to implement entities using external databases (ex. Graph Database) so that their implementation be easier? If yes, are there examples of such implementation, or guidelines? If not, what are the drawbacks of using such a technique?