Arquitetura de custom fields

Bom dia pessoal.

Estou começando um novo projeto que tem um requisito desafiador: custom fields (ou campos definidos pelo usuário).
Basicamente, o usuário poderá (através de API) registrar eventos, com propriedades definidas por ele e depois gerar relatórios e gráficos em um interface web. Pois bem, vem a questão: como armazenar essas propriedades, visto que, é impossível saber de antemão qual são as propriedades e os tipos das mesmas. Algumas propriedades são fixas para controle interno da aplicação.
A idéia é ter muitos usuários e consequentemente muitas “tabelas” de eventos totalmente distintas.

Encontrei diversas formas:

  1. Usar um banco NoSQL de documento (MongoDB por exemplo): simplesmente inserir os documentos (as propriedades fixas + as definidas pelos usuários) em uma única coleção de documentos.
  2. Usar um banco NoSQL de documento (MongoDB por exemplo): inserir os dados gerais em uma coleção e ter uma coleção para cada “tabela” de eventos definida pelo usuário. Dessa forma, faria sentido usar uma solução dessas pela facilidade de adicionar novas propriedades.
  3. BD relacional, uma tabela no banco para cada “tabela” definida pelo usuário. Similar à opção 2.
  4. BD relacional, com uma tabela com dados gerais e com uma tabela chave / valor (/tipo?) para as propriedades. Seriam então (basicamente) duas tabelas compartilhadas entre todos os usuários.
  5. NoSQL ou BD relacional: um banco por usuário.
  6. BD relacional: uma tabela com colunas extras (string1, string2, string3, stringN, inteiro1, inteiro2, inteiroN, etc)

Alguns requisitos: os usuários irão realizar consultas, 99% do tempo, de agregação, ou seja, somando, definindo máximos, desvio padrão, etc, podendo ser filtradas todas por todas as propriedades definidas pelo usuário. Outra questão é o volume de dados, portanto a performance é importante. Meu ideal é agregar em torno de 3Mi de registros em menos de 1 segundo. Sei que para isto é necessário mais que deixar tudo na mão do banco, mas o mesmo é um componente crucial.

Gostaria da opinião de vocês que já utilizaram uma ou mais dessas alternativas que listei, e caso tenham utilizado outra solução, será bem vinda.

Muito obrigado.

Algumas considerações baseado no que disse:

Opção 1: Você provavelmente precisaria manter seus meta-dados em uma coleção a parte.
Como pesquisar por um custom field, se você não sabe se ele existe?
Bancos NoSql não vão facilitar nessa parte.

Opção 2: Mesmo da 1

Opção 3: Criar esquema de banco de dados em runtime é sempre complicado e muito sujeito a erros.
Sem falar que manipular o esquema para tabelas de grande volumes pode tomar certo tempo.
Terá que ter uma UI mais responsiva pra monitorar progresso e tal (adicionar uma coluna numa tabela com milhões de registros pode levar tempo).

Opção 4: Esse padrão é conhecido com EAV. Para alguns é considerado um anti-pattern na verdade.
Mas o caso de uso dele é justamente essa situação.
Ele tira muitas das vantagens que um banco relacional te dá e a performance pode sofrer com isso.
(Imagine que para ler 2 objetos de uma custom entidade com 5 propriedades, terá que ler 10 linhas e fazer uma “transposição” nelas.
Tem que ter mais cuidado com o tipo dos dados também. Enfim, tem várias desvantagens que você pode consultar por aí.

Opção 5: Eu não faria isso se espera tantos usuários. Quanto mais dinâmico e automático for, para adicionar usuários, melhor.

Opção 6: Essa acredito ser a pior de todas opções. Você coleta todas as desvantagens das outras sem ganhar nada em troca.

Outros

  • Há um tempo atrás foi divulgado aqui um novo framework para esse tipo de situação (acho). Foi um tópico do Guerra (acho), editor de uma dessas revistas de Java (esqueci o nome dela).

  • Para questão de performance você pode sempre utilizar outras técnicas (como cache) ao invés de contar tudo no db.