乐趣区

关于架构:聊聊gobanktransfer项目对Clean-Architecture的实践

本文次要赏析一下 go-bank-transfer 对于 Clean Architecture 的实际

我的项目构造

├── adapter
│   ├── api
│   │   ├── action
│   │   ├── logging
│   │   ├── middleware
│   │   └── response
│   ├── logger
│   ├── presenter
│   ├── repository
│   └── validator
├── domain
├── infrastructure
│   ├── database
│   ├── log
│   ├── router
│   └── validation
└── usecase

这里分为 adapter、domain、infrastructure、usecase 四层

domain

account

type AccountID string

func (a AccountID) String() string {return string(a)
}

type (
    AccountRepository interface {Create(context.Context, Account) (Account, error)
        UpdateBalance(context.Context, AccountID, Money) error
        FindAll(context.Context) ([]Account, error)
        FindByID(context.Context, AccountID) (Account, error)
        FindBalance(context.Context, AccountID) (Account, error)
    }

    Account struct {
        id        AccountID
        name      string
        cpf       string
        balance   Money
        createdAt time.Time
    }
)

func NewAccount(ID AccountID, name, CPF string, balance Money, createdAt time.Time) Account {
    return Account{
        id:        ID,
        name:      name,
        cpf:       CPF,
        balance:   balance,
        createdAt: createdAt,
    }
}

func (a *Account) Deposit(amount Money) {a.balance += amount}

func (a *Account) Withdraw(amount Money) error {
    if a.balance < amount {return ErrInsufficientBalance}

    a.balance -= amount

    return nil
}

func (a Account) ID() AccountID {return a.id}

func (a Account) Name() string {return a.name}

func (a Account) CPF() string {return a.cpf}

func (a Account) Balance() Money {return a.balance}

func (a Account) CreatedAt() time.Time {return a.createdAt}

func NewAccountBalance(balance Money) Account {return Account{balance: balance}
}

account 定义了 AccountRepository 接口及 Account 类型,同时还提供了 Withdraw、Deposit 办法

transfer

type TransferID string

func (t TransferID) String() string {return string(t)
}

type (
    TransferRepository interface {Create(context.Context, Transfer) (Transfer, error)
        FindAll(context.Context) ([]Transfer, error)
        WithTransaction(context.Context, func(context.Context) error) error
    }

    Transfer struct {
        id                   TransferID
        accountOriginID      AccountID
        accountDestinationID AccountID
        amount               Money
        createdAt            time.Time
    }
)

func NewTransfer(
    ID TransferID,
    accountOriginID AccountID,
    accountDestinationID AccountID,
    amount Money,
    createdAt time.Time,
) Transfer {
    return Transfer{
        id:                   ID,
        accountOriginID:      accountOriginID,
        accountDestinationID: accountDestinationID,
        amount:               amount,
        createdAt:            createdAt,
    }
}

func (t Transfer) ID() TransferID {return t.id}

func (t Transfer) AccountOriginID() AccountID {return t.accountOriginID}

func (t Transfer) AccountDestinationID() AccountID {return t.accountDestinationID}

func (t Transfer) Amount() Money {return t.amount}

func (t Transfer) CreatedAt() time.Time {return t.createdAt}

transfer 定义了 TransferRepository 接口及 Transfer 类型

usecase

➜  usecase git:(master) tree
.
├── create_account.go
├── create_account_test.go
├── create_transfer.go
├── create_transfer_test.go
├── find_account_balance.go
├── find_account_balance_test.go
├── find_all_account.go
├── find_all_account_test.go
├── find_all_transfer.go
└── find_all_transfer_test.go

这一层定义了 CreateAccountUseCase 与 CreateAccountPresenter、CreateTransferUseCase 与 CreateTransferPresenter、FindAccountBalanceUseCase 与 FindAccountBalancePresenter、FindAllAccountUseCase 与 FindAllAccountPresenter、FindAllTransferUseCase 与 FindAllTransferPresenter 接口

adapter

➜  adapter git:(master) tree
.
├── api
│   ├── action
│   │   ├── create_account.go
│   │   ├── create_account_test.go
│   │   ├── create_transfer.go
│   │   ├── create_transfer_test.go
│   │   ├── find_account_balance.go
│   │   ├── find_account_balance_test.go
│   │   ├── find_all_account.go
│   │   ├── find_all_account_test.go
│   │   ├── find_all_transfer.go
│   │   ├── find_all_transfer_test.go
│   │   ├── health_check.go
│   │   └── health_check_test.go
│   ├── logging
│   │   ├── error.go
│   │   └── info.go
│   ├── middleware
│   │   └── logger.go
│   └── response
│       ├── error.go
│       └── success.go
├── logger
│   └── logger.go
├── presenter
│   ├── create_account.go
│   ├── create_account_test.go
│   ├── create_transfer.go
│   ├── create_transfer_test.go
│   ├── find_account_balance.go
│   ├── find_account_balance_test.go
│   ├── find_all_account.go
│   ├── find_all_account_test.go
│   ├── find_all_transfer.go
│   └── find_all_transfer_test.go
├── repository
│   ├── account_mongodb.go
│   ├── account_postgres.go
│   ├── nosql.go
│   ├── sql.go
│   ├── transfer_mongodb.go
│   └── transfer_postgres.go
└── validator
    └── validator.go

adapter 层实现了 domain 与 usecase 层定义的接口

小结

go-bank-transfer 工程在 domain 层定义了 model 及 repository 接口,usecase 层定义了 usecase 及 presenter 接口,同时调用 domain 层实现业务编排;adapter 则实现了下面两层定义的接口。

doc

  • go-bank-transfer
退出移动版