私はDatomicを調べてきましたが、とても面白そうです。しかし、Datomic が技術的にどのように機能するかについては非常に優れた情報があるように見えますが、データ モデリングについてどのように考えるべきかについてはあまり見たことがありません。
Datomic でのデータ モデリングのベスト プラクティスは何ですか? この件に関する良いリソースはありますか?
私はDatomicを調べてきましたが、とても面白そうです。しかし、Datomic が技術的にどのように機能するかについては非常に優れた情報があるように見えますが、データ モデリングについてどのように考えるべきかについてはあまり見たことがありません。
Datomic でのデータ モデリングのベスト プラクティスは何ですか? この件に関する良いリソースはありますか?
Datomicは新しく、私の経験は限られているため、この回答をベストプラクティスと見なすべきではありません。代わりに、これを、リレーショナルのバックグラウンドを持ち、より生産的なデータストアを求めている人のためのDatomicのイントロとして取り上げてください。
Datomicでは、ドメインデータを属性の値を持つエンティティとしてモデル化します。別のエンティティへの参照は属性の値である可能性があるため、エンティティ間の関係を簡単にモデル化できます。
一見すると、これは従来のリレーショナルデータベースでデータをモデル化する方法とそれほど違いはありません。SQLでは、テーブルの行はエンティティであり、テーブルの列は値を持つ属性に名前を付けます。リレーションシップは、別のテーブル行の主キー値を参照する1つのテーブル行の外部キー値によって表されます。
ドメインをモデル化するときに従来のERダイアグラムをスケッチできるので、この類似性は素晴らしいです。SQLデータベースの場合と同じように関係に依存できますが、外部キーが処理されるため、外部キーをいじくり回す必要はありません。Datomicでの書き込みはトランザクションであり、読み取りは一貫しています。そのため、全体像を提供するために結合に依存して、データを適切と思われる粒度でエンティティに分割できます。これは、多くのNoSQLストアで失われる便利さです。ここでは、更新中に有用なレベルのアトミック性を実現するために、非正規化された大きなエンティティが一般的です。
この時点で、良いスタートを切ることができます。ただし、DatomicはSQLデータベースよりもはるかに柔軟性があります。
時間は本質的にすべてのDatomicデータの一部であるため、データモデルの一部としてデータの履歴を具体的に含める必要はありません。これはおそらくDatomicの最も話題になっている側面です。
Datomicでは、スキーマはSQLで必要な「長方形」で厳密に定義されていません。つまり、エンティティ1は、モデルを満たすために必要な属性を持つことができます。エンティティNULL
には、適用されない属性のデフォルト値が必要ありません。また、必要に応じて、特定の個々のエンティティに属性を追加できます。
したがって、ドメインの変更(またはドメインの理解の変更)に対応するために、時間の経過とともに個々のエンティティの形状を変更できます。だから何?これは、MongoDBやCouchDBのようなドキュメントストアと同じです。
違いは、Datomicを使用すると、影響を受けるすべてのエンティティに対してスキーマの変更をアトミックに実行できることです。つまり、トランザクションを発行して、任意のドメインロジックに基づいて、言語[2]で記述されたすべてのエンティティの形状を更新できます。このロジックは、コミットされるまでリーダーに影響を与えることなく実行されます。リレーショナルストアスペースでもドキュメントストアスペースでも、この種の力に近いものは何も知りません。
あなたの実体も「単一のテーブルに住んでいる」と厳密に定義されていません。Datomicでエンティティの「タイプ」を定義するものを決定します。明示的に選択して、モデル内のすべてのエンティティに、:table
それがどの「タイプ」であるかを暗示する属性を持たせるように指示することができます。または、エンティティは、各タイプの属性要件を満たすだけで、任意の数の「タイプ」に準拠できます。
たとえば、モデルは次のことを義務付けることができます。
:name
、、:ssn
:dob
:name
と:title
します:salary
:name
、:address
:id
と:plan
します:expiration
これは私のようなエンティティを意味します:
{:name "Brian" :ssn 123-45-6789 :dob 1976-09-15
:address "400 South State St, Chicago, IL 60605"
:id 42 :plan "Basic" :expiration 2012-05-01}
、、およびであると推測できますが、でPerson
はResident
ありMember
ませんEmployee
。
DatomicクエリはDatalogで表現され、Datomicに保存されていないデータとリソースを参照して、独自の言語で表現されたルールを組み込むことができます。データベース関数は、Datomic内にファーストクラスの値として格納できます。これらはSQLのストアドプロシージャに似ていますが、トランザクション内で値として操作でき、言語で記述することもできます。これらの機能はどちらも、よりドメイン中心の方法でクエリと更新を表現できます。
最後に、OOとリレーショナルワールドの間のインピーダンスの不一致は常に私を苛立たせてきました。機能的なデータ中心の言語(Clojure)を使用するとそれが役立ちますが、Datomicは、コードからストレージにブリッジするために精神的な体操を必要としない耐久性のあるデータストアを提供するように見えます。
例として、Datomicからフェッチされたエンティティは、Clojure(またはJava)マップのように見えて動作します。オブジェクトインスタンスや一般的なデータ構造に変換せずに、アプリケーションの上位レベルに渡すことができます。そのエンティティの関係をトラバースすると、関連するエンティティがDatomicから遅延してフェッチされます。ただし、同時更新が発生した場合でも、元のクエリとの整合性が保証されます。そして、それらのエンティティは、最初のエンティティ内にネストされたプレーンな古いマップのように見えます。
これにより、データモデリングがより自然になり、私の意見でははるかに少ない争いになります。
競合する属性
上記の例は、モデルの潜在的な落とし穴を示しています。:id
後でそれが属性でもあると判断した場合はどうなりますEmployee
か?解決策は、属性を名前空間に編成することです。したがって、との両方が:member/id
あり:employee/id
ます。事前にこれを行うと、後で競合を回避するのに役立ちます。
属性の定義は(まだ)変更できません
Datomicで属性を特定のタイプ、インデックス付きかどうか、一意などとして定義すると、後でそれを変更することはできません。ALTER TABLE ALTER COLUMN
ここではSQL用語で話しています。今のところ、正しい定義で置換属性を作成し、既存のデータを移動することができます。
これはひどいように聞こえるかもしれませんが、そうではありません。トランザクションはシリアル化されているため、新しい属性を作成し、それにデータをコピーし、競合を解決し、古い属性を削除するトランザクションを送信できます。他のトランザクションからの干渉なしに実行され、母国語のドメイン固有ロジックを利用してそれを実行できます。これは基本的に、を発行するときにRDBMSが舞台裏で行っていることALTER TABLE
ですが、ルールに名前を付けます。
「キャンディストアの子供」にならないでください
柔軟なスキーマは、データモデルがないことを意味するものではありません。他のデータストアの場合と同じように、物事を適切な方法でモデル化するための事前の計画を立てることをお勧めします。できるという理由だけでなく、必要なときにDatomicの柔軟性を活用してください。
大きく、絶えず変化するデータを保存することは避けてください
Datomicは、BLOBや絶えず変化する非常に大きなデータに適したデータストアではありません。以前の値の履歴レコードを保持し、古いバージョンを(まだ)パージする方法がないためです。この種のものは、ほとんどの場合、S3のようなオブジェクトストアに適しています。更新:属性ごとに履歴を無効にする方法があります。更新:データを切り出す方法もあります; ただし、オブジェクト自体ではなく外部オブジェクトへの参照を格納することは、BLOBを処理するための最良のアプローチである可能性があります。この戦略をバイト配列の使用と比較してください。
bkirkbri からの非常に素晴らしい回答です。いくつか追加したい:
類似しているが等しくない「タイプ」またはスキーマのエンティティを多数格納する場合は、スキーマで type キーワードを使用します。
[:db/add #db/id[:db.part/user] :db/ident :article.type/animal]
[:db/add #db/id[:db.part/user] :db/ident :article.type/weapon]
[:db/add #db/id[:db.part/user] :db/ident :article.type/candy]
{:db/id #db/id[:db.part/db]
:db/ident :article/type
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "The type of article"
:db.install/_attribute :db.part/db}
それらを読むときは、クエリからエンティティ ID を取得し、必要に応じてタイプでディスパッチするマルチメソッドによってそれらを使用および解析しますdatomic.api/entity
。eid