▶️

DDDの使い道を見つけた話。ChatGPTと単体ファイルのDDDの相性はとても良い。

2023/11/15に公開

その1: mapにデータを格納するコードを単体ファイルのDDDで作成する

抽象型(リポジトリ) type IUserRepository interface を作っておく。
具象実装(リポジトリ) type InMemoryUserRepository struct をDBに変更する予定で何も考えずに作る。

package main

import (
	"errors"
	"fmt"
	"log"
	"unicode/utf8"
	"net/mail"
	"time"

	"github.com/oklog/ulid/v2"
)

// ---appService型-s-
type SaveUserUseCase struct {
	userServ *UserService
}
//
func NewSaveUserUseCase(userServ *UserService) *SaveUserUseCase {
	return &SaveUserUseCase{userServ: userServ}
}
//
func (uc *SaveUserUseCase) Run(dto SaveUseCaseDto) error {
	user, err := NewUser(dto.LastName, dto.FirstName, dto.Email, dto.Post, dto.Idlimit)
	if err != nil {
		return err
	}
	return uc.userServ.Repository.Save(user)
}
// DTO
type SaveUseCaseDto struct {
	LastName  string
	FirstName string
	Email     string
	Post      string
	Idlimit time.Time
}
//
//---e-

// ---appService型-s-
type FindUserUseCase struct {
	userServ *UserService
}
//
func NewFindUserUseCase(userServ *UserService) *FindUserUseCase {
	return &FindUserUseCase{userServ: userServ}
}
//
func (uc *FindUserUseCase) Run(dto FindUseCaseDto) (*FindUseCaseDto, error) {
	user, err := uc.userServ.Repository.FindByFullName(dto.LastName, dto.FirstName)
	if err != nil {
		return nil, err
	}
	return &FindUseCaseDto{
		ID:          user.ID(),
		LastName:    user.LastName(),
		FirstName:   user.FirstName(),
		Email:       user.Email(),
		Post:        user.Post(),
		Idlimit:     user.Idlimit(),
	}, nil
}
// DTO
type FindUseCaseDto struct {
	ID        string
	LastName  string
	FirstName string
	Email     string
	Post      string
	Idlimit time.Time
}
//
//---e-

// --- app/domain/
// --- 処理型 -s-
type User struct {
	id        string
	lastName  string
	firstName string
	email     string
	post      string
	idlimit time.Time
}
// private
func newUser(id string, lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	// ガード節
	if utf8.RuneCountInString(lastName) < nameLengthMin || utf8.RuneCountInString(lastName) > nameLengthMax {
		return nil, errors.New("名前(姓)の値が不正です。")
	}
	if utf8.RuneCountInString(firstName) < nameLengthMin || utf8.RuneCountInString(firstName) > nameLengthMax {
		return nil, errors.New("名前(名)の値が不正です。")
	}
	if _, err := mail.ParseAddress(email); err != nil {
		return nil, errors.New("メールアドレスの値が不正です。")
	}
	if !isDateFormatValid(idlimit.Format("2006-01-02")) {
		return nil, errors.New("無効な日付形式です")
	}
	
	return &User{id: id, lastName: lastName, firstName: firstName, email: email, post: post, idlimit: idlimit}, nil
}
// 日付の形式が有効かどうかを検証する関数
func isDateFormatValid(dateStr string) bool {
	_, err := time.Parse("2006-01-02", dateStr)
	return err == nil
}
//
const (
	nameLengthMax = 255
	nameLengthMin = 1
)
//
func NewUser(lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	return newUser(NewULID(), lastName, firstName, email, post, idlimit)
}
//
func Reconstruct(id string, lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	return newUser(id, lastName, firstName, email, post, idlimit)
}
//
func (u *User) ID() string {
	return u.id
}
//
func (u *User) LastName() string {
	return u.lastName
}
//
func (u *User) FirstName() string {
	return u.firstName
}
//
func (u *User) Email() string {
	return u.email
}
//
func (u *User) Post() string {
	return u.post
}
//
func (u *User) Idlimit() time.Time {
	return u.idlimit
}
//
func (u *User) PrintUserDetails() {
	fmt.Printf("ID: %s\nLastName: %s\nFirstName: %s\nEmail: %s\nPost: %s\nIdlimit: %s\n\n",
	    u.ID(), u.LastName(), u.FirstName(), u.Email(), u.Post(), u.Idlimit())
}
//---e-

