Apply a default filter per relation for a multi tenant app

Hi,

I am working on a multi tenant app, so I need some relations to be filtered by tentant.

So some of them will have a tenant_id and right now I have a method in the relation for_tenant that I need to chain to all calls.

Is there a way to make this filtering default? so that the tenant filtering applies to all queries for a relation. Something like dataset but the tenant_id is request scoped.

I’m not sure if I understand well your question, but rom itself won’t help you with anything related to the http layer, because it is not its responsibility and it would be a smell of a bad design breaking a clean architecture. The web interface is at the outermost part of the typical architecture circle, so you have to look it in the other way. You have to find a way to instruct the persistence layer from the request layer (or any other below).

Poor description on on my part.

The problem is I have relation that has a tenant_id column, I want to always aply a where clause on this column.

Of course I can add a method in the relation and chain it, but don’t want to have to rember calling this method every time.

Does it make more sens now?

As you said, for default datasets you can use dataset on your relation. But from there you have no way, and you should not, access your request layer. You need to provide something from the request layer to the persistence layer, and not having the persistence layer taking something from the request layer. Soon or later this coupling would cause you troubles when dealing with your layers in the middle.

Just try to deal with it in its context. For example, if your repository function requires the argument you won’t never forget to provide it from your controller. Maybe you can also rely on some sort of custom made relation plugin to add a consistent behaviour.

Have had similar experiences to this as well. We ended up just having access methods on the repo.

However, recently I had the idea of requiring the repo to be “opened” with an context:

StoreRepo.open_for_tenant(tenant_id)

This would then filter the root of the Relation.

Thanks for the reply, I ended up with a slightly different solution.

I’ve added a module:

    module Organizational
      extend ActiveSupport::Concern

      included do
        def for_organization
          organization_id.nil? ? self : where(self[:organization_id] => organization_id)
        end

        private

        def organization_id
          Api::Container['organization_id']
        end
      end
    end

Then in a base repository I overwrite root and check the module inclusion

  class Repository < ROM::Repository::Root
    include Import.args['persistence.rom']

    def root
      super.respond_to?(:for_organization) ? super.for_organization : super
    end
  end

This means that I can simply use root in the repo now and it will filter by default.

NOTE: One improvement is that I can use a little meta programming to overwrite the relation name also and do a similar check like for root