カテゴリ: Go言語 更新日: 2026/01/14

Go言語の構造体を使った設計パターン集(DTO・VOなど)

Go言語の構造体を使った設計パターン集(DTO・VOなど)
Go言語の構造体を使った設計パターン集(DTO・VOなど)

先生と生徒の会話形式で理解しよう

生徒

「Go言語で構造体って使い道がたくさんあるみたいだけど、DTOとかVOって何ですか?」

先生

「DTOやVOは、データを整理して扱いやすくする“設計パターン”の一つです。Goの構造体を使うと、このような設計がとてもシンプルにできますよ。」

生徒

「DTOって難しそう…。初心者でも大丈夫ですか?」

先生

「大丈夫です!わかりやすい例えやコードで、DTOやVOなどの設計パターンを初心者向けにしっかり解説していきますね!」

1. DTOとVOとは何?

1. DTOとVOとは何?
1. DTOとVOとは何?

まず用語の整理から始めましょう。DTO(Data Transfer Object/データ転送オブジェクト)は、データをまとめて運ぶための“箱”です。画面から受け取った入力やAPIへ返す結果など、ひとかたまりで受け渡ししたい情報を構造体に詰めます。一方、VO(Value Object/値オブジェクト)は、中身そのものに意味とルールを与えた“値”です。「これはメール」「これは金額」のように、型の名前で意図をはっきり示せます。

イメージとしては、返事を入れる封筒=DTOと、その封筒に入っている手紙=VO。封筒は運搬役、手紙は内容の主役です。Goではこの違いを、構造体と独自の型定義でシンプルに表現できます。まずは最小の例で雰囲気をつかみましょう。


package main

import "fmt"

// DTO: 受け渡し用に情報をまとめた箱
type UserDTO struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"` // 今はシンプルにstring
}

// VO: 値そのものに名前を与えて意味を明確化
type Email string

func main() {
    // DTOは「まとめて渡す」ために使う
    dto := UserDTO{
        ID:    1,
        Name:  "佐藤",
        Email: "sato@example.com",
    }

    // VOは「これはメールだよ」という意図を型名で示す
    var mail Email = "sato@example.com"

    fmt.Println(dto.Name, dto.Email)
    fmt.Println(mail) // Email型だが中身は文字列
}

この例では、UserDTOが「運ぶための箱」、Emailが「意味のある値」です。プログラミング未経験の方は、DTO=まとめて渡す入れ物/VO=中身のラベル付きの値と覚えるだけで十分。まずは名前を付けて区別できることが大切です。細かな作り込みは後の章で少しずつステップアップしていきます。

2. DTOパターン:構造体でデータをまとめる

2. DTOパターン:構造体でデータをまとめる
2. DTOパターン:構造体でデータをまとめる

DTOは、データをまとめて運ぶために使います。次の例では、「ユーザー情報」をAPIなどに渡すためのDTOを構造体で定義しています。


type UserDTO struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

この構造体は、エンコード用や他の関数にまとめて渡すときに便利です。

3. VOパターン:意味のある型を使って表現

3. VOパターン:意味のある型を使って表現
3. VOパターン:意味のある型を使って表現

VOは、値そのものを型として扱うパターンです。例えば「メールアドレス」を強調したいときに、stringではなくEmailという型を使います。


type Email string

func NewEmail(e string) Email {
    // バリデーションなどの処理も追加できる
    return Email(e)
}

これにより、「これはメールアドレスですよ」という設計がコードから伝わります。

4. DTOにVOを組み合わせる

4. DTOにVOを組み合わせる
4. DTOにVOを組み合わせる

DTOとVOは一緒に使うこともできます。次の例では、Email型のVOをDTOの中に入れています。


type UserDTO struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email Email  `json:"email"`
}

こうすることで、メールアドレスはVOで制約や意味を持ちつつ、DTOとしてデータ箱に収められます。

5. Builderパターン風:構造体の初期化をやさしくする

5. Builderパターン風:構造体の初期化をやさしくする
5. Builderパターン風:構造体の初期化をやさしくする

構造体を初期化するSetter風関数を使うと、複雑なデータ構築がやさしくなります。


type UserBuilder struct {
    user UserDTO
}

func NewUserBuilder() *UserBuilder {
    return &UserBuilder{}
}

func (b *UserBuilder) SetID(id int) *UserBuilder {
    b.user.ID = id
    return b
}

func (b *UserBuilder) SetName(name string) *UserBuilder {
    b.user.Name = name
    return b
}

func (b *UserBuilder) SetEmail(email Email) *UserBuilder {
    b.user.Email = email
    return b
}

func (b *UserBuilder) Build() UserDTO {
    return b.user
}