//---pkg/ulid
func NewULID() string { //同じidを生成してしまう対策
	return ulid.Make().String()
}
//
func IsValid(s string) bool {
	_, err := ulid.Parse(s)
	return err == nil
}
//---e-

// --- ドメインサービス用型(メソッドのみ) -s-
type UserService struct {
	Repository IUserRepository //抽象化
}
//
func NewUserRepository(repository IUserRepository) *UserService {
	return &UserService{Repository: repository}
}
//---e-

// --- 抽象型(リポジトリ) -s-
type IUserRepository interface {
	Save(user *User) error
	FindByFullName(lastName string, firstName string) (*User, error)
	FindAllUsers() ([]*User, error)
}
// --- e-

// --- 具象実装(リポジトリ) -s-
type InMemoryUserRepository struct {
	Users map[string]*User
}
//
func NewInMemoryUserRepository() *InMemoryUserRepository {
	return &InMemoryUserRepository{
		Users: make(map[string]*User),
	}
}
//
func (imur *InMemoryUserRepository) Save(user *User) error {
    //fmt.Println("Saving user with ID:", user.ID())
    imur.Users[user.ID()] = user
    return nil
}
//
func (imur *InMemoryUserRepository) FindByFullName(lastName string, firstName string) (*User, error) {
    for _, user := range imur.Users {
        if user.LastName() == lastName && user.FirstName() == firstName {
            return user, nil
        }
    }
    return nil, errors.New("User not found")
}
//
func (imur *InMemoryUserRepository) FindAllUsers() ([]*User, error) {
	users := make([]*User, 0, len(imur.Users))
	for _, user := range imur.Users {
		users = append(users, user)
	}
	return users, nil
}
//---e-


func main() {
    // Initialize repository
    inMemoryUserRepository := NewInMemoryUserRepository()

    // Initialize service with the repository
    userService := NewUserRepository(inMemoryUserRepository)

    // Initialize use cases with the service
    saveUserUseCase := NewSaveUserUseCase(userService)
    findUserUseCase := NewFindUserUseCase(userService)

    // Save user
    saveDto := SaveUseCaseDto{
        LastName:  "Smith",
        FirstName: "John",
        Email:     "john.smith@example.com",
        Post:      "Chief",
        Idlimit:   time.Now().AddDate(50, 0, 0),
    }
    err := saveUserUseCase.Run(saveDto)
    if err != nil {
        log.Fatal("Error saving user:", err)
    }
	
	// Save another user
	saveDto2 := SaveUseCaseDto{
	    LastName:  "Doe",
	    FirstName: "Jane",
	    Email:     "jane.doe@example.com",
	    Post:      "Manager",
	    Idlimit:   time.Now().AddDate(50, 0, 0),
	}
	err = saveUserUseCase.Run(saveDto2)
	if err != nil {
	    log.Fatal("Error saving user:", err)
	}

    // Find user by full name
    findDto := FindUseCaseDto{
        LastName:  "Smith",
        FirstName: "John",
    }
    foundUserDto, err := findUserUseCase.Run(findDto)
    if err != nil {
        log.Fatal("Error finding user:", err)
    }
    // Print found user details
    fmt.Println("===Found User:===")
    fmt.Printf("ID: %s\nLastName: %s\nFirstName: %s\nEmail: %s\nPost: %s\nIdlimit: %s\n",
        foundUserDto.ID, foundUserDto.LastName, foundUserDto.FirstName, foundUserDto.Email, foundUserDto.Post, foundUserDto.Idlimit)

	// Fetch and display all users
	allUsers, err := userService.Repository.FindAllUsers()
	if err != nil {
	    log.Fatal("Error fetching all users:", err)
	}
	fmt.Println("")

	fmt.Println("===All Users:===")
	for _, user := range allUsers {
	    user.PrintUserDetails()
	}
	
}

