moduleを切る基準 — どこで分割するか判断するガイド

1. 概要

  • moduleを切るべき判断基準
  • module化のメリットとデメリット
  • module化の具体的なサイン(いつ切るか)
  • module化すべきでないケース
  • ディレクトリ構成の実例

Terraformのmoduleは「再利用性」だけのための機能ではありません。コードの意図を明確にし、チームで管理しやすくするためのものです。しかし、早すぎるmodule化はかえってコードを複雑にします。この記事では「いつmoduleを切るか」の判断基準を具体例とともに解説します。


2. module化の基本的な判断基準

✅ module化すべきサイン

同じリソースセットを2箇所以上で書いているとき

同一のリソース群(例: ALB + ターゲットグループ + リスナー)を複数の環境やサービスで繰り返す場合は、module化の強いサインです。

# ❌ dev/main.tf と prd/main.tf で同じ構成を重複
resource "aws_lb" "web" { ... }
resource "aws_lb_target_group" "web" { ... }
resource "aws_lb_listener" "http" { ... }

# ✅ modules/alb/main.tf として切り出し
module "alb" {
  source = "../../modules/alb"
  name   = "${var.environment}-web"
  vpc_id = module.vpc.vpc_id
}

論理的にまとまりのある単位があるとき

「ネットワーク層(VPC/サブネット/ルートテーブル)」「コンピュート層(EC2/ASG)」「データ層(RDS/ElastiCache)」のように論理的な境界が明確な場合は分割候補です。

チームをまたいで共有するとき

インフラチームが提供する共通moduleをアプリチームが使う、という組織的な分離にもmoduleは有効です。


3. module化のメリット

terraform {
  required_version = ">= 1.9"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

variable "environment" {
  description = "環境名(dev/stg/prd)"
  type        = string
  default     = "dev"
}

# moduleを呼び出す側(ルートモジュール)
module "network" {
  source      = "./modules/network"
  environment = var.environment
  vpc_cidr    = "10.0.0.0/16"
}

module "compute" {
  source         = "./modules/compute"
  environment    = var.environment
  vpc_id         = module.network.vpc_id
  subnet_ids     = module.network.private_subnet_ids
  instance_type  = var.environment == "prd" ? "t3.medium" : "t3.micro"
}
# modules/network/main.tf
variable "environment" {
  type = string
}
variable "vpc_cidr" {
  type = string
}

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr

  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_subnet" "private" {
  count             = 2
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index)
  availability_zone = data.aws_availability_zones.available.names[count.index]

  tags = {
    Name        = "${var.environment}-private-${count.index + 1}"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

data "aws_availability_zones" "available" {
  state = "available"
}

output "vpc_id" {
  value = aws_vpc.main.id
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id
}

4. module化すべきでないケース

❌ 1箇所でしか使わないとき(まだ)

「将来使いまわすかも」という理由でのmodule化は早すぎます。DRY原則よりも「YAGNI(You Aren’t Gonna Need It)」を優先してください。

# ❌ 1箇所しか使わないのに複雑なmoduleを作る
module "single_ec2" {
  source = "./modules/single-ec2-that-no-one-else-uses"
  ...
}

# ✅ シンプルにそのまま書く
resource "aws_instance" "bastion" {
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = "t3.micro"

  tags = {
    Name        = "${var.environment}-bastion"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

❌ ラッパーが薄すぎるとき

単に1つのリソースをラップするだけのmoduleは、抽象化のコストだけが増えます。

# ❌ S3バケット1つをラップするだけのmodule(不要)
module "s3" {
  source      = "./modules/s3"
  bucket_name = "my-bucket"
}

❌ moduleの境界が不明確なとき

「ALBとRDSをまとめたmodule」のように、論理的なまとまりのない寄せ集めはmodeule化しても管理が難しくなります。


5. 判断フローチャート

同じリソース群を2回以上書いた?
  → YES → module化を検討
  → NO  → まだ書かない

論理的なまとまりがある?(ネットワーク層、コンピュート層など)
  → YES → module化OK
  → NO  → ルートモジュールに直書きのままで可

チームをまたいで共有する?
  → YES → module化 + バージョン管理を検討
  → NO  → ローカルmoduleか直書きで十分

6. ディレクトリ構成の実例

project/
├── environments/
│   ├── dev/
│   │   ├── main.tf          # moduleを呼ぶルートモジュール
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   └── prd/
│       ├── main.tf
│       ├── variables.tf
│       └── terraform.tfvars
└── modules/
    ├── network/             # VPC・サブネット・ルートテーブル
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    ├── compute/             # EC2・ASG・起動テンプレート
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    └── rds/                 # RDS・パラメータグループ・サブネットグループ
        ├── main.tf
        ├── outputs.tf
        └── variables.tf

7. 関連記事


8. まとめ

  • 2箇所以上で使う論理的なまとまりチーム間共有がmodule化の3つのサイン
  • 「将来使うかも」という理由での早すぎるmodule化はコードを複雑にする
  • 1リソースをラップするだけの薄いmoduleは不要
  • まず直書き → 重複が生まれたら切り出す、という順序が現実的

動作確認バージョン: Terraform >= 1.9 公式ドキュメント: https://developer.hashicorp.com/terraform/language/modules/develop