使い方は次のとおり:


user := NewUserBuilder().
    SetID(1).
    SetName("佐藤").
    SetEmail(NewEmail("sato@example.com")).
    Build()

6. Repositoryパターン:データのやり取りを分離

6. Repositoryパターン:データのやり取りを分離
6. Repositoryパターン:データのやり取りを分離

実際のDBやファイルとやり取りする部分を構造体とメソッドでまとめると、コードがシンプルになります。


type UserRepo struct {
    // DB接続情報など
}
func (r *UserRepo) Save(u UserDTO) error {
    // 保存処理
    return nil
}
func (r *UserRepo) FindByID(id int) (UserDTO, error) {
    // 取得処理
    return UserDTO{}, nil
}

このように構造体とメソッドをまとめることで、保存や取得のロジックを見やすくできます。

7. なぜ設計パターンが役立つの?

7. なぜ設計パターンが役立つの?
7. なぜ設計パターンが役立つの?

構造体とDTO、VO、Builder、Repositoryといった設計パターンを使うと、データの流れをわかりやすく整理できるようになります。特に、コードが読みやすくなり、バグも減りやすくなるメリットがあります。

例えばDTOを使えば、他の関数に必要な情報だけを渡すことができ、関数がシンプルになります。

まとめ

まとめ
まとめ

ここまで、Go言語で構造体を使いながら、データを整理して扱いやすくするための設計パターンについて学んできました。たとえば、データをまとめてやり取りするためのDTO、意味を持った値として扱うVO、構造体の初期化をわかりやすくするBuilder、そして保存や取得を任せるRepositoryなど、現実のアプリケーションで出てきやすい考え方を少しずつ紹介しました。どれも難しそうな名前ですが、実際にコードを見てみると、「なるほど、こういうことか」と自然に理解できるようになっています。とくにDTOは、他の関数や外部とデータをやり取りするときに役立ち、VOは中身の文字列に意味を与えて、読みやすさや間違いにくさにつながります。

ここで大切なのは、「きれいに整理されたデータは、バグを減らし、理解をやさしくする」ということです。初心者がつまずきやすい部分は、コードが複雑になってくると、どこからどんなデータが渡っているのかがわからなくなってしまうところです。DTOなら専用の箱として整理され、VOなら名前だけで何を意味しているのかが自然に読み取れます。まるで封筒に宛名を書いて情報を渡すような感覚で、プログラムの中をデータが移動してくれます。

さらに、構造体はGo言語でとても大切な仕組みなので、設計パターンを意識しながら使っていくと、ゆっくりと理解が深まり、複雑なコードでも迷わずに読み進められるようになります。とくに、入力や出力が増えたときほど、設計が生きてきます。必要な値だけを箱につめて関数に渡したり、VOで意味のある名前をつけたりすることで、コード全体の「読みやすさ」がぐっと良くなります。

また、サンプルとして出てきたように、Builder風の書き方を使うと、構造体の初期化がわかりやすくなるという効果もあります。たとえば値が増えても一行ずつ積み重ねる形なので、何を設定しているのかが一目でわかり、初心者でも読みやすい形になります。

Repositoryについても、実際のアプリケーションで使われることが多い考え方です。保存や取得をまとめておくことで、データベース処理を書く場所と、ビジネスロジックを書く場所を分けられます。これによって、ひとつの関数に処理が詰まりすぎず、後から読み返しても理解しやすい形になります。ただ、最初に使うときは名前が難しく見えるかもしれません。ですが「保存の担当」と「取得の担当」をひとつの構造体にまとめたもの、と考えれば自然と受け入れやすいでしょう。

最初は「設計パターン」という言葉に身構えてしまいがちですが、ひとつずつゆっくり触れてみると、むずかしい数学のような仕組みではなく、ただ「読みやすく」「整理された」書き方の工夫に過ぎません。はじめてプログラムを書く人でも、DTOを用意して値を詰めることは簡単にできますし、VOに名前を付けることも特別な技術ではありません。「意味のある名前」「必要な値だけを渡す」「読みやすい形にしておく」という、誰でも感じられるポイントを大切にすれば十分です。

ここでもう一度、シンプルなサンプルを振り返ってみましょう。


package main

import (
    "encoding/json"
    "fmt"
)

type Email string
type UserDTO struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email Email  `json:"email"`
}

func NewEmail(e string) Email {
    return Email(e)
}

func main() {
    u := UserDTO{
        ID:    1,
        Name:  "佐藤",
        Email: NewEmail("sato@example.com"),
    }

    b, _ := json.Marshal(u)
    fmt.Println(string(b))
}