その2: SQLiteにデータを格納するコードをChatGptにお願いする。

InMemoryUserRepositoryをSQLiteUserRepositoryに載せ替えて。
"github.com/mattn/go-sqlite3"を使って。
package main

import (
	"errors"
	"fmt"
	"log"
	"unicode/utf8"
	"net/mail"
	"time"
	"database/sql"

	"github.com/oklog/ulid/v2"
	_ "github.com/mattn/go-sqlite3" //SQLite用サードパーティードライバ
)

// ---appService型-s-
type SaveUserUseCase struct {
	userServ *UserService
}
//
func NewSaveUserUseCase(userServ *UserService) *SaveUserUseCase {
	return &SaveUserUseCase{userServ: userServ}
}
//
func (uc *SaveUserUseCase) Run(dto SaveUseCaseDto) error {
	user, err := NewUser(dto.LastName, dto.FirstName, dto.Email, dto.Post, dto.Idlimit)
	if err != nil {
		return err
	}
	return uc.userServ.Repository.Save(user)
}
// DTO
type SaveUseCaseDto struct {
	LastName  string
	FirstName string
	Email     string
	Post      string
	Idlimit time.Time
}
//
//---e-

// ---appService型-s-
type FindUserUseCase struct {
	userServ *UserService
}
//
func NewFindUserUseCase(userServ *UserService) *FindUserUseCase {
	return &FindUserUseCase{userServ: userServ}
}
//
func (uc *FindUserUseCase) Run(dto FindUseCaseDto) (*FindUseCaseDto, error) {
	user, err := uc.userServ.Repository.FindByFullName(dto.LastName, dto.FirstName)
	if err != nil {
		return nil, err
	}
	return &FindUseCaseDto{
		ID:          user.ID(),
		LastName:    user.LastName(),
		FirstName:   user.FirstName(),
		Email:       user.Email(),
		Post:        user.Post(),
		Idlimit:     user.Idlimit(),
	}, nil
}
// DTO
type FindUseCaseDto struct {
	ID        string
	LastName  string
	FirstName string
	Email     string
	Post      string
	Idlimit time.Time
}
//
//---e-

// --- app/domain/
// --- 処理型 -s-
type User struct {
	id        string
	lastName  string
	firstName string
	email     string
	post      string
	idlimit time.Time
}
// private
func newUser(id string, lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	// ガード節
	if utf8.RuneCountInString(lastName) < nameLengthMin || utf8.RuneCountInString(lastName) > nameLengthMax {
		return nil, errors.New("名前(姓)の値が不正です。")
	}
	if utf8.RuneCountInString(firstName) < nameLengthMin || utf8.RuneCountInString(firstName) > nameLengthMax {
		return nil, errors.New("名前(名)の値が不正です。")
	}
	if _, err := mail.ParseAddress(email); err != nil {
		return nil, errors.New("メールアドレスの値が不正です。")
	}
	if !isDateFormatValid(idlimit.Format("2006-01-02")) {
		return nil, errors.New("無効な日付形式です")
	}
	
	return &User{id: id, lastName: lastName, firstName: firstName, email: email, post: post, idlimit: idlimit}, nil
}
// 日付の形式が有効かどうかを検証する関数
func isDateFormatValid(dateStr string) bool {
	_, err := time.Parse("2006-01-02", dateStr)
	return err == nil
}
//
const (
	nameLengthMax = 255
	nameLengthMin = 1
)
//
func NewUser(lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	return newUser(NewULID(), lastName, firstName, email, post, idlimit)
}
//
func Reconstruct(id string, lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	return newUser(id, lastName, firstName, email, post, idlimit)
}
//
func (u *User) ID() string {
	return u.id
}
//
func (u *User) LastName() string {
	return u.lastName
}
//
func (u *User) FirstName() string {
	return u.firstName
}
//
func (u *User) Email() string {
	return u.email
}
//
func (u *User) Post() string {
	return u.post
}
//
func (u *User) Idlimit() time.Time {
	return u.idlimit
}
//
func (u *User) PrintUserDetails() {
	fmt.Printf("ID: %s\nLastName: %s\nFirstName: %s\nEmail: %s\nPost: %s\nIdlimit: %s\n\n",
	    u.ID(), u.LastName(), u.FirstName(), u.Email(), u.Post(), u.Idlimit())
}
//---e-

