user714241
user714241

Reputation: 380

Extend database for multi-tenant application

We're rebuilding a multi-tenant asp.net web app and I'm having trouble nailing down the most appropriate database structure. I've read loads of questions/answers on SO and the Microsoft multi-tenancy architecture article, but can't seem to find the info I'm looking for.

The application has 2 types of end users:

Issue:

My inclination is to use Name-Value pairs for the database fields so we can extend the database as needed for each corporation. My questions:

Input/answers greatly appreciated.

Upvotes: 0

Views: 694

Answers (1)

Aaron Bertrand
Aaron Bertrand

Reputation: 280350

I've done EAV solutions in the past, and they work quite well. I think this could work for your situation if I understand it right (that the vendor-corporation relationship is many-to-many / all-to-many). I've written about the pros and cons of EAV here:

Whether this is appropriate for you will depend on a couple of things. Are the "additional fields" you mention a relatively fixed set, or are they added on demand? If they are a relatively fixed set, then you could just have an access (not Microsoft Access) table that tells whether each set of columns is relevant for that vendor-corp pair.

CREATE TABLE dbo.Corporations
(
    CorporationID INT PRIMARY KEY,
    Name NVARCHAR(255) NOT NULL UNIQUE
    -- ... other columns ...
);

CREATE TABLE dbo.Vendors
(
    VendorID INT PRIMARY KEY,
    Name NVARCHAR(255) NOT NULL UNIQUE
    -- ... other columns ...
);


CREATE TABLE dbo.AdditionalColumnSets
(
    ColumnSetID INT PRIMARY KEY,
    Name NVARCHAR(255) NOT NULL UNIQUE -- e.g. Insurance
    -- ... other columns ...
);

CREATE TABLE dbo.AdditionalData
(
    VendorID INT, -- foreign key here
    ColumnSetID INT, -- foreign key here
    ColumnName NVARCHAR(255),
    ColumnValue NVARCHAR(2048),
    -- you may want to extend this to store string, number, date
    -- data differently
    PRIMARY KEY(VendorID, ColumnSetID, ColumnName)
);

CREATE TABLE dbo.AdditionalDataAccess
(
    CorporationID INT, -- foreign key here
    VendorID INT, -- foreign key here
    ColumnSetID INT, -- foreign key here
    HasAccess BIT NOT NULL DEFAULT (1),
    PRIMARY KEY(CorporationID, VendorID, ColumnSetID)
);

-- now, you can check for HasAccess in this table
-- you can also infer from lack of being in this table
-- whether that means they have access or they don't
-- have access to a particular column set.

-- ultimately, after you got hte base data from the
-- standard tables like Vendors, the query would look 
-- something like this, if presence in the 
-- AdditionalDataAccess table is required:

DECLARE @CorporationID INT = 1, @VendorID INT = 1;

SELECT
    ColumnName,
    ColumnValue
FROM
    dbo.AdditionalData AS ad
WHERE
    VendorID = @VendorID
    AND EXISTS
    (
        SELECT 1
            FROM dbo.AdditionalDataAccess
            WHERE ColumnSetID = ad.ColumnSetID
            AND CorporationID = @CorporationID
            AND VendorID = @VendorID
            AND HasAccess = 1
    );

-- you'll have to pivot or transform in the client to
-- see these as columns instead of rows

Hope this is useful and makes sense.

Upvotes: 1

Related Questions