目次
- なぜ「分離」が重要なのか
- 現場で遭遇する順番で学ぶ10の分離
- レベル1:最初に出会う基本の分離(Day1から)
- レベル2:チーム開発で必須の分離(1ヶ月目)
- レベル3:サービス成長で必要になる分離(3ヶ月目以降)
- 実践チェックリスト
- まとめ
1. なぜ「分離」が重要なのか
スタートアップで開発していると、こんな場面に遭遇します:
- 「ボタンの色変えたいだけなのに、なんでバックエンドも触らないといけないの?」
- 「本番のパスワードがコードに書いてあって、GitHub に上がってる...」
- 「エンジニア2人追加したいけど、みんな同じファイル触るから競合しまくる」
これ、全部「分離不足」が原因です。
分離とは**「変更の影響範囲を限定すること」**。適切に分離されていると:
- 開発スピードが上がる(担当を分けて並行作業できる)
- バグが減る(変更箇所が明確で影響範囲が予測できる)
- 新メンバーが理解しやすい(どこを見ればいいかわかる)
逆に分離できていないと、ちょっとした修正で全体が壊れ、レビューに時間がかかり、デプロイが怖くなります。
2. 現場で遭遇する順番で学ぶ10の分離
レベル1:最初に出会う基本の分離(Day1から)
1-1. 設定値とコードの分離
優先度:★★★★★(最優先)
何が問題?
// ❌ ダメな例
function connectDB() {
return mysql.connect('localhost', 'root', 'password123');
}
パスワードがコードに直書き。これをGitHubに上げたら情報漏洩です。
どう分離する?
// ✅ 良い例
// .env ファイル(Gitには上げない)
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=password123
// コード
function connectDB() {
return mysql.connect(
process.env.DB_HOST,
process.env.DB_USER,
process.env.DB_PASSWORD
);
}
現場での使い方:
-
.envファイルに環境ごとの設定を書く -
.gitignoreに.envを追加(絶対にコミットしない) -
.env.exampleを作って必要な項目だけ共有
いつ使う? プロジェクト開始の初日から必須。
1-2. 表示とデータの分離
優先度:★★★★★(最優先)
何が問題?
// ❌ ダメな例
function showUserList() {
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 }
];
return '<ul><li>Alice (25)</li><li>Bob (30)</li></ul>';
}
データ取得と表示が混ざってる。デザイン変更でロジックを触ることになります。
どう分離する?
// ✅ 良い例
// データ取得
function getUsers() {
return [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 }
];
}
// 表示(React の例)
function UserList() {
const users = getUsers();
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name} ({user.age})</li>
))}
</ul>
);
}
現場での使い方:
- データ取得関数とコンポーネントを分ける
- デザイナーが触るのは表示部分だけ
いつ使う? 画面を作る最初から。
1-3. フロントエンド・バックエンド分離
優先度:★★★★★(最優先)
何が問題?
昔はPHPやRailsで全部やってました:
<!-- ❌ 古い方法 -->
<h1>ユーザー一覧</h1>
<?php foreach($users as $user): ?>
<div><?= $user['name'] ?></div>
<?php endforeach; ?>
これだと:
- フロントエンド担当が PHP を理解する必要がある
- スマホアプリを作るときに全部作り直し
- 画面の変更でサーバー再起動が必要
どう分離する?
フロントエンド(React/Vue) バックエンド(Rails/Node)
↓ ↓
ブラウザで動く サーバーで動く
↓ ↓
見た目担当 データ担当
←────── API ──────→
具体例:
// フロントエンド(React)
function UserPage() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users') // APIでデータ取得
.then(res => res.json())
.then(data => setUsers(data));
}, []);
return <div>{users.map(u => <p>{u.name}</p>)}</div>;
}
# バックエンド(Rails)
class Api::UsersController < ApplicationController
def index
users = User.all
render json: users # JSON形式で返す
end
end
現場での使い方:
- フロントとバックを別々のリポジトリで管理
- デザイナーはフロントだけ見ればいい
- スマホアプリが必要になっても同じAPIを使える
いつ使う? 今どき標準。最初から分ける。
レベル2:チーム開発で必須の分離(1ヶ月目)
2-1. 責務分離(関心の分離)
優先度:★★★★☆
何が問題?
// ❌ ダメな例:1つの関数が何でもやる
function registerUser(email, password) {
// バリデーション
if (!email.includes('@')) return 'Invalid email';
if (password.length < 8) return 'Password too short';
// パスワード暗号化
const hash = bcrypt.hash(password);
// DB保存
db.query('INSERT INTO users VALUES (?, ?)', [email, hash]);
// メール送信
sendEmail(email, 'Welcome!');
// ログ記録
logger.info('User registered');
}
この関数は「バリデーション」「暗号化」「DB操作」「メール送信」「ログ」を全部やってます。どこかバグがあっても探しにくい。
どう分離する?
// ✅ 良い例:責務ごとに分割
function validateUser(email, password) {
if (!email.includes('@')) throw new Error('Invalid email');
if (password.length < 8) throw new Error('Password too short');
}
function hashPassword(password) {
return bcrypt.hash(password);
}
function saveUser(email, hashedPassword) {
return db.query('INSERT INTO users VALUES (?, ?)', [email, hashedPassword]);
}
function sendWelcomeEmail(email) {
sendEmail(email, 'Welcome!');
}
// メイン処理
function registerUser(email, password) {
validateUser(email, password);
const hash = hashPassword(password);
saveUser(email, hash);
sendWelcomeEmail(email);
logger.info('User registered');
}
現場での使い方:
- 1つの関数は1つの仕事だけ
- 関数名で何をするか分かるように
- テストもしやすくなる
いつ使う? 関数を書くたびに意識。
2-2. レイヤー分離(階層分離)
優先度:★★★★☆
何が問題?
コードがごちゃまぜだと、どこに何があるか分からない。
どう分離する?
プレゼンテーション層(見た目)
↓
ビジネスロジック層(ルール)
↓
データアクセス層(DB操作)
具体例:
// プレゼンテーション層(Controller)
app.post('/users', async (req, res) => {
const user = await userService.create(req.body);
res.json(user);
});
// ビジネスロジック層(Service)
class UserService {
async create(data) {
// ビジネスルール:メールアドレスは小文字化
data.email = data.email.toLowerCase();
// 既存ユーザーチェック
const exists = await userRepository.findByEmail(data.email);
if (exists) throw new Error('Email already exists');
return await userRepository.save(data);
}
}
// データアクセス層(Repository)
class UserRepository {
async findByEmail(email) {
return db.query('SELECT * FROM users WHERE email = ?', [email]);
}
async save(data) {
return db.query('INSERT INTO users SET ?', data);
}
}
現場での使い方:
- Controller:HTTPの入出力だけ
- Service:ビジネスロジック(値段計算、在庫チェックなど)
- Repository:DBの読み書きだけ
いつ使う? ファイルが10個超えたら導入。
2-3. API分離(エンドポイント分離)
優先度:★★★★☆
何が問題?
// ❌ ダメな例:1つのエンドポイントで何でもやる
app.post('/api', (req, res) => {
if (req.body.action === 'getUser') {
// ユーザー取得
} else if (req.body.action === 'updateUser') {
// ユーザー更新
} else if (req.body.action === 'deleteUser') {
// ユーザー削除
}
});
どう分離する?
// ✅ 良い例:REST APIで分離
app.get('/api/users/:id', getUser); // 取得
app.put('/api/users/:id', updateUser); // 更新
app.delete('/api/users/:id', deleteUser); // 削除
現場での使い方:
- URLで何をするか分かるように
- HTTPメソッド(GET, POST, PUT, DELETE)を使い分け
- バージョニング:
/api/v1/users,/api/v2/users
いつ使う? API設計の最初から。
2-4. 認証と業務ロジックの分離
優先度:★★★★☆
何が問題?
// ❌ ダメな例
app.post('/orders', (req, res) => {
// 認証チェック
const token = req.headers.authorization;
if (!token) return res.status(401).send('Unauthorized');
const user = verifyToken(token);
// 注文処理
const order = createOrder(user, req.body);
res.json(order);
});
全てのエンドポイントで同じ認証コードを書くのは無駄。
どう分離する?
// ✅ 良い例:ミドルウェアで分離
function authMiddleware(req, res, next) {
const token = req.headers.authorization;
if (!token) return res.status(401).send('Unauthorized');
req.user = verifyToken(token);
next();
}
// 使う側
app.post('/orders', authMiddleware, (req, res) => {
// 認証済みなので業務ロジックだけ
const order = createOrder(req.user, req.body);
res.json(order);
});
現場での使い方:
- 認証はミドルウェアで一元管理
- 権限チェックも同様に分離
- 業務ロジックに認証コードを書かない
いつ使う? ログイン機能を作る時。
レベル3:サービス成長で必要になる分離(3ヶ月目以降)
3-1. データとロジックの分離
優先度:★★★☆☆
何が問題?
// ❌ ダメな例
const user = {
name: 'Alice',
birthYear: 1995
};
// 年齢計算がバラバラ
const age1 = 2024 - user.birthYear;
const age2 = new Date().getFullYear() - user.birthYear;
どう分離する?
// ✅ 良い例:クラスで分離
class User {
constructor(name, birthYear) {
this.name = name;
this.birthYear = birthYear;
}
getAge() {
return new Date().getFullYear() - this.birthYear;
}
isAdult() {
return this.getAge() >= 18;
}
}
const user = new User('Alice', 1995);
console.log(user.getAge()); // 常に同じロジック
現場での使い方:
- データに関連するロジックはクラスに入れる
- 計算ロジックが統一される
- テストしやすい
いつ使う? 同じ計算を複数箇所でやってる時。
3-2. データベース分離
優先度:★★★☆☆
何が問題?
全てのデータを1つのDBに入れると:
- アクセス集中でパフォーマンス低下
- 障害時に全サービス停止
どう分離する?
ユーザーDB(認証用)
- users テーブル
- sessions テーブル
商品DB(EC機能)
- products テーブル
- orders テーブル
分析DB(レポート用)
- access_logs テーブル
- sales_summary テーブル
現場での使い方:
- 読み取り専用DBを分ける(レプリケーション)
- 機能ごとにDBを分ける
- 重いレポート処理は分析DBで
いつ使う? ユーザー数が1万人超えたら検討。
3-3. マイクロサービス分離
優先度:★★☆☆☆(初期は不要)
何が問題?
1つの巨大アプリ(モノリス)だと:
- デプロイに時間がかかる
- 一部の変更で全体テストが必要
- チームが増えると競合しまくる
どう分離する?
ユーザーサービス(認証担当)
↓ API
注文サービス(注文処理担当)
↓ API
決済サービス(決済担当)
↓ API
通知サービス(メール送信担当)
現場での使い方:
- 各サービスは独立してデプロイ
- チームごとにサービスを担当
- APIで連携
いつ使う? エンジニアが10人超えたら検討。それまでは早すぎる。
3. 実践チェックリスト
今日からやること
-
.envファイルで設定値を分離 -
.gitignoreに.envを追加 - データ取得と表示を別の関数に
1週間以内にやること
- フロントとバックでリポジトリを分ける
- REST API設計(GET/POST/PUT/DELETE)
- 1つの関数は1つの仕事に
1ヶ月以内にやること
- Controller/Service/Repository に分割
- 認証ミドルウェアを作成
- APIエンドポイントを整理
3ヶ月後に検討すること
- データベースのレプリケーション
- 読み取り専用DBの分離
- (チームが大きければ)マイクロサービス化
4. まとめ
分離の本質は「変更の影響範囲を限定すること」
スタートアップでは「最初から完璧」を目指す必要はありません。ただし:
- 設定値の分離は初日から必須(セキュリティ)
- フロント・バック分離は初日から必須(チーム開発)
- 責務・レイヤー分離は1ヶ月以内に導入(コード品質)
- マイクロサービスは焦らない(チームが大きくなってから)
「今どの分離が必要か?」を見極めて、一歩ずつ改善していきましょう。
完璧な設計よりも、動くものを素早くリリースして改善するのがスタートアップのエンジニアリングです。