//---pkg/ulid
func NewULID() string { //同じidを生成してしまう対策
	return ulid.Make().String()
}
//
func IsValid(s string) bool {
	_, err := ulid.Parse(s)
	return err == nil
}
//---e-

// --- ドメインサービス用型(メソッドのみ) -s-
type UserService struct {
	Repository IUserRepository //抽象化
}
//
func NewUserRepository(repository IUserRepository) *UserService {
	return &UserService{Repository: repository}
}
//---e-

// --- 抽象型(リポジトリ) -s-
type IUserRepository interface {
	Save(user *User) error
	FindByFullName(lastName string, firstName string) (*User, error)
	FindAllUsers() ([]*User, error)
}
// --- e-

// --- 具象実装(リポジトリ) -s-
type SQLiteUserRepository struct {
	db *sql.DB
}
//
func NewSQLiteUserRepository() (*SQLiteUserRepository, error) {
	db, err := sql.Open("sqlite3", "database.sqlite")
	if err != nil {
		return nil, err
	}

	// Create the users table if it doesn't exist
	_, err = db.Exec(`
		CREATE TABLE IF NOT EXISTS users (
			id TEXT PRIMARY KEY,
			last_name TEXT,
			first_name TEXT,
			email TEXT,
			post TEXT,
			idlimit TEXT
		);
	`)
	if err != nil {
		return nil, err
	}

	return &SQLiteUserRepository{db: db}, nil
}
//
func (r *SQLiteUserRepository) Save(user *User) error {
	_, err := r.db.Exec(
		"INSERT INTO users (id, last_name, first_name, email, post, idlimit) VALUES (?, ?, ?, ?, ?, ?)",
		user.ID(), user.LastName(), user.FirstName(), user.Email(), user.Post(), user.Idlimit().Format("2006-01-02"),
	)
	return err
}
//
func (r *SQLiteUserRepository) FindByFullName(lastName string, firstName string) (*User, error) {
	var user User
	row := r.db.QueryRow("SELECT * FROM users WHERE last_name = ? AND first_name = ?", lastName, firstName)
	var idlimitStr string
	if err := row.Scan(&user.id, &user.lastName, &user.firstName, &user.email, &user.post, &idlimitStr); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil // User not found
		}
		return nil, err // Handle other errors
	}

	idlimit, err := time.Parse("2006-01-02", idlimitStr)
	if err != nil {
		return nil, err
	}

	user.idlimit = idlimit
	return &user, nil
}
//
func (r *SQLiteUserRepository) FindAllUsers() ([]*User, error) {
	rows, err := r.db.Query("SELECT * FROM users")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var users []*User

	for rows.Next() {
		var user User
		var idlimitStr string
		err := rows.Scan(&user.id, &user.lastName, &user.firstName, &user.email, &user.post, &idlimitStr)
		if err != nil {
			return nil, err
		}

		idlimit, err := time.Parse("2006-01-02", idlimitStr)
		if err != nil {
			return nil, err
		}

		user.idlimit = idlimit
		users = append(users, &user)
	}

	return users, nil
}


