Usecase 層是我們應用程式的核心,它體現了所有的業務流程。它的職責是:
一個設計良好的 usecase 應該有明確的輸入(Input)和輸出(Output)資料結構。這使得它的「契約」非常清晰。
internal/usecase/api/user/register/register.go
// 定義 usecase 的輸入與輸出
type Input struct {
email string
password string
}
type Output struct {
AccessToken string
}
接下來,我們定義 usecase
結構體,它包含了執行此業務所需的所有「依賴」。
internal/usecase/api/user/register/register.go
// 定義 usecase 需要的依賴介面
type repository interface {
CheckEmailIsExists(email string) (bool, error)
CreateUser(email, hashedPassword string) (int, error)
}
type password interface {
Hash(password string) (string, error)
}
type token interface {
GenerateAccessToken(userID int) (string, error)
}
// 定義 usecase
type UseCase struct {
repository repository
password password
token token
}
func NewUseCase(repository repository, password password, token token) *UseCase {
return &UseCase{
repository: repository,
password: password,
token: token,
}
}
接下來,我們實現 Execute
方法,這是 usecase 的核心。它負責安排所有的業務邏輯。
func (u *UseCase) Execute(ctx context.Context, input Input) (*Output, error) {
isExists, err := u.repository.CheckEmailIsExists(input.email)
if err != nil {
return nil, err
}
if isExists {
return nil, errors.New("email is already registered")
}
hashedPassword, err := u.password.Hash(input.password)
if err != nil {
return nil, err
}
userID, err := u.repository.CreateUser(input.email, hashedPassword)
if err != nil {
return nil, err
}
accessToken, err := u.token.GenerateAccessToken(userID)
if err != nil {
return nil, err
}
return &Output{
AccessToken: accessToken,
}, nil
}
現在,請仔細觀察我們剛剛寫好的 Execute
方法。你會發現一個美妙的事實:這段程式碼裡,沒有任何與外部技術相關的內容。
gin.Context
。它只認識 context.Context
、以及幾個抽象的介面。這就是六角形架構帶給我們的最大好處:一個與框架無關、高度可測試、可獨立演進的核心業務邏輯層。
在這一章中,我們學會了如何設計和實現一個乾淨的 Usecase 層。
這個層次負責編排我們的核心業務邏輯,並且與外部技術細節完全解耦。
這使得我們的應用程式更加模組化、易於測試和維護。
以上程式碼的完整內容可以到 Github 觀看