Existem exemplos de abordagens não CRUD?

14

Sou programador, mas também trabalhei como arquivista. Como arquivista, trata-se muito de manter dados.

Costumo discutir com colegas quando se trata de operações sobre dados. Eu não gosto muito de U e D em CRUD. Em vez de atualizar um registro, prefiro adicionar um novo e ter uma referência ao registro antigo. Dessa forma, você constrói um histórico de mudanças. Também não gosto de excluir registros, mas marcá-los como inativos.

Existe um termo para isso? Basicamente, apenas criando e lendo dados? Existem exemplos dessa abordagem?

Pieter B
fonte
1
Is there a term for this? Basically only creating and reading data?Claro que existe: CR; P
yannis
7
Do ponto de vista do usuário, isso ainda é CRUD. Não conheço rótulos específicos para esse estilo de implementação, mas acho que isso é comum em MUITOS aplicativos. (Stack Exchange é um exemplo bom ...)
Mark E. Haase
Você pode assistir a uma palestra chamada The Impedance Mismatch is Our Fault .
Anton Barkovsky
+1 Em algum momento, alguém vai querer relatórios e é muito difícil criar relatórios sobre dados que não existem porque foram "atualizados" fora da existência. Acho sua abordagem muito avançada.
amigos estão dizendo sobre chuck
2
Você também pode querer assistir uma palestra sobre Datomic: infoq.com/presentations/The-Design-of-Datomic
Marjan Venema

Respostas:

16

Marcar um registro como excluído é conhecido como exclusão eletrônica . Eu nunca ouvi uma frase alternativa para atualização, mas acho que é porque você exclui o registro antigo e cria um novo.

Deve-se notar, esta é uma técnica controversa. Veja os links: Con vs Pro .

pdr
fonte
11

Um dos problemas para manter um histórico de alterações é que ele atrapalha o banco de dados e pode aumentar drasticamente seu tamanho (dependendo dos padrões de uso). Portanto, uma boa idéia seria armazenar a trilha de auditoria em um local separado e manter as tabelas de aplicativos reais preenchidas apenas com dados relevantes. Portanto, toda vez que uma operação CRUD é executada pelo aplicativo, a alteração é registrada nas tabelas de auditoria e a operação CRUD é executada nas tabelas do aplicativo (sem exclusões suaves).

Ao manter a trilha de auditoria separada, você obtém um armazenamento de dados primitivo para o seu aplicativo interagir, mantendo o histórico de alterações, caso necessário. Agora você também pode arquivar a trilha de auditoria separadamente ou até destruí-la, dependendo dos requisitos de negócios.

Sistema caiu
fonte
3
Este. Além disso, a integridade referencial se torna um pesadelo; você não pode especificar uma chave estrangeira para "o único registro nesta tabela com essa chave não exclusiva que não esteja marcada como excluída". Você contorna isso persistindo os dados para a cópia de um registro que está prestes a ser atualizado em uma tabela diferente, antes de atualizar essa "cópia de trabalho" com os novos dados, mas você ainda tem um grande volume de dados para lidar.
Keiths
5

EventSourcing soa como o padrão que você pode estar procurando.

