本文主要赏析一下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


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...