Question

I am trying to understand how the read side of CQRS can work with a large document management application (videos/pdf files/ etc) that we are writing.

We want to show a list of all documents which the user has edit permission on (i.e. show all the documents the user can edit).There could be 10,000s of documents that a particular user could edit.

In general I have read that the a single "table" (flat structure) should suffice for most screens and with permissions you could have a table per role.

How would I design my read model to allow me to quickly get the documents that I can edit for a specific user?

Currently I can see a table holding holding my documents, another holding the users and another table that links the "editing" role between the user and the documents. So I am doing joins to get the data for this screen.

Also, there could be roles for deleting, viewing etc.

Is this the correct way in this case?

JD

Was it helpful?

Solution

You can provide a flat table that has a user id along with the respective denormalized document information.

SELECT * FROM documents_editable_by_user WHERE UserId = @UserId
SELECT * FROM documents_deletable_by_user WHERE UserId = @UserId
SELECT * FROM documents_visible_for_user WHERE UserId = @UserId

But you could even dynamically create a table/list per user in your read model store. This becomes quite easy once you switch from a SQL-based read store to NoSQL (if you haven't already.)

Especially when there are tens of thousands of documents visible for or editable by a user, flattened tables can give a real performance boost compared to joins.

OTHER TIPS

When I had a read model that took the form of a filtering-search-form (pun not intended), I used rhino-security as the foundation of an authorization service.

I configured the system so that the authorization service's tables got pushed through SQL Server's pub-sub system and SQL Server Agent, to the clients that were partially displaying the denormalized data - I then let Rhino.Security join the authorization model together into the read model, on a per-user basis.

Because I essentially never wrote to the read model's authorization tables from the read model, we got a nice encapsulation on the authorization service's database and logic, because authorization was only changed through that service, and it was globally unique and specific (consistent) to that service. This meant that our custom GUIs for handling advanced (hierarchial entities, user groups, users, permissions, per-entity-permissions) authorization requirements could still do CRUD against this authorization model and that would be pushed in soft real time to any read model.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top