Na minha experiência, quando os desenvolvedores tentam tornar seu sistema realmente "dinâmico", eles estão realmente tentando codificar problemas nos quais ainda não pensaram. Isso geralmente é um caminho ruim a tomar. É realmente muito trabalho extra para um módulo incluir duas tabelas em vez de uma?
Em todos os casos em que eu vi o padrão (ou anti-padrão?) de tentar fazer uma tabela genérica "faz tudo" ela caiu de cara no chão. Os RDBMSs funcionam melhor com áreas problemáticas bem definidas. Se o módulo precisar manter o histórico, o módulo deve adicionar uma tabela de histórico para acompanhar a própria tabela. Isso também tem uma grande vantagem, pois no futuro você provavelmente desejará manter diferentes tipos de informações no histórico, dependendo da tabela ou módulo para o qual o histórico está sendo mantido. Se você tiver uma tabela de histórico genérica, isso se tornará muito mais difícil.
Agora, se você deseja simplesmente capturar o último usuário a atualizar ou inserir um item específico (linha da tabela) e que pode estar em várias tabelas, você pode usar um padrão de herança onde você tem uma tabela pai e várias tabelas filhas. Por exemplo:
CREATE TABLE Audited_Items
(
id INT NOT NULL IDENTITY,
CONSTRAINT PK_Audited_Items PRIMARY KEY CLUSTERED (id)
)
CREATE TABLE Articles
(
id INT NOT NULL,
[Article specific columns]
CONSTRAINT PK_Articles PRIMARY KEY CLUSTERED (id),
CONSTRAINT FK_Articles_Audited_Items FOREIGN KEY (id) REFERENCES Audited_Items (id)
)
CREATE TABLE Media
(
id INT NOT NULL,
[Media specific columns]
CONSTRAINT PK_Media PRIMARY KEY CLUSTERED (id),
CONSTRAINT FK_Media_Audited_Items FOREIGN KEY (id) REFERENCES Audited_Items (id)
)
CREATE TABLE Audit_Trail
(
audited_item_id INT NOT NULL,
audit_datetime DATETIME NOT NULL,
user_id INT NOT NULL,
[audit columns]
CONSTRAINT PK_Audit_Trail PRIMARY KEY CLUSTERED (audited_item_id, audit_datetime),
CONSTRAINT FK_Audit_Trail_Audited_Items FOREIGN KEY (audited_item_id) REFERENCES Audited_Items (id)
)