func main() {
	// Initialize repository with the SQLite database
	sqliteUserRepository, err := NewSQLiteUserRepository()
	if err != nil {
		log.Fatal("Error initializing SQLite repository:", err)
	}
	defer sqliteUserRepository.db.Close()

	// Initialize service with the repository
	userService := NewUserRepository(sqliteUserRepository)

	// Initialize use cases with the service
	saveUserUseCase := NewSaveUserUseCase(userService)
	findUserUseCase := NewFindUserUseCase(userService)

	// Save user
	saveDto := SaveUseCaseDto{
		LastName:  "Smith",
		FirstName: "John",
		Email:     "john.smith@example.com",
		Post:      "Chief",
		Idlimit:   time.Now().AddDate(50, 0, 0),
	}
	err = saveUserUseCase.Run(saveDto)
	if err != nil {
		log.Fatal("Error saving user:", err)
	}

	// Save another user
	saveDto2 := SaveUseCaseDto{
		LastName:  "Doe",
		FirstName: "Jane",
		Email:     "jane.doe@example.com",
		Post:      "Manager",
		Idlimit:   time.Now().AddDate(50, 0, 0),
	}
	err = saveUserUseCase.Run(saveDto2)
	if err != nil {
		log.Fatal("Error saving user:", err)
	}

	// Find user by full name
	findDto := FindUseCaseDto{
		LastName:  "Smith",
		FirstName: "John",
	}
	foundUserDto, err := findUserUseCase.Run(findDto)
	if err != nil {
		log.Fatal("Error finding user:", err)
	}

	// Print found user details
	fmt.Println("===Found User:===")
	fmt.Printf("ID: %s\nLastName: %s\nFirstName: %s\nEmail: %s\nPost: %s\nIdlimit: %s\n",
		foundUserDto.ID, foundUserDto.LastName, foundUserDto.FirstName, foundUserDto.Email, foundUserDto.Post, foundUserDto.Idlimit)

	// Fetch and display all users
	allUsers, err := userService.Repository.FindAllUsers()
	if err != nil {
		log.Fatal("Error fetching all users:", err)
	}

	fmt.Println("\n===All Users:===")
	for _, user := range allUsers {
		user.PrintUserDetails()
	}
}

その3: PostgreSQLにデータを格納するコードをChatGptにお願いする。

SQLiteUserRepositoryをPostgreSQLUserRepositoryに載せ替えて。
"github.com/lib/pq"を使って。
package main

import (
	"errors"
	"fmt"
	"log"
	"unicode/utf8"
	"net/mail"
	"time"
	"database/sql"

	"github.com/oklog/ulid/v2"
	_ "github.com/lib/pq" //PostgreSQL用サードパーティードライバ
)

// ---appService型-s-
type SaveUserUseCase struct {
	userServ *UserService
}
//
func NewSaveUserUseCase(userServ *UserService) *SaveUserUseCase {
	return &SaveUserUseCase{userServ: userServ}
}
//
func (uc *SaveUserUseCase) Run(dto SaveUseCaseDto) error {
	user, err := NewUser(dto.LastName, dto.FirstName, dto.Email, dto.Post, dto.Idlimit)
	if err != nil {
		return err
	}
	return uc.userServ.Repository.Save(user)
}
// DTO
type SaveUseCaseDto struct {
	LastName  string
	FirstName string
	Email     string
	Post      string
	Idlimit time.Time
}
//
//---e-

// ---appService型-s-
type FindUserUseCase struct {
	userServ *UserService
}
//
func NewFindUserUseCase(userServ *UserService) *FindUserUseCase {
	return &FindUserUseCase{userServ: userServ}
}
//
func (uc *FindUserUseCase) Run(dto FindUseCaseDto) (*FindUseCaseDto, error) {
	user, err := uc.userServ.Repository.FindByFullName(dto.LastName, dto.FirstName)
	if err != nil {
		return nil, err
	}
	return &FindUseCaseDto{
		ID:          user.ID(),
		LastName:    user.LastName(),
		FirstName:   user.FirstName(),
		Email:       user.Email(),
		Post:        user.Post(),
		Idlimit:     user.Idlimit(),
	}, nil
}
// DTO
type FindUseCaseDto struct {
	ID        string
	LastName  string
	FirstName string
	Email     string
	Post      string
	Idlimit time.Time
}
//
//---e-