この短いサンプルの中でも、DTOとVOの役割がはっきり分かれています。UserDTOは情報を一まとめにして渡す箱、Emailは「これはメールアドレスですよ」という意味を持った値です。どんな小さなプログラムでも、この考え方を取り入れて書くことで、後から自分で読み返したときに理解しやすくなります。アプリケーションが大きくなると、こうした小さな積み重ねが大きな違いを生みます。

もう少し大きな視点で考えると、このような設計パターンはチーム開発でも役立ちます。誰かが書いた構造体に意味がついていれば、他の人が読んだとき「ああ、これは入力の箱なんだな」「これはメールアドレスなんだな」とすぐに分かります。言葉で説明する必要がなく、型そのものが説明書の役割を果たしてくれます。つまりプログラマ同士の会話がスムーズになり、誤解も減ります。

初心者のうちは「とにかく動けばいい」という気持ちでコードを書くことが多いのですが、少し余裕が出てきたら、こうした設計の考え方を取り入れてみると、ひとつ上のレベルに進めます。いきなり全部取り入れなくても、まずはDTOとVOだけでも十分効果があります。小さな改善を重ねていくうちに、自然と理解が深まり、今後の学習にも繋がります。

仕組みを知っておくことで、たとえば入力チェックをどこに書くか、データの保存はどこで行うかなどがはっきりして、プログラムが迷子になりません。設計パターンは難しい呪文ではなく、迷子にならないための地図のようなものです。地図があると、途中で道を忘れてもすぐに戻れます。何度も書きながら少しずつ身につけていくのが一番の近道です。

これで、Go言語の構造体を使った設計パターンの基本をひととおり学ぶことができました。現場で活躍するエンジニアも、実はこうした基本的な設計を大切にしながら毎日コードを書いています。初心者でも、ひとつずつ真似していけば必ず身につきます。焦らず、楽しく書き続けてみてください。

先生と生徒の振り返り会話

生徒

「DTOとかVOって、名前を聞いたときはむずかしそうだと思ったけど、こうして見ていくとただの整理術なんですね。」

先生

「そうなんです。箱に入れるのがDTO、意味を持つ値がVO。道具として使い分けるだけで、コードの見通しがよくなります。」

生徒

「設計パターンって聞くと専門的な印象でしたけど、途中のサンプルみたいに、実際のプログラムにそのまま使える形なんですね。」

先生

「はい。特別な仕掛けではなく、読みやすく整理するための工夫です。これからアプリを作るときにも、自然に役立ちますよ。」

生徒

「少しずつ取り入れて、読みやすいコードを目指してみます!」

この記事を読んだ人からの質問

この記事を読んだ人からの質問
この記事を読んだ人からの質問

プログラミング初心者からのよくある疑問/質問を解決します

Go言語のDTOは何に使うのですか?WebアプリやAPI開発で役立ちますか?

Go言語のDTOは、APIやWebアプリでユーザー情報や商品情報などのデータをまとめて運ぶための箱として使われます。関数に大量の値を渡すより、ひとつのDTOでまとめると整理しやすくなり、読みやすいコードになります。
関連記事:
カテゴリの一覧へ
新着記事
New1
Kotlin
Kotlinの構文ルールまとめ!インデント・セミコロンなど初心者が最初に覚えるべきポイント
New2
Kotlin
Kotlinの関数ドキュメンテーションコメント(KDoc)の書き方を徹底解説!初心者でもわかる丁寧なガイド
New3
Kotlin
KotlinでHello Worldを表示するには?最初の1行を実行してみよう
New4
Go言語
Go言語の依存関係管理を始めよう!go modの基本設定と導入手順
人気記事
No.1
Java&Spring記事人気No1
Go言語
Go言語の関数パラメータ!値渡しと参照渡しの違いを理解しよう
No.2
Java&Spring記事人気No2
Kotlin
KotlinのAPI通信でGETリクエストを送る方法!初心者向け徹底ガイド
No.3
Java&Spring記事人気No3
Go言語
Go言語のテストでタイムアウト・並行処理を扱うポイント
No.4
Java&Spring記事人気No4
Swift
SwiftのURLSessionでのネットワークエラー対策!再試行とタイムアウトを初心者向けに解説
No.5
Java&Spring記事人気No5
Kotlin
Gradleファイル(build.gradle.kts)の書き方と役割をやさしく解説!Kotlin初心者向け完全ガイド
No.6
Java&Spring記事人気No6
Kotlin
Android Studioのインストール手順と初期設定を初心者向けに完全解説!
No.7
Java&Spring記事人気No7
Go言語
Go言語のWebアプリにおけるセキュリティベストプラクティス集
No.8
Java&Spring記事人気No8
Swift
Swift JSONデコード失敗の原因と対処|DecodingError徹底解説