【Express】Express × Sequelize × DAOのベストプラクティス - ChuN6868/CodeChrysalis-fullstuck-project GitHub Wiki

Express × Sequelize × DAOのベストプラクティス

🏗️ 全体構成のイメージ

src/
├── controllers/     ← ルーティング処理
│   └── userController.js
├── routes/          ← エンドポイント定義
│   └── userRoutes.js
├── services/        ← ビジネスロジック
│   └── userService.js
├── daos/            ← データアクセス層(Sequelize操作)
│   └── userDAO.js
├── models/          ← Sequelizeモデル定義
│   └── user.js
├── db/              ← Sequelize初期化
│   └── index.js
└── app.js           ← アプリエントリーポイント

  1. 🧱 Sequelizeモデルの定義
// models/user.js
module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    name: DataTypes.STRING,
    email: DataTypes.STRING
  });

  return User;
};
  1. 🔌 Sequelizeの初期化
// db/index.js
const { Sequelize, DataTypes } = require('sequelize');
const userModel = require('../models/user');

const sequelize = new Sequelize(process.env.DB_URI);

const db = {};
db.sequelize = sequelize;
db.User = userModel(sequelize, DataTypes);

module.exports = db;
  1. 📦 DAO層(データ操作のみ)
// daos/userDAO.js
class UserDAO {
  constructor(UserModel) {
    this.User = UserModel;
  }

  async findAll() {
    return await this.User.findAll();
  }

  async findById(id) {
    return await this.User.findByPk(id);
  }

  async create(userData) {
    return await this.User.create(userData);
  }

  async update(id, updateData) {
    const user = await this.User.findByPk(id);
    if (!user) return null;
    return await user.update(updateData);
  }

  async delete(id) {
    return await this.User.destroy({ where: { id } });
  }
}

module.exports = UserDAO;
  1. 💼 サービス層(ビジネスロジック)
// services/userService.js
class UserService {
  constructor(userDAO) {
    this.userDAO = userDAO;
  }

  getAllUsers() {
    return this.userDAO.findAll();
  }

  getUserById(id) {
    return this.userDAO.findById(id);
  }

  createUser(data) {
    return this.userDAO.create(data);
  }

  updateUser(id, data) {
    return this.userDAO.update(id, data);
  }

  deleteUser(id) {
    return this.userDAO.delete(id);
  }
}

module.exports = UserService;
  1. 🎮 コントローラー(HTTP層)
// controllers/userController.js
const getAllUsers = async (req, res) => {
  try {
    const users = await req.services.userService.getAllUsers();
    res.json(users);
  } catch (err) {
    res.status(500).json({ error: 'Server error' });
  }
};

module.exports = { getAllUsers };
  1. 🛣️ ルーティング
// routes/userRoutes.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

router.get('/', userController.getAllUsers);

module.exports = router;
  1. 🚀 アプリ起動と依存注入
// app.js
require('dotenv').config();
const express = require('express');
const app = express();
const db = require('./db');
const UserDAO = require('./daos/userDAO');
const UserService = require('./services/userService');
const userRoutes = require('./routes/userRoutes');

app.use(express.json());

// Sequelize初期化
db.sequelize.sync();

// DAO・Serviceのインスタンス生成
const userDAO = new UserDAO(db.User);
const userService = new UserService(userDAO);

// DI(依存注入)をmiddlewareで行う
app.use((req, res, next) => {
  req.services = { userService };
  next();
});

app.use('/users', userRoutes);

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

🧪 ベストプラクティスまとめ

ポイント | 内容
🔄 DAO層 | Sequelize操作だけに専念。ロジックを持たせない。
🧠 サービス層 | ビジネスルールを集中。例外処理、条件分岐もここ。
🎯 コントローラ層 | リクエストとレスポンス処理だけ。
🔌 DI(依存注入) | Service/DAOをリクエストごとに注入してテスト性を向上。
✅ テスト容易性 | DAOやServiceをモック可能。
⚙️ 拡張性 | 新しいモデルやロジックの追加がしやすい構造。

データベース操作ライブラリ(比較)

ORMとはObject-Relational Mappingのことで、Modelsフォルダのエンティティでimportしてカラムなどの情報と紐づけることで、SQLライクではない方法で記述可能 image