// --- app/domain/
// --- 処理型 -s-
type User struct {
	id        string
	lastName  string
	firstName string
	email     string
	post      string
	idlimit time.Time
}
// private
func newUser(id string, lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	// ガード節
	if utf8.RuneCountInString(lastName) < nameLengthMin || utf8.RuneCountInString(lastName) > nameLengthMax {
		return nil, errors.New("名前(姓)の値が不正です。")
	}
	if utf8.RuneCountInString(firstName) < nameLengthMin || utf8.RuneCountInString(firstName) > nameLengthMax {
		return nil, errors.New("名前(名)の値が不正です。")
	}
	if _, err := mail.ParseAddress(email); err != nil {
		return nil, errors.New("メールアドレスの値が不正です。")
	}
	if !isDateFormatValid(idlimit.Format("2006-01-02")) {
		return nil, errors.New("無効な日付形式です")
	}
	
	return &User{id: id, lastName: lastName, firstName: firstName, email: email, post: post, idlimit: idlimit}, nil
}
// 日付の形式が有効かどうかを検証する関数
func isDateFormatValid(dateStr string) bool {
	_, err := time.Parse("2006-01-02", dateStr)
	return err == nil
}
//
const (
	nameLengthMax = 255
	nameLengthMin = 1
)
//
func NewUser(lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	return newUser(NewULID(), lastName, firstName, email, post, idlimit)
}
//
func Reconstruct(id string, lastName string, firstName string, email string, post string, idlimit time.Time) (*User, error) {
	return newUser(id, lastName, firstName, email, post, idlimit)
}
//
func (u *User) ID() string {
	return u.id
}
//
func (u *User) LastName() string {
	return u.lastName
}
//
func (u *User) FirstName() string {
	return u.firstName
}
//
func (u *User) Email() string {
	return u.email
}
//
func (u *User) Post() string {
	return u.post
}
//
func (u *User) Idlimit() time.Time {
	return u.idlimit
}
//
func (u *User) PrintUserDetails() {
	fmt.Printf("ID: %s\nLastName: %s\nFirstName: %s\nEmail: %s\nPost: %s\nIdlimit: %s\n\n",
	    u.ID(), u.LastName(), u.FirstName(), u.Email(), u.Post(), u.Idlimit())
}
//---e-

//---pkg/ulid
func NewULID() string { //同じidを生成してしまう対策
	return ulid.Make().String()
}
//
func IsValid(s string) bool {
	_, err := ulid.Parse(s)
	return err == nil
}
//---e-

// --- ドメインサービス用型(メソッドのみ) -s-
type UserService struct {
	Repository IUserRepository //抽象化
}
//
func NewUserRepository(repository IUserRepository) *UserService {
	return &UserService{Repository: repository}
}
//---e-

// --- 抽象型(リポジトリ) -s-
type IUserRepository interface {
	Save(user *User) error
	FindByFullName(lastName string, firstName string) (*User, error)
	FindAllUsers() ([]*User, error)
}
// --- e-

