メインコンテンツへスキップ

データモデリング

データモデリングとは?

データモデリングという用語は、**アプリケーション内のオブジェクトの形状と構造を定義するプロセス**を指します。これらのオブジェクトはしばしば「アプリケーションモデル」と呼ばれます。リレーショナルデータベース(PostgreSQLなど)では、テーブルに保存されます。ドキュメントデータベース(MongoDBなど)を使用する場合、それらはコレクションに保存されます。

アプリケーションのドメインによって、モデルは異なります。例えば、ブログアプリケーションを作成している場合、ブログ著者記事といったモデルがあるかもしれません。カーシェアリングアプリを作成している場合は、おそらくドライバールートのようなモデルを持つでしょう。アプリケーションモデルを使用すると、それぞれのデータ構造を作成することで、これらの異なるエンティティをコードで表現できます。

データをモデリングする際、通常以下のような質問をします。

  • アプリケーションの主要なエンティティ/概念は何ですか?
  • それらは互いにどのように関連していますか?
  • それらの主な特徴/プロパティは何ですか?
  • それらは自分の技術スタックでどのように表現できますか?

Prisma ORMなしでのデータモデリング

データモデリングは通常、(少なくとも)2つのレベルで行う必要があります。

  • **データベース**レベルで
  • **アプリケーション**レベルで(つまり、プログラミング言語で)

両方のレベルでアプリケーションモデルが表現される方法は、いくつかの理由により異なる場合があります。

  • データベースとプログラミング言語は異なるデータ型を使用する
  • リレーションは、データベースとプログラミング言語で異なる方法で表現される
  • データベースは通常、インデックス、カスケード削除、またはさまざまな追加の制約(例:ユニーク、NOT 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_idnameemailisAdmin
1Alicealice@prisma.iofalse
2Bobbob@prisma.iofalse
3Sarahsarah@prisma.iotrue

以下のカラムがあります。

  • 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
// }

これらの例では、アプリケーションモデルが「ダム(単純)」であることに注目してください。つまり、ロジックを実装しておらず、その唯一の目的はデータをプレーンなJavaScriptオブジェクトとして運ぶことです。

ORMによるデータモデリング

ORMは、開発者がデータベースを扱いやすくするために、オブジェクト指向言語で一般的に使用されます。ORMの主要な特徴は、基になるデータベースのテーブルにマッピングされるクラスの観点からアプリケーションデータをモデル化できることです。

上記の解説されたアプローチとの主な違いは、これらのクラスがデータを運ぶだけでなく、かなりの量のロジックを実装していることです。主にストレージ、取得、シリアライゼーション、デシリアライゼーションのためですが、時にはアプリケーション固有のビジネスロジックも実装します。

これは、データベースでのデータの読み書きにSQL文を記述するのではなく、モデルクラスのインスタンスがデータを保存および取得するためのAPIを提供するということです。

SequelizeはNode.jsエコシステムで人気のあるORMです。Sequelizeのモデリングアプローチを使用して、前のセクションと同じ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マイグレーションを使用する

前のセクションで示されたように、Userクラスを手動でインスタンス化する(new User(...)を使用する)ことはなく、代わりにUserクラスの静的メソッドを呼び出し、それがUserモデルインスタンスを返すことに注意してください。

const user = await User.findByPk(42)

findByPkの呼び出しは、ID値42で識別されるUserレコードを取得するためのSQLステートメントを作成します。

結果として得られるuserオブジェクトは、SequelizeのModelクラスのインスタンスです(UserModelを継承しているため)。POJO(プレーンなJavaScriptオブジェクト)ではなく、Sequelizeの追加動作を実装するオブジェクトです。

Prisma ORMによるデータモデリング

アプリケーションでPrisma ORMのどの部分を使用したいかによって、データモデリングのフローは若干異なります。次の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を生成することです。これにより、アプリケーションモデルの形でデータを読み書きするためのプログラム的で型安全な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が作成されます。

主なワークフローの概要は次のとおりです。

  1. SQLを使用してデータベーススキーマを変更する(例:CREATE TABLEALTER TABLEなど)
  2. prisma db pullを実行してデータベースをイントロスペクトし、アプリケーションモデルをPrismaスキーマに追加する
  3. prisma generateを実行してPrisma Client APIを更新する

Prisma ClientとPrisma Migrateを使用する

Prisma Migrateを使用する場合、アプリケーションモデルをPrismaスキーマで定義し、リレーショナルデータベースではprisma migrateサブコマンドを使用してプレーンなSQLマイグレーションファイルを生成します。これらのファイルは適用する前に編集できます。MongoDBでは、代わりにprisma db pushを使用し、変更をデータベースに直接適用します。

主なワークフローの概要は次のとおりです。

  1. Prismaスキーマ内のアプリケーションモデルを手動で変更する(例:新しいモデルの追加、既存のモデルの削除など)
  2. prisma migrate devを実行してマイグレーションを作成および適用するか、prisma db pushを実行して変更を直接適用します(どちらの場合もPrisma Clientは自動的に生成されます)
© . All rights reserved.