データモデリング
データモデリングとは?
データモデリングという用語は、アプリケーション内のオブジェクトの形状と構造を定義するプロセスを指します。これらのオブジェクトはしばしば「アプリケーションモデル」と呼ばれます。リレーショナルデータベース(PostgreSQLなど)では、これらはテーブルに格納されます。ドキュメントデータベース(MongoDBなど)を使用する場合、これらはコレクションに格納されます。
アプリケーションのドメインに応じて、モデルは異なります。たとえば、ブログアプリケーションを作成している場合、ブログ、著者、記事などのモデルがあるかもしれません。カーシェアリングアプリを作成している場合、ドライバー、車、ルートのようなモデルがあるでしょう。アプリケーションモデルを使用すると、それぞれのデータ構造を作成することにより、これらの異なるエンティティをコード内で表現できます。
データモデリングを行う際、通常は次のような質問をします。
- アプリケーションの主なエンティティ/概念は何ですか?
- それらは互いにどのように関連していますか?
- それらの主な特性/プロパティは何ですか?
- それらは私の技術スタックでどのように表現できますか?
Prisma ORMなしのデータモデリング
データモデリングは通常、(少なくとも)2つのレベルで行う必要があります。
- データベースレベルで
- アプリケーションレベルで(つまり、プログラミング言語で)
アプリケーションモデルが両方のレベルで表現される方法が異なる理由はいくつかあります。
- データベースとプログラミング言語は異なるデータ型を使用します
- リレーションは、データベースとプログラミング言語で異なる方法で表現されます
- データベースは通常、インデックス、カスケード削除、またはさまざまな追加制約(例:ユニーク、非NULLなど)のような、より強力なデータモデリング機能を備えています
- データベースとプログラミング言語には異なる技術的制約があります
データベースレベルでのデータモデリング
リレーショナルデータベース
リレーショナルデータベースでは、モデルはテーブルによって表現されます。たとえば、アプリケーションのユーザーに関する情報を格納するために、users
テーブルを定義するかもしれません。PostgreSQLを使用すると、次のように定義します。
CREATE TABLE users (
user_id SERIAL PRIMARY KEY NOT NULL,
name VARCHAR(255),
email VARCHAR(255) UNIQUE NOT NULL,
isAdmin BOOLEAN NOT NULL DEFAULT false
);
ランダムデータを含むusers
テーブルの視覚的な表現は、次のようになります。
user_id | name | email | isAdmin |
---|---|---|---|
1 | Alice | alice@prisma.io | false |
2 | Bob | bob@prisma.io | false |
3 | Sarah | sarah@prisma.io | true |
これには次の列があります。
user_id
:users
テーブルの新しいレコードごとにインクリメントされる整数。また、各レコードの主キーを表します。name
: 最大255文字の文字列。email
: 最大255文字の文字列。さらに、追加された制約は、email
列に重複した値を持つレコードがあってはならず、すべてのレコードがその値を持つ必要があることを示します。isAdmin
: ユーザーが管理者権限を持っているかどうかを示すブール値(デフォルト値:false
)
MongoDB
MongoDBデータベースでは、モデルはコレクションによって表現され、任意の構造を持つドキュメントを含めることができます。
{
_id: '607ee94800bbe41f001fd568',
slug: 'prisma-loves-mongodb',
title: 'Prisma <3 MongoDB',
body: "This is my first post. Isn't MongoDB + Prisma awesome?!"
}
Prisma Clientは現在、一貫性のあるモデルと正規化されたモデル設計を期待しています。これは、次のことを意味します。
- モデルまたはフィールドがPrismaスキーマに存在しない場合、それは無視されます
- フィールドが必須であるにもかかわらずMongoDBデータセットに存在しない場合、エラーが発生します
アプリケーションレベルでのデータモデリング
アプリケーションドメインのエンティティを表すテーブルを作成することに加えて、プログラミング言語でアプリケーションモデルを作成する必要もあります。オブジェクト指向言語では、これはしばしばモデルを表すクラスを作成することによって行われます。プログラミング言語によっては、インターフェースまたは構造体で行われることもあります。
データベース内のテーブルとコードで定義するモデルの間には、強い相関関係があることがよくあります。たとえば、前述のusers
テーブルからのレコードをアプリケーションで表現するために、次のようなJavaScript(ES6)クラスを定義するかもしれません。
class User {
constructor(user_id, name, email, isAdmin) {
this.user_id = user_id
this.name = name
this.email = email
this.isAdmin = isAdmin
}
}
TypeScriptを使用する場合、代わりにインターフェースを定義するかもしれません。
interface User {
user_id: number
name: string
email: string
isAdmin: boolean
}
どちらの場合も、User
モデルは前の例のusers
テーブルと同じプロパティを持っていることに注意してください。データベーステーブルとアプリケーションモデルの間には1対1のマッピングがあることが多いですが、モデルがデータベースとアプリケーションで完全に異なる方法で表現されることもあります。
この設定で、users
テーブルからレコードを取得し、User
型のインスタンスとして格納できます。次のコードスニペットは、PostgreSQLのドライバーとしてpg
を使用し、上記の定義されたJavaScriptクラスに基づいてUser
インスタンスを作成します。
const resultRows = await client.query('SELECT * FROM users WHERE user_id = 1')
const userData = resultRows[0]
const user = new User(
userData.user_id,
userData.name,
userData.email,
userData.isAdmin
)
// user = {
// user_id: 1,
// name: "Alice",
// email: "alice@prisma.io",
// isAdmin: false
// }
これらの例では、アプリケーションモデルは「ダム」であることに注意してください。つまり、ロジックを実装していませんが、その唯一の目的は、データをPlain Old JavaScript Objectsとして運ぶことです。
ORMを使用したデータモデリング
ORMは、開発者がデータベースを扱いやすくするために、オブジェクト指向言語で一般的に使用されています。ORMの重要な特徴は、アプリケーションデータを、基盤となるデータベースのテーブルにマッピングされるクラスの観点からモデル化できることです。
上記の approach との主な違いは、これらのクラスがデータだけでなく、実質的な量のロジックも実装していることです。主にストレージ、取得、シリアライズ、およびデシリアライズのためですが、アプリケーションに固有のビジネスロジックを実装することもあります。
これは、データベースでデータを読み書きするためにSQLステートメントを書くのではなく、モデルクラスのインスタンスがデータを保存および取得するためのAPIを提供することを意味します。
SequelizeはNode.jsエコシステムで人気のあるORMです。これは、Sequelizeのモデリング approach を使用して、前のセクションと同じUser
モデルを定義する方法です。
class User extends Model {}
User.init(
{
user_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING(255),
email: {
type: Sequelize.STRING(255),
unique: true,
},
isAdmin: Sequelize.BOOLEAN,
},
{ sequelize, modelName: 'user' }
)
このUser
クラスを動作させる例を取得するには、対応するテーブルをデータベースに作成する必要があります。Sequelizeでは、これを行うための2つの方法があります。
User.sync()
を実行する(通常、本番環境では推奨されません)- Sequelize migrationsを使用してデータベーススキーマを変更する
前のセクションで示したように、User
クラスを手動でインスタンス化する(new User(...)
を使用)ことは決してなく、代わりにUser
クラスでstaticメソッドを呼び出すと、User
モデルインスタンスが返されることに注意してください。
const user = await User.findByPk(42)
findByPk
の呼び出しは、ID値42
で識別されるUser
レコードを取得するためのSQLステートメントを作成します。
結果として得られるuser
オブジェクトは、SequelizeのModel
クラスのインスタンスです(User
がModel
を継承するため)。これはPOJOではなく、Sequelizeからの追加の動作を実装するオブジェクトです。
Prisma ORMを使用したデータモデリング
アプリケーションで使用したいPrisma ORMの parts に応じて、データモデリングフローは少し異なります。次の2つのセクションでは、Prisma Clientのみを使用する場合と、Prisma ClientとPrisma Migrateを使用する場合のワークフローについて説明します。
ただし、どのアプローチでも、Prisma ORMを使用すると、クラス、インターフェース、または構造体を手動で定義して、プログラミング言語でアプリケーションモデルを作成することは決してありません。代わりに、アプリケーションモデルはPrismaスキーマで定義されます。
- Prisma Clientのみ:Prismaスキーマのアプリケーションモデルは、データベーススキーマのイントロスペクションに基づいて生成されます。データモデリングは主にデータベースレベルで行われます。
- Prisma ClientとPrisma Migrate:データモデリングは、Prismaスキーマでアプリケーションモデルを手動で追加することによって行われます。Prisma Migrateは、これらのアプリケーションモデルを基盤となるデータベースのテーブルにマッピングします(現在、リレーショナルデータベースでのみサポートされています)。
例として、前の例のUser
モデルは、Prismaスキーマでは次のように表現されます。
model User {
user_id Int @id @default(autoincrement())
name String?
email String @unique
isAdmin Boolean @default(false)
}
アプリケーションモデルがPrismaスキーマにある(イントロスペクションを通じて追加されたか、手動で追加されたかに関わらず)と、通常、次のステップはPrisma Clientを生成することです。Prisma Clientは、アプリケーションモデルの形状でデータを読み書きするためのプログラム的で型安全なAPIを提供します。
Prisma Clientは、TypeScript 型エイリアスを使用して、コード内のアプリケーションモデルを表します。たとえば、User
モデルは、生成されたPrisma Clientライブラリでは次のように表現されます。
export type User = {
id: number
name: string | null
email: string
isAdmin: boolean
}
生成された型に加えて、Prisma Clientは、@prisma/client
パッケージをインストールすると使用できるデータアクセスAPIも提供します。
import { PrismaClient } from '@prisma/client'
// or
// const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
// use inside an `async` function to `await` the result
await prisma.user.findUnique(...)
await prisma.user.findMany(...)
await prisma.user.create(...)
await prisma.user.update(...)
await prisma.user.delete(...)
await prisma.user.upsert(...)
Prisma Clientのみを使用する
Prisma Clientのみを使用し、アプリケーションでPrisma Migrateを使用しない場合、データモデリングはSQLを介してデータベースレベルで行う必要があります。SQLスキーマの準備ができたら、Prismaのイントロスペクション機能を使用して、アプリケーションモデルをPrismaスキーマに追加します。最後に、Prisma Clientを生成すると、データベース内のデータを読み書きするための型とプログラムAPIが作成されます。
主なワークフローの概要は次のとおりです。
- SQLを使用してデータベーススキーマを変更します(例:
CREATE TABLE
、ALTER TABLE
など) prisma db pull
を実行してデータベースをイントロスペクトし、アプリケーションモデルをPrismaスキーマに追加しますprisma generate
を実行してPrisma Client APIを更新します
Prisma ClientとPrisma Migrateを使用する
Prisma Migrateを使用する場合、Prismaスキーマでアプリケーションモデルを定義し、リレーショナルデータベースではprisma migrate
サブコマンドを使用して、プレーンなSQLマイグレーションファイルを生成します。これらは、適用する前に編集できます。MongoDBでは、代わりにprisma db push
を使用して、変更をデータベースに直接適用します。
主なワークフローの概要は次のとおりです。
- Prismaスキーマでアプリケーションモデルを手動で変更します(例:新しいモデルを追加する、既存のモデルを削除するなど)
prisma migrate dev
を実行してマイグレーションを作成および適用するか、prisma db push
を実行して変更を直接適用します(どちらの場合もPrisma Clientは自動的に生成されます)