// --- 具象実装(リポジトリ) -s-
type PostgreSQLUserRepository struct {
	db *sql.DB
}
//
func NewPostgreSQLUserRepository() (*PostgreSQLUserRepository, error) {
	db, err := sql.Open("postgres", "user=postgres dbname=postgres password=postgresgo sslmode=disable")
	if err != nil {
		return nil, err
	}

	// Create the users table if it doesn't exist
	_, err = db.Exec(`
		CREATE TABLE IF NOT EXISTS users (
			id TEXT PRIMARY KEY,
			last_name TEXT,
			first_name TEXT,
			email TEXT,
			post TEXT,
			idlimit TEXT
		);
	`)
	if err != nil {
		return nil, err
	}

	return &PostgreSQLUserRepository{db: db}, nil
}
//
func (r *PostgreSQLUserRepository) Save(user *User) error {
	/*
	_, err := r.db.Exec(
		"INSERT INTO users (id, last_name, first_name, email, post, idlimit) VALUES (?, ?, ?, ?, ?, ?)",
		user.ID(), user.LastName(), user.FirstName(), user.Email(), user.Post(), user.Idlimit().Format("2006-01-02"),
	)
	*/
	_, err := r.db.Exec(
		"INSERT INTO users (id, last_name, first_name, email, post, idlimit) VALUES ($1, $2, $3, $4, $5, $6)",
		user.ID(), user.LastName(), user.FirstName(), user.Email(), user.Post(), user.Idlimit().Format("2006-01-02"),
	)
	return err
}
//
func (r *PostgreSQLUserRepository) FindByFullName(lastName string, firstName string) (*User, error) {
	var user User
	//row := r.db.QueryRow("SELECT * FROM users WHERE last_name = ? AND first_name = ?", lastName, firstName)
	row := r.db.QueryRow("SELECT * FROM users WHERE last_name = $1 AND first_name = $2", lastName, firstName)
	var idlimitStr string
	if err := row.Scan(&user.id, &user.lastName, &user.firstName, &user.email, &user.post, &idlimitStr); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil // User not found
		}
		return nil, err // Handle other errors
	}

	idlimit, err := time.Parse("2006-01-02", idlimitStr)
	if err != nil {
		return nil, err
	}

	user.idlimit = idlimit
	return &user, nil
}
//
func (r *PostgreSQLUserRepository) FindAllUsers() ([]*User, error) {
	rows, err := r.db.Query("SELECT * FROM users")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var users []*User

	for rows.Next() {
		var user User
		var idlimitStr string
		err := rows.Scan(&user.id, &user.lastName, &user.firstName, &user.email, &user.post, &idlimitStr)
		if err != nil {
			return nil, err
		}

		idlimit, err := time.Parse("2006-01-02", idlimitStr)
		if err != nil {
			return nil, err
		}

		user.idlimit = idlimit
		users = append(users, &user)
	}

	return users, nil
}
//---e-

func main() {
	// Initialize repository with the PostgreSQL database
	postgreSQLUserRepository, err := NewPostgreSQLUserRepository()
	if err != nil {
    	log.Fatal("Error initializing PostgreSQL repository:", err)
	}
	defer postgreSQLUserRepository.db.Close()

	// Initialize service with the repository
	userService := NewUserRepository(postgreSQLUserRepository)

	// Initialize use cases with the service
	saveUserUseCase := NewSaveUserUseCase(userService)
	findUserUseCase := NewFindUserUseCase(userService)

	// Save user
	saveDto := SaveUseCaseDto{
		LastName:  "Smith",
		FirstName: "John",
		Email:     "john.smith@example.com",
		Post:      "Chief",
		Idlimit:   time.Now().AddDate(50, 0, 0),
	}
	err = saveUserUseCase.Run(saveDto)
	if err != nil {
		log.Fatal("Error saving user:", err)
	}

	// Save another user
	saveDto2 := SaveUseCaseDto{
		LastName:  "Doe",
		FirstName: "Jane",
		Email:     "jane.doe@example.com",
		Post:      "Manager",
		Idlimit:   time.Now().AddDate(50, 0, 0),
	}
	err = saveUserUseCase.Run(saveDto2)
	if err != nil {
		log.Fatal("Error saving user:", err)
	}

	// Find user by full name
	findDto := FindUseCaseDto{
		LastName:  "Smith",
		FirstName: "John",
	}
	foundUserDto, err := findUserUseCase.Run(findDto)
	if err != nil {
		log.Fatal("Error finding user:", err)
	}

	// Print found user details
	fmt.Println("===Found User:===")
	fmt.Printf("ID: %s\nLastName: %s\nFirstName: %s\nEmail: %s\nPost: %s\nIdlimit: %s\n",
		foundUserDto.ID, foundUserDto.LastName, foundUserDto.FirstName, foundUserDto.Email, foundUserDto.Post, foundUserDto.Idlimit)

	// Fetch and display all users
	allUsers, err := userService.Repository.FindAllUsers()
	if err != nil {
		log.Fatal("Error fetching all users:", err)
	}

	fmt.Println("\n===All Users:===")
	for _, user := range allUsers {
		user.PrintUserDetails()
	}
}

その4: pkg分け

。。。

結論

『集約の境界』&『依存関係逆転の原則(DIP)』がChatGPT3.5のおバカさんと相性が良い。
mapにデータ格納のDDDを作って、あとはAIに数秒で変更してもらう。

Discussion