我想定义一个跨团队的模式;平台有效。这很简单,可以看作是一种本体论。我需要的是能够定义字段所代表的内容,以及每个平台上字段的名称。我希望模式能够为每种使用的语言生成数据对象,因此我想知道我的需求是否可以在Protobuf或GraphQL中得到满足。注意,我的约定可能不同于我生成的目标语言中的琐碎约定,因为它需要与数据库兼容。我需要一个简单的例子:
{
"lastName": {
"mssqlName":"LastName",
"oracleName":"FamilyName",
"elasticName":"lastName",
"cassandraName":"last_name",
"rocksDbName":"surname",
},
"age" : {
...
}
正如你所看到的,在一些平台上,我的名字与其他平台完全不同。我想知道解决这个问题的常用方法和技术是什么,以及像Proto&GraphQL。
一个单一的模式作为跨数据库、通信链接、多种语言和平台的所有对象/消息定义的单一真理点?那就太好了,不是吗?
我能想到的最接近XSD(XML模式),但我认为它在工具方面不起作用。例如,我知道一些工具会采用XSD模式,并为您生成代码,这些代码会将对象序列化到XML或从XML中取消序列化(例如,Microsoft的XSD.exe)。
还有一些工具可以从XSD模式创建SQL表。但是,构建可以访问这些表的类的代码生成器并不是为了将对象序列化/取消序列化为XML线格式而构建它们。
基本上,我还没有遇到过一种模式语言,它有可以做所有事情的工具。ASN.1工具非常擅长创建串行化类,但我从未找到一个也针对SQL交互的工具。与XSD相同。
我的知识当然不是详尽无遗的,JSON领域中可能有一些东西是有效的。
最小疼痛折衷方法
我过去所做的决定是接受我必须围绕模式的更改进行一些手动编码,但可能不会太多。我会在Google协议缓冲区中完全定义消息,并将其用于应用程序/语言之间的对象交换。如果我想将对象存储在数据库中,我会接受这样的事实:为此,我必须在表列中对对象进行并行定义,但仅限于我想搜索的关键字段。最后一列将是一个任意容器,能够完整存储序列化的对象。
例如,如果GPB消息有一个整数ID字段、一个字符串名称字段以及一堆其他字段。然后,我的数据库表将有一个ID列、一个Name列和用于存储Bytes的列。
通过这种方式,我可以串行化一个对象,并将其推入一行的Bytes列,同时填充ID和Name列。由于有Name/ID列,我可以快速搜索对象。如果我想访问存储在数据库中的对象中的其他字段,我必须从数据库中检索记录并取消字节列的序列化。
这种方式本质上是在打赌,在模式的开发过程中,这些关键列/字段名(ID,Name)永远不会更改。但这很可能是一个安全的选择。一般来说,在项目的早期,可以很容易地解决这样的问题,在开发过程中可能会改变模式的其余部分。
一个小的回报是,如果在数据库中查找对象的原因是能够通过通信信道发送它,那么它已经在数据库中串行化了。在发送到通信链路之前,无需再次串行化。
因此,这种方法可能会留下一些代码/事实点的重复,但在运行时的部分时间里,在避免串行化步骤方面可以表现得很好。
你也可以作弊。如果串行有线格式是基于文本的(JSON、XML、一些ASN.1格式等),那么在字节列上进行字符串搜索很有可能会产生良好的结果。例如,假设一个消息字段是MiddleName,但我并没有在数据库中将其创建为一个不同的表列。通过在Bytes列中搜索值,我可以找到任何给定MiddleName的可能记录,因为它以文本形式存储在其中的某个位置。
基于反射的方法
另一种潜在的方法是接受工具的存在并不能满足所有需求,并使用语言特性(反射)进行调整,以利用代码生成器的一个共同特性。
例如,考虑GPB的原型编译器。在生成的代码中,您最终得到的类的成员以消息中的字段命名。它与为访问具有相同名称列的数据库表而生成的任何代码都或多或少相同。
因此,可以使用反射在生成的类之间进行自动转录。您向下迭代一个类中的成员树,然后可以将其匹配到另一个生成类中的一个成员。
这避免了对以下代码的需要:
Protobuf::MyClass myObj_g; // An object built using GPB
JSON::MyClass myObj_j; // equivalent object to be copied from myObj_g;
myObj_j.Field1 = myObj_g.Field1;
myObj_j.Field2 = myObj_g.Field2;
.
.
.
相反:
Protobuf::MyClass myObj_g; // An object built using GPB
JSON::MyClass myObj_j; // equivalent object to be copied from myObj_g;
foreach (Protobuf::MyClass::Reflection::Field field in Protobuf::MyClass.Fields)
{
myObj_j.Reflection.FindByName(field.Name) = myObj_g.Reflection.FindByName(field.Name);
}
为了在每种数据库和串行化技术之间,每种语言都能做到这一点,你会有很多麻烦,但关键是你只需要写一次。任何后续的模式更改都不需要代码更改,至少不需要在串行化技术和数据库访问技术之间交换对象。
显然,反思在某些语言中更容易/可能,而在其他语言中则不然。
运行时修复方法
Apache Avro具有串行数据描述其自身形状的特点。基本上,有线格式数据有自己的模式,因此消费者可以自动构建数据的表示。在某些语言中,这很可怕(C、C++),但库是存在的。
基本上,它迫使您编写应用程序,以便它们自己处理数据;