是否有非CRUD方法的示例?


14

我是一名程序员,但也曾担任过档案管理员。作为档案管理员,保存数据非常重要。

在数据操作方面,我经常与同事争论。我不太喜欢CRUD中的U和D。宁愿更新一条记录,我也喜欢添加一条新记录并引用旧记录。这样,您就可以建立变更历史。我也不喜欢删除记录,而是将它们标记为无效。

有这个用语吗?基本上只创建和读取数据?有这种方法的例子吗?


1
Is there a term for this? Basically only creating and reading data?当然有:CR; P
yannis 2012年

7
从用户的角度来看,这仍然是CRUD。我不知道该实现方式的任何特定标签,但我认为它在许多应用程序中很常见。(Stack Exchange是一个很好的例子……)
Mark E. Haase 2012年

您可能需要观看一个名为“阻抗不匹配是我们的错”的演讲。
安东·巴尔科夫斯基

+1在某些时候,有人会想要报告,并且很难针对不存在的数据创建报告,因为这些数据已经“更新”了。我发现您的方法非常具有前瞻性。
Chuck Conway 2012年

2
您可能还需要观看谈Datomic:infoq.com/presentations/The-Design-of-Datomic
马里安Venema

Answers:


16

将记录标记为已删除称为软删除。我从来没有听到过要更新的备用短语,但是我想这会导致您软删除旧记录并创建新记录。

应当指出,这是一个有争议的技术。查看链接:Con vs Pro


11

保留更改历史记录的问题之一是,它会使数据库杂乱无章,并且可能极大地增加数据库的大小(取决于使用模式)。因此,一个好主意是将审计跟踪记录存储在单独的位置,并使实际的应用程序表仅填充相关数据。因此,每次应用程序执行CRUD操作时,所做的更改都会记录在审核表中,并且对应用程序表执行CRUD操作(不进行软删除)。

通过使审计跟踪保持独立,可以为您的应用程序提供一个原始数据存储库,以便您与之交互,同时在需要时仍保留更改历史记录。现在,您还可以根据业务需求单独存档审计跟踪,甚至销毁它。


3
这个。同样,参照完整性也成为噩梦。您不能将外键指定为“此表中具有此未唯一键且未标记为已删除的一条记录”。通过使用新数据更新此“工作副本”之前,通过将要更新的记录副本的数据持久化到另一个表中,可以解决此问题,但是您仍然需要处理大量数据。
KeithS 2012年

5

EventSourcing听起来像您可能正在寻找的模式。

让我们以使用一个简单的“ car”对象为例,我们将要跟踪其颜色(后面是伪C#代码)。

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

通过CRUD实现,当我们更新汽车的颜色时,先前的颜色将丢失。

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

对我来说,这种信息丢失听起来像是您最想避免的事情(因此,不喜欢CRUD模式的更新和删除部分)。

如果我们要重写car类以在更新其更改时响应事件,则它可能如下所示:

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;
    }
}

现在我们将如何更新该对象的颜色?我们可以创建一个CarColorChanged事件!

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

注意到实际模型对象上没有保存吗?这是因为,我们不是直接保留模型,而是保留将模型置于当前状态的事件。这些事件应该是一成不变的

现在,让我们快进并更改颜色几次:

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

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

如果我们要查看事件存储(可以是关系数据库,基于文件等),我们将看到与汽车对象有关的一系列事件:

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

如果我们想重建该汽车对象,我们可以简单地通过创建一个新的汽车对象并将事件从我们的事件存储区应用到该对象来实现。

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

通过事件流,我们可以简单地通过创建一个新的汽车对象并仅应用我们想要的事件来将汽车的状态回滚到先前的时间段:

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

5

事件采购是必经之路,您应该看看Greg Young关于此事的看法。

http://goodenoughsoftware.net/

还可以在他的数据库(事件存储)上查看此演示文稿。您也可以找到其他视频。

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

除非您特别需要能够搜索已删除的项目,否则我不会寻求“软删除”的答案,但是那样的话您不应该将它们视为已删除而是已存档。我认为术语在这里非常重要。

我也不想维护“版本表”。我见过的所有“版本表” (包括目前正在尝试清除的版本表-由于错误而损坏了7年的数据……即使有历史数据也无法取回。)。。。。因为损坏的原因同样如此)最终会由于代码中的错误而损坏,最终您仍然会丢失数据,因为您永远无法返回并重新创建损坏了的数据。

对于事件源模型,情况并非如此。您始终可以准确地重放用户所做的事情。这是CRUD和事件来源之间非常重要的区别。事件源体系结构将事件保存在事件存储中,而不是数据对象或域模型对象中。一个事件很容易影响多个对象。试想一下购物车解决方案,您可以将购物车中的每个项目转换为实际订单。一个事件影响所有项目对象以及购物车对象,这些对象被转换为订单对象。

如果您在数据库的每个表中都保留了每一行的版本副本,那么想象一下不得不倒退到特定的时间戳的恐怖,更不用说维护该版本表的大量空间和性能开销了。

使用事件源,您只需重播事件直到某个时间点即可轻松倒带。可以使用快照来实现快进,但这只是实现的问题。

但是,我想您会喜欢的真正好处是,因为您对不丢失数据特别感兴趣,因此,如果您发现保存该数据的代码中的错误,则无需回去清理数据(这通常是不可能的,因为数据几乎永远不会完成)。相反,只需修复该错误,然后重播所有事件。然后,您将拥有一个包含正确数据的数据库。

如果进行调试,您有多少次要求用户告诉您他们做了什么...为什么不重播他们所做的然后逐步浏览代码!很漂亮吧。

希望这可以帮助。


2

不完全是您的示例,但是在较旧的财务系统中,您拥有WORM存储。如果您需要“更新”,则可以编写新记录,并且始终将最后一条记录称为当前记录,但是任何提交的数据都不会被覆盖。

很多人把这个想法带到了后来的系统中。我听说过您所描述的被称为WORM表的信息,但仅在那些圈子中。


2

是的,它在企业系统中非常普遍,基本上有两种方法:

  • “双向-时间”,其中每个记录都具有一个有效的时间戳记和一个有效的时间戳记(“当前”记录的有效日期为“永远”-空,“ 9999-12-31”或某些此类高值)。永远不会删除记录,而将“有效期”日期设置为当前时间,并且在更新的情况下,将插入一条新记录,其中包含当前时间的有效起始日期和永远有效的日期。
  • “历史记录表”-每次更改记录时,都会将旧记录的副本转储到具有事件时间戳记的历史记录/日志表中。

两种方法的粒度差异很大。例如,如果更改了一个订单项目上的小部件数量,您是否维护整个订单或仅一个项目的历史记录?

一般来说,“双向”是很多额外的工作,只有在您有很多用例(例如“审计师于12月31日获得订单状态”)时才值得。



-2

没有这4件事基本上不可能完成。Crud意味着创建,读取,更新和删除,因此当您仅尝试读取数据时,可以使用简单的查询来进行查询,但是所有这三件事都附加到数据库的一个和另一个简单概念上

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.