Vamos dar um exemplo usando um objeto "car" simples que gostaríamos de acompanhar a cor de (segue o pseudo código C #).

public class Car {
  public string Color { get; set; }
  public Car() { this.Color = "Blue"; }
}

Com uma implementação CRUD, quando atualizamos a cor do carro, a cor anterior seria perdida.

MyCar.Color = "Red";
MyCar.Save();  // Persist the update to the database and lose the previous data

Essa perda de informações me parece o que você mais gostaria de evitar (daí a aversão à atualização e a exclusão de parte do padrão CRUD).

Se reescrevéssemos a classe car, para responder a eventos ao atualizar sua alteração, pode ser assim:

public class Car {
    public string Color { get; private set; } // Cannot be set from outside the class

    public void ApplyEvent(CarColorChangedEvent e) {
      this.Color = e.Color;
    }
}

Agora, como atualizaríamos a cor desse objeto? Poderíamos criar um evento CarColorChanged !

var evnt = new CarColorChangedEvent("Red");
MyEventStore.save(evnt);
MyCar.ApplyEvent(evnt);

Observe a falta de um salvamento no objeto de modelo real? Isso ocorre porque, em vez de persistir o modelo diretamente, persistimos os eventos que colocam o modelo no estado atual. Esses eventos devem ser imutáveis .

Agora vamos avançar e mudar a cor mais algumas vezes:

var evnt = new CarColorChangedEvent("Green");
MyEventStore.save(evnt);
MyCar.ApplyEvent(evnt);

var evnt = new CarColorChangedEvent("Purple");
MyEventStore.save(evnt);
MyCar.ApplyEvent(evnt);

Se analisássemos nosso armazenamento de eventos (poderia ser um banco de dados de relações, com base em arquivos etc.), veríamos uma série de eventos relacionados ao nosso objeto carro:

CarColorChangedEvent => Red
CarColorChangedEvent => Green
CarColorChangedEvent => Purple

Se quiséssemos reconstruir esse objeto de carro, poderíamos fazê-lo simplesmente criando um novo objeto de carro e aplicando os eventos de nosso armazenamento de eventos ao referido objeto.

var MyCar = new Car();
var events = MyDatabase.SelectEventsForCar("CarIdentifierHere");
foreach(var e in events) {
  MyCar.ApplyEvent(e);
}
Console.WriteLine(MyCar.Color); // Purple

Com o fluxo de eventos, podemos reverter o estado do carro para um período anterior, simplesmente criando um novo objeto de carro e aplicando apenas os eventos que queremos:

var MyCar = new Car();
var event = MyDatabase.GetFirstEventForCar("CarIdentifierHere");
MyCar.ApplyEvent(e);
Console.WriteLine(MyCar.Color); // Red
Mike
fonte
5

O Event Sourcing é o caminho a seguir, e você deve dar uma olhada no que Greg Young tem a dizer sobre isso.

http://goodenoughsoftware.net/

Veja também esta apresentação em seu banco de dados (armazenamento de eventos). Você pode encontrar outros vídeos também.

http://oredev.org/2012/sessions/a-deep-look-into-the-event-store

Eu não aceitaria a resposta "exclusões eletrônicas", a menos que você precise pesquisar itens excluídos especificamente, mas não pense neles como excluídos, mas arquivados. Eu acho que a terminologia é muito importante aqui.

Também não gostaria de manter uma "tabela de versões". Todas as "tabelas de versões" que eu já vi (incluindo as que estou tentando limpar no momento - 7 anos de dados corrompidos por causa de bugs ... e nenhuma maneira de recuperá-los, mesmo que tenhamos dados históricos .. porque isso é tão corrupto) acaba corrompido devido a erros no código e, no final, você ainda perde dados porque nunca pode voltar atrás e recriar os dados que a corrupção estragou.

Com o modelo de fornecimento de eventos, esse não é o caso. Você sempre pode reproduzir exatamente o que o usuário fez. Essa é a diferença muito importante entre CRUD e Event Sourcing. A arquitetura Event Sourcing salva eventos em um armazenamento de eventos, e não objetos de dados ou objetos de modelo de domínio. Um evento pode facilmente impactar vários objetos. Pense em uma solução de carrinho de compras onde você converte cada item no carrinho de compras em um pedido real. Um evento afeta todos os objetos do item, bem como os objetos do carrinho de compras, que são transformados em um objeto de pedido.

Se você manteve uma cópia com versão de cada linha em todas as tabelas do banco de dados, imagine o horror de ter que retroceder para um carimbo de data / hora específico, sem mencionar a quantidade insana de espaço e sobrecarga de desempenho na manutenção dessa tabela de versões.

Com o Event Sourcing, você pode retroceder facilmente, apenas reproduzindo eventos até um determinado momento. Avanços rápidos podem ser implementados usando snapshots, mas isso é tudo uma questão de implementação.

Mas a vantagem real que eu acho que você gostará, considerando que está particularmente interessado em não perder dados, é que, se você descobrir um bug no código que salva esses dados, não precisará voltar e limpar os dados (o que geralmente é impossível porque os dados quase nunca estão completos). Em vez disso, basta consertar o erro e reproduzir todos os eventos. Então você terá um banco de dados com bons dados corretos.

No caso de depuração, com que frequência você pediu ao usuário para lhe dizer o que fez ... por que não apenas repetir o que fez e depois percorrer o código! Muito bacana, hein.

Espero que isto ajude.

Jay Pete
fonte
2

Não é exatamente o seu exemplo, mas em sistemas financeiros mais antigos você tinha armazenamento WORM . Se você precisava "atualizar", gravou um novo registro e sempre se referiu ao último registro como atual, mas nenhum dado confirmado pode ser substituído.

Muitas pessoas levaram essa idéia para sistemas posteriores. Eu ouvi o que você está descrevendo referido como tabelas WORM, mas apenas nesses círculos.

Conta
fonte
2

Sim, é bastante comum em sistemas empresariais, existem basicamente duas abordagens:

  • "bi - temporal", no qual todo registro tem um válido de e válido para registro de data e hora (o registro "atual" tem uma data válida de "para sempre" - nula, "9999-12-31" ou algum valor tão alto)). Os registros nunca são excluídos. Em vez disso, a data "válido até" é definida como a hora atual e, no caso de uma atualização, um novo registro é inserido com uma válida a partir da hora atual e uma válida para sempre até a data.
  • "tabela de histórico" - sempre que um registro é alterado, uma cópia do registro antigo é despejada em uma tabela de histórico / log com um registro de data e hora para o evento.

Existem grandes variações de granularidade para ambas as abordagens. Por exemplo, se a quantidade de widgets em um item do pedido for alterada, você mantém o histórico de todo o pedido ou apenas desse item?

De um modo geral, "bitemporal" é muito trabalho extra e só vale a pena se você tiver muitos casos de uso como "o auditor obtém o status do pedido em 31 de dezembro".

James Anderson
fonte
-2

basicamente crud não pode ser completado sem essas 4 coisas. Crud significa criar, ler, atualizar e excluir; portanto, quando você está apenas tentando ler dados, pode usar uma consulta simples para isso, mas todas essas três coisas estão anexadas a um e a outros conceitos simples de banco de dados.

Tayyab Vohra
fonte