安西先生...!! .........書籍管理アプリを作りたいです......

安西先生...!!
.........書籍管理アプリを作りたいです......
.........
.........
...Goで...
...読みやすいコードで...
...GraphQL...導入しましょう...
...あわよくばDDDも取り入れてみたいですね......


## この記事の目的
話題になっていた ChatGPT を触ってみたかった。
ChatGPT がどこまでコードを書けるのか気になった。
面白そう！

## ChatGPT ってなに？
twitter で見かけたのが最初ですが、こちらが質問したことに回答をしてくれる AI です。
↓ こんな感じです。
```
自分: 元気ですか？
ChatCPT: はい、元気です。

自分: 今あなたが一番欲しいものはなんですか？
ChatCPT: 今一番欲しいものは、健康であり、幸せな毎日を過ごせることです。

自分: それはとても素晴らしいですね
ChatCPT: はい、ありがとうございます。

自分: 何か質問はありますか？
ChatCPT: いいえ、今は特に質問はありません。

自分: 質問してみてください。なんでもいいです。
ChatCPT: 今日は何をしましたか？

```
詳しくは[公式](https://openai.com/blog/chatgpt/) をご覧ください。

> ChatGPTは、対話形式によってフォローアップの質問に答えたり、間違いを認めたり、間違った前提に異議を唱えたり、不適切な要求を拒否したりすることが可能なInstructGPTの兄弟モデルであり、プロンプトの指示に従って詳細な応答を提供するようトレーニングされている。



## 指示一覧

### 書籍管理アプリを作りたいです。要件を整理してください。
```
## 要件

- 書籍を登録する
- 書籍を検索する
- 書籍を削除する
- 書籍を編集する

## ユースケース

- 書籍を登録する
  - 書籍名を入力する
  - 書籍の著者を入力する
  - 書籍の出版社を入力する
  - 書籍の出版日を入力する
  - 書籍の価格を入力する
  - 書籍のURLを入力する
  - 書籍のコメントを入力する
  - 登録ボタンを押す
  - 書籍が登録される

- 書籍を検索する
  - 書籍名を入力する
  - 検索ボタンを押す
  - 書籍が検索される

- 書籍を削除する
  - 書籍を検索する
  - 書籍を選択する
  - 削除ボタンを押す
  - 書籍が削除される

- 書籍を編集する
  - 書籍を検索する
  - 書籍を選択する
  - 編集ボタンを押す
  - 書籍名を入力する
  - 書籍の著者を入力する
  - 書籍の出版社を入力する
  - 書籍の出版日を入力する
  - 書籍の価格を入力する
  - 書籍のURLを入力する
  - 書籍のコメントを入力する
  - 登録ボタンを押す
  - 書籍が編集される

## テーブル定義

- 書籍
  - 書籍ID
  - 書籍名
  - 著者
  - 出版社
  - 出版日
  - 価格
  - URL
  - コメント

```

### DBはMySQLを使いたいです。DBに登録するテーブル設計をしてください。

```
## テーブル設計

- 書籍
  - 書籍ID
  - 書籍名
  - 著者
  - 出版社
  - 出版日
  - 価格
  - URL
  - コメント
```

### 続けて、DBに登録するデータを決めてください。
```

## データ定義

- 書籍
  - 書籍ID
    - 整数
    - 主キー
    - 自動採番
  - 書籍名
    - 文字列
    - 必須
  - 著者
    - 文字列
    - 必須
  - 出版社
    - 文字列
    - 必須
  - 出版日
    - 日付
    - 必須
  - 価格
    - 整数
    - 必須
  - URL
    - 文字列
    - 必須
  - コメント
    - 文字列
    - 必須
```

### GraphQL を導入して設計してみてください。

```graphql
## GraphQL スキーマ定義
type Book {
  id: ID!
  name: String!
  author: String!
  publisher: String!
  publishedAt: String!
  price: Int!
  url: String!
  comment: String!
}

type Query {
  books: [Book]
  book(id: ID!): Book
}

type Mutation {
  createBook(
    name: String!
    author: String!
    publisher: String!
    publishedAt: String!
    price: Int!
    url: String!
    comment: String!
  ): Book
  updateBook(
    id: ID!
    name: String!
    author: String!
    publisher: String!
    publishedAt: String!
    price: Int!
    url: String!
    comment: String!
  ): Book
  deleteBook(id: ID!): Book
}
```


```graphql
## GraphQL クエリ
query {
  books {
    id
    name
    author
    publisher
    publishedAt
    price
    url
    comment
  }
}
```

```graphql
query {
  book(id: 1) {
    id
    name
    author
    publisher
    publishedAt
    price
    url
    comment
  }
}
```

```graphql
mutation {
  createBook(
    name: "GraphQL入門"
    author: "山田太郎"
    publisher: "技術評論社"
    publishedAt: "2020-01-01"
    price: 3000
    url: "https://gihyo.jp/book/2020/978-4-297-11100-9"
    comment: "GraphQL入門書"
  ) {
    id
    name
    author
    publisher
    publishedAt
    price
    url
    comment
  }
}
```

```graphql
mutation {
  updateBook(
    id: 1
    name: "GraphQL入門"
    author: "山田太郎"
    publisher: "技術評論社"
    publishedAt: "2020-01-01"
    price: 3000
    url: "https://gihyo.jp/book/2020/978-4-297-11100-9"
    comment: "GraphQL入門書"
  ) {
    id
    name
    author
    publisher
    publishedAt
    price
    url
    comment
  }
}
```

```graphql
mutation {
  deleteBook(id: 1) {
    id
    name
    author
    publisher
    publishedAt
    price
    url
    comment
  }
}
```

### エンドポイント名は /book にしてください。サーバのエンドポイントを Go で実装してください。


```go
//  エンドポイント実装
package main

import (
	"log"
	"net/http"

	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
)

func main() {
	schema, err := graphql.NewSchema(graphql.SchemaConfig{
		Query:    queryType,
		Mutation: mutationType,
	})
	if err != nil {
		log.Fatalf("failed to create new schema, error: %v", err)
	}

	h := handler.New(&handler.Config{
		Schema:   &schema,
		Pretty:   true,
		GraphiQL: true,
	})

	http.Handle("/book", h)
	log.Println("Now server is running on port 8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
```

### /createbook というエンドポイントを追加してください。そこでは書籍の登録をすることができます。サーバの実装を Go で書いてください。


```go
//  エンドポイント実装
package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
)

func main() {
	schema, err := graphql.NewSchema(graphql.SchemaConfig{
		Query:    queryType,
		Mutation: mutationType,
	})
	if err != nil {
		log.Fatalf("failed to create new schema, error: %v", err)
	}
	h := handler.New(&handler.Config{
		Schema:   &schema,
		Pretty:   true,
		GraphiQL: true,
	})

	http.Handle("/book", h)
	http.HandleFunc("/createbook", createBook)
	log.Println("Now server is running on port 8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func createBook(w http.ResponseWriter, r *http.Request) {
	var params struct {
		Query string `json:"query"`
	}

	if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	result := executeQuery(params.Query, schema)
	json.NewEncoder(w).Encode(result)
}
```

### /updatebook というエンドポイントを追加してください。そこでは書籍の更新をすることができます。サーバの実装を Go で書いてください。


```go

// エンドポイント実装
package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
)

func main() {
	schema, err := graphql.NewSchema(graphql.SchemaConfig{
		Query:    queryType,
		Mutation: mutationType,
	})
	if err != nil {
		log.Fatalf("failed to create new schema, error: %v", err)
	}

	h := handler.New(&handler.Config{
		Schema:   &schema,
		Pretty:   true,
		GraphiQL: true,
	})

	http.Handle("/book", h)
	http.HandleFunc("/createbook", createBook)
	http.HandleFunc("/updatebook", updateBook)
	log.Println("Now server is running on port 8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func updateBook(w http.ResponseWriter, r *http.Request) {
	var params struct {
		Query string `json:"query"`
	}

	if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	result := executeQuery(params.Query, schema)
	json.NewEncoder(w).Encode(result)
}
```

### /deletebook というエンドポイントを追加してください。そこでは書籍の削除をすることができます。サーバの実装を Go で書いてください。


```go
// エンドポイント実装
package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
)

func main() {
	schema, err := graphql.NewSchema(graphql.SchemaConfig{
		Query:    queryType,
		Mutation: mutationType,
	})
	if err != nil {
		log.Fatalf("failed to create new schema, error: %v", err)
	}

	h := handler.New(&handler.Config{
		Schema:   &schema,
		Pretty:   true,
		GraphiQL: true,
	})

	http.Handle("/book", h)
	http.HandleFunc("/createbook", createBook)
	http.HandleFunc("/updatebook", updateBook)
	http.HandleFunc("/deletebook", deleteBook)
	log.Println("Now server is running on port 8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func deleteBook(w http.ResponseWriter, r *http.Request) {
	var params struct {
		Query string `json:"query"`
	}

	if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	result := executeQuery(params.Query, schema)
	json.NewEncoder(w).Encode(result)
}
```

### 今までのコードのディレクトリ構造を考えてください。


```
 ディレクトリ構造
├── main.go
├── schema.go
├── resolver.go
├── model
│   └── book.go
└── database
    └── database.go
```

### DDD を用いて考え直してください。


```
ディレクトリ構造
├── main.go
├── schema.go
├── resolver.go
├── domain
│   └── book.go
├── infrastructure
│   └── database.go
└── application
    └── book.go
```


## 感想
20分ほど触ってみました、、、え、めっちゃ優秀じゃないですか、、、

設計や実装を対話式で組み立ててくれるのはとてもありがたいですね。ただ自分が学び途中の知識（今回でいう DDDなど）は理解しきれていない箇所が多いので使用できる場面は限られてくると感じました。自己学習用で用いたり、別のアプローチがないか探すときには使っても良さそうだと思いました。
今回はセキュリティについて触れれなかったのでもっと触って理解を深めていこうと思います。

明日も期末考査があるのでお先失礼しますね。

Aizu Advent Calendar 5日目 でした！