【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 ← アプリエントリーポイント
- 🧱 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;
};
- 🔌 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;
- 📦 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;
- 💼 サービス層(ビジネスロジック)
// 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;
- 🎮 コントローラー(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 };
- 🛣️ ルーティング
// routes/userRoutes.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
router.get('/', userController.getAllUsers);
module.exports = router;
- 🚀 アプリ起動と依存注入
// 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ライクではない方法で記述可能