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

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とのやり取りで使う「受け渡し用の箱」です。入力フォームやデータベースから集めた情報をひとかたまりにし、必要な項目だけを安全に運べます。ひとつの構造体に整理しておくと、引数が増えてゴチャつくのを防ぎ、読みやすさと変更のしやすさが上がります。


type UserDTO struct {
    ID    int    `json:"id"`    // JSONにするときのキー名
    Name  string `json:"name"`  // 大文字で始まるフィールドは外部へ公開される
    Email string `json:"email"` // 使う情報だけを詰めるのがポイント
}

初心者向けの最小サンプルとして、DTOを作ってJSON文字列に変換してみましょう。タグ(json:"...")を付けると、APIとやり取りするときのキー名がそろって便利です。


package main

import (
    "encoding/json"
    "fmt"
)

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

func main() {
    // 1) 入力をまとめる(受け渡し用の箱を用意)
    u := UserDTO{
        ID:    1,
        Name:  "佐藤",
        Email: "sato@example.com",
    }

    // 2) APIに返したいのでJSONへ変換(エンコード)
    b, err := json.Marshal(u)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(b)) // {"id":1,"name":"佐藤","email":"sato@example.com"}

    // 3) 逆に、受け取ったJSONをDTOに読み込む(デコード)こともできます
    var received UserDTO
    _ = json.Unmarshal(b, &received)
    fmt.Println(received.Name) // 佐藤
}

ポイントは次の3つ。①必要な項目だけ入れる(余計な情報は入れない)、②フィールド名は大文字で始めて外部公開(パッケージ外からも使える)、③タグでキー名を明示(APIやファイル保存でズレにくい)。まずはこの型を「受け渡しの形」として決めることが、シンプルな設計への近道です。

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言語を基礎からスッキリ学びたい人や、 文法だけでなく「実用的な使い方」まで押さえたい人には、 定番の入門書がこちらです。

基礎からわかるGo言語をAmazonで見る

※ Amazon広告リンク

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

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

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

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

Go言語のDTOは、APIやWebアプリでユーザー情報や商品情報などのデータをまとめて運ぶための箱として使われます。関数に大量の値を渡すより、ひとつのDTOでまとめると整理しやすくなり、読みやすいコードになります。
関連セミナーのご案内

【超入門】ゼロから始めるGo言語プログラミング:最速で「動くアプリ」を作るマンツーマン指導

「プログラミングの仕組み」が根本からわかる。Go言語でバックエンド開発の第一歩を。

本講座を受講することで、単なる文法の暗記ではなく、「プログラムがコンピュータの中でどう動いているか」という本質的な理解につながります。シンプルながら強力なGo言語(Golang)を通じて、現代のバックエンドエンジニアに求められる基礎体力を最短距離で身につけます。

具体的な開発内容と環境

【つくるもの】
ターミナル(黒い画面)上で動作する「対話型計算プログラム」や、データを整理して表示する「ミニ・ツール」をゼロから作成します。自分の書いたコードが形になる感動を体験してください。

【開発環境】
プロの現場でシェアNo.1のVisual Studio Code (VS Code)を使用します。インストールから日本語化、Go言語用の拡張機能設定まで、現場基準の環境を一緒に構築します。

この60分で得られる3つの理解

1. 環境構築の完全な理解

「なぜ動くのか」という設定の仕組みを理解し、今後の独学で詰まらない土台を作ります。

2. Go言語の基本構造(変数・型)

データの種類やメモリの概念など、他言語にも通じるプログラミングの本質を学びます。

3. 読みやすいコードの書き方

ただ動くだけでなく、誰が見ても分かりやすい「綺麗なコード」を書くための考え方を伝授します。

※本講座は、将来的にバックエンドエンジニアクラウドインフラに興味がある未経験者のためのエントリー講座です。マンツーマン形式により、あなたの理解度に合わせて進行します。

セミナー画像

初めてのGo言語を一緒に学びましょう!

関連記事:
カテゴリの一覧へ
新着記事
New1
Go言語
Go言語のwhile的なforループの使い方!条件式ループの基本を解説
New2
Go言語
Go言語プログラムの実行方法まとめ!VSCode・ターミナルでの実行手順を解説
New3
Swift
Swift意味とは?プログラミング言語・金融・鳥の違いを徹底解説
New4
Swift
Swift 戻り値の扱い方と複数戻り値の返し方|初心者でも分かる関数の基本
人気記事
No.1
Java&Spring記事人気No1
Go言語
Go言語でリダイレクト処理を行う方法(http.Redirect)を初心者向けに解説
No.2
Java&Spring記事人気No2
Swift
Swift開発環境の構築方法を徹底解説!Xcode・Windows・Linux対応
No.3
Java&Spring記事人気No3
Kotlin
Android Studioのインストール手順と初期設定を初心者向けに完全解説!
No.4
Java&Spring記事人気No4
Kotlin
Gradleファイル(build.gradle.kts)の書き方と役割をやさしく解説!Kotlin初心者向け完全ガイド
No.5
Java&Spring記事人気No5
Go言語
Go言語のgo.modファイル完全ガイド!初心者でもわかる仕組みと書き方
No.6
Java&Spring記事人気No6
Swift
Swift Playgroundの使い方を完全解説!初心者に最適な学習環境の始め方
No.7
Java&Spring記事人気No7
Go言語
Go言語で条件式を1行で書くコツ!三項演算子の代替と短縮記法
No.8
Java&Spring記事人気No8
Kotlin
Kotlinのログ出力方法を完全ガイド!LogcatとTimberでトラブルシューティング