リポジトリは倉庫と覚えておけばいい。

// model/user.js
class User {
  constructor(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }

  isValidEmail() {
    // 簡単なメールアドレスの形式チェック
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
  }

  getFormattedName() {
    return this.name.trim(); // 前後の空白を削除した名前を返す
  }
}

module.exports = User;
// repository/user_repository.js
const User = require('../model/user');
const db = require('../config/database'); // データベース接続情報

class UserRepository {
  async findById(id) {
    const [rows] = await db.execute('SELECT * FROM users WHERE id = ?', [id]);
    if (rows.length === 1) {
      const { id, name, email } = rows[0];
      return new User(id, name, email);
    }
    return null;
  }

  async findAll() {
    const [rows] = await db.execute('SELECT * FROM users');
    return rows.map(row => new User(row.id, row.name, row.email));
  }

  async save(user) {
    await db.execute('INSERT INTO users (name, email) VALUES (?, ?)', [user.name, user.email]);
    // 通常は挿入された ID を取得する処理が必要
  }

  async update(user) {
    await db.execute('UPDATE users SET name = ?, email = ? WHERE id = ?', [user.name, user.email, user.id]);
  }

  async deleteById(id) {
    await db.execute('DELETE FROM users WHERE id = ?', [id]);
  }
}

module.exports = new UserRepository();
// service/user_service.js
const UserRepository = require('../repository/user_repository');
const User = require('../model/user');

class UserService {
  async registerUser(name, email) {
    const newUser = new User(null, name, email);

    if (!newUser.isValidEmail()) {
      throw new Error('無効なメールアドレスです。');
    }

    // 同じメールアドレスのユーザーが既に存在するか確認 (Repository を利用)
    const existingUsers = await UserRepository.findAll();
    if (existingUsers.some(user => user.email === newUser.email)) {
      throw new Error('そのメールアドレスは既に登録されています。');
    }

    // 新しいユーザーを保存 (Repository を利用)
    await UserRepository.save(newUser);
    return newUser; // 登録されたユーザー情報を返す
  }

  async getUserById(id) {
    return await UserRepository.findById(id);
  }

  async getAllUsers() {
    return await UserRepository.findAll();
  }

  async updateUser(id, name, email) {
    const existingUser = await UserRepository.findById(id);
    if (!existingUser) {
      throw new Error('ユーザーが見つかりません。');
    }
    const updatedUser = new User(id, name, email);
    if (!updatedUser.isValidEmail()) {
      throw new Error('無効なメールアドレスです。');
    }
    await UserRepository.update(updatedUser);
    return updatedUser;
  }

  async deleteUser(id) {
    const existingUser = await UserRepository.findById(id);
    if (!existingUser) {
      throw new Error('ユーザーが見つかりません。');
    }
    await UserRepository.deleteById(id);
  }
}

module.exports = new UserService();

かなりざっくり書くと、純粋なデータを持っているのが Repository で加工などをするのが ServiceModel はプロパティ定義やバリデーション、共通の出力を定めるのに使う。

Modelclass ではなく struct だと考えても良い。 まぁ JavaScript には構造体は存在しないんだけどね。

ちなみにコードは Gemini に吐かせたから module.exports の部分とかの書き方が珍妙